【问题标题】:python multiprocessing read file cost too much timepython多处理读取文件花费太多时间
【发布时间】:2016-03-24 18:00:06
【问题描述】:

我的代码中有一个函数应该读取文件。每个文件大约 8M,但是读取速度太低,为了改进我使用多处理。可悲的是,它似乎被阻塞了。我想知道有什么方法可以帮助解决这个问题并提高阅读速度?

我的代码如下:

import multiprocessing as mp
import json
import os

def gainOneFile(filename):

    file_from = open(filename)
    json_str = file_from.read()
    temp = json.loads(json_str)
    print "load:",filename," len ",len(temp)
    file_from.close()
    return temp

def gainSortedArr(path):
    arr = []
    pool = mp.Pool(4)
    for i in xrange(1,40):
        abs_from_filename = os.path.join(path, "outputDict"+str(i))
        result = pool.apply_async(gainOneFile,(abs_from_filename,)) 
        arr.append(result.get())

    pool.close()
    pool.join()                                               
    arr = sorted(arr,key = lambda dic:len(dic))

    return arr

以及调用函数:

whole_arr = gainSortedArr("sortKeyOut/")  

【问题讨论】:

  • 看来问题是在主进程中收集数据期间的序列化/反序列化。
  • 跳过中间字符串会获得适度的收益...temp = json.load(file_from).
  • 这里没有mp收益。您在子进程中解码 JSON,但在返回父进程时必须再次对其进行序列化和反序列化。它可能比在一个文件中完成所有操作要慢。在这里,您的传输成本高,并行计算量低。
  • 多处理 ValueArray 使用共享内存......那里可能有一些好处。但您可能想尝试其他更快 (?) 的 json 解析器。
  • 但我必须读取文件并对其进行解码。有什么解决方案吗?@tdelaney

标签: python file-read


【解决方案1】:

你有几个问题。首先,你没有并行化。你这样做:

result = pool.apply_async(gainOneFile,(abs_from_filename,)) 
arr.append(result.get())

一遍又一遍地分派一个任务,然后立即调用.get(),它会在您分派任何其他任务之前等待它完成;您实际上永远不会同时运行一个以上的工人。存储所有结果而不调用.get(),然后稍后调用.get()。或者只使用Pool.map 或相关方法,省去手动个人结果管理的一些麻烦,例如(使用imap_unordered 来最小化开销,因为你只是在排序):

# Make generator of paths to load
paths = (os.path.join(path, "outputDict"+str(i)) for i in xrange(1, 40))
# Load them all in parallel, and sort the results by length (lambda is redundant)
arr = sorted(pool.imap_unordered(gainOneFile, paths), key=len)

其次,multiprocessing 必须腌制和取消腌制在主进程和工作程序之间发送的所有参数和返回值,并且它们都通过管道发送,这会导致系统调用开销来启动。由于您的文件系统不太可能从并行读取中获得显着的速度,因此很可能是净损失,而不是收益。

可能可以通过切换到基于线程的池获得一点提升;将import 更改为import multiprocessing.dummy as mp,您将获得一个在线程方面实现的Pool 版本;它们不能围绕 CPython GIL 工作,但由于这段代码几乎可以肯定是 I/O 绑定的,这几乎不重要,它消除了酸洗和解酸以及工作人员通信中涉及的 IPC。

最后,如果您在类似 UNIX 的系统上使用 Python 3.3 或更高版本,您可以让操作系统更积极地将文件拉入系统缓存,从而帮助您解决问题。如果您可以打开文件,则在文件描述符上使用os.posix_fadvise(在文件对象上使用.fileno())和WILLNEEDSEQUENTIAL,当您从通过在您请求文件数据之前积极预取文件数据,在稍后的某个时间点文件。

【讨论】:

  • @IgnacioVazquez-Abrams:在很多情况下,我也会这样,但那是我还不想打开的另外一罐蠕虫。它也不总是最好的方法。 32 位系统在处理大文件时会出现问题,并且在文件被解析为 JSON 的情况下,在 Python 3 上,mmap 对象只能用作bytes 类对象,而不是strjson在 Python 3 上仅从 str 加载;您仍然需要从mmap 读取和解码,因此您从中获得的收益很少。即使在 Py2 上,如果 mmaploads 一起使用,我怀疑它最终会被解码,所以再一次,没有真正的节省。
  • 不要忘记,除非您从多个磁盘读取,否则这将是 IO 绑定的,因此从同一个磁盘读取多个文件,尤其是在旋转时,可能会 伤害性能。
  • @DavidEhrmann:对于有限的并行性,它可能会有所帮助(解析在一个工作人员中读取的数据所花费的时间是其他工作人员读取的时间),如果操作系统很好地安排了读取,它可以 减少延迟(当然,如果它在物理磁盘的任一端交错读取,它也可以很容易地增加它)。条带化或镜像 RAID 阵列,或 NFS(网络延迟是杀手)也可以使这种模式变得有意义。我确实提到并行读取不太可能产生收益,但这并不意味着它不能。
猜你喜欢
  • 1970-01-01
  • 2021-06-16
  • 1970-01-01
  • 2016-05-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多