使用 python 3.6 将多个文件并行加载到内存中的最佳方法是啥?

Posted

技术标签:

【中文标题】使用 python 3.6 将多个文件并行加载到内存中的最佳方法是啥?【英文标题】:What is the best way to load multiple files into memory in parallel using python 3.6?使用 python 3.6 将多个文件并行加载到内存中的最佳方法是什么? 【发布时间】:2018-05-23 03:46:06 【问题描述】:

我有 6 个大文件,每个文件都包含一个字典对象,我使用 pickle 函数将其保存在硬盘中。按顺序加载所有这些大约需要 600 秒。我想同时开始加载所有这些以加快进程。假设它们都具有相同的大小,我希望在 100 秒内加载它们。我使用 multiprocessing 和 apply_async 分别加载它们中的每一个,但它像顺序一样运行。这是我使用的代码,它不起作用。 该代码适用于其中 3 个文件,但其中 6 个文件相同。我把第三个文件放在另一个硬盘上,以确保 IO 不受限制。

def loadMaps():    
    start = timeit.default_timer()
    procs = []
    pool = Pool(3)
    pool.apply_async(load1(),)
    pool.apply_async(load2(),)
    pool.apply_async(load3(),)
    pool.close()
    pool.join()
    stop = timeit.default_timer()
    print('loadFiles takes in %.1f seconds' % (stop - start))

【问题讨论】:

如果文件存储在同一个卷中,您将受到 I/O 的限制。因此,并行加载文件不会加快进程,更糟糕的是它会减慢速度,因为您可能会增加对硬盘驱动器的随机访问次数。最好按顺序加载文件,抱歉。 您的瓶颈可能是大容量存储。即使使用并行处理,它仍将是瓶颈,并且可能会变得更糟。 我在这台机器上有多个硬盘,我将第三个文件放在不同的硬盘中,这实际上是一个并行文件系统,使用 OpenMP 我可以同时读取具有多个内核的大文件。我想用 python 达到同样的性能。 【参考方案1】:

如果您的代码主要受 IO 限制并且文件位于多个磁盘上,您可能可以使用线程来加速它:

import concurrent.futures
import pickle

def read_one(fname):
    with open(fname, 'rb') as f:
        return pickle.load(f)

def read_parallel(file_names):
    with concurrent.futures.ThreadPoolExecutor() as executor:
        futures = [executor.submit(read_one, f) for f in file_names]
        return [fut.result() for fut in futures]

GIL 不会强制 IO 操作以序列化方式运行,因为 Python 在执行 IO 时会始终释放它。

关于替代品的几点说明:

multiprocessing 不太可能有帮助,因为虽然它保证在多个进程中完成工作(因此没有 GIL),但它还需要在子进程和主进程之间传输内容,这需要额外的时间。

asyncio 根本不会帮助您,因为它本身不支持异步文件系统访问(流行的操作系统也不支持)。虽然可以emulate it with threads,但效果和上面的代码一样,只是多了一些仪式感。

这两个选项都不会将这六个文件的加载速度提高六倍。考虑到至少有一些时间用于创建字典,这些字典将由 GIL 序列化。如果你想真正加快启动速度,更好的方法是不要预先创建整个字典并切换到in-file database,可能使用字典来缓存对其内容的访问。

【讨论】:

我用 3 个文件和 6 个文件运行它。使用多个核心。 3 个文件耗时 171 秒,6 个文件耗时 361 秒。感谢您解释详细信息。请您详细说明关于文件内数据库的最后一个要点? @EhsanSadr 我认为建议是使用存储在文件而不是字典中的数据库。这个答案建议使用 sqlite3 模块,我之前使用过 shelve 模块来执行此操作。这个想法是,不是在脚本开始时将 6 个海量字典全部加载到内存中,而是将所有数据留在磁盘上并根据需要访问它。您会因此受到运行时损失,但会消除启动延迟。 @EhsanSadr 正如 skrrgwasme 所描述的那样。这个建议是否真的有意义取决于您的应用程序 - 如果它最终需要所有这些条目,那么最好预先加载它们并完成它。否则,轻量级数据存储可能正是您所需要的。根据您何时需要字典,您甚至可以创建混合场景,在应用程序执行其他操作时在后台读取文件。 感谢您的澄清,这对我以后的其他应用程序很有用。在这个应用程序中,我需要所有用于快速查找的字典,并且正如您提到的,我愿意为较慢的启动付费。

以上是关于使用 python 3.6 将多个文件并行加载到内存中的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

无法在 Python 3.6 中使用 _pickle 加载 Python 2.x .dat 文件 [重复]

将文件/URI并行加载到greenplum中的外部表

如何提高 Python 3.6 中的 SQLite 插入性能?

用于将数据从AWS S3加载到Redshift的Python脚本

无法加载原生 TensorFlow 运行时。 Windows 10 上的 Python 3.6

Azure Data PlatformETL工具(13)——ADF并行加载多个文件