【问题标题】:Performance: fastest way of reading in files with Python性能:使用 Python 读取文件的最快方式
【发布时间】:2014-11-26 22:54:13
【问题描述】:

所以我有大约 400 个文件,大小从 10kb 到 56mb,文件类型为 .txt/.doc(x)/.pdf/.xml,我必须全部阅读。我读入的文件基本上是:

#for txt files
with open("TXT\\" + path, 'r') as content_file:
    content = content_file.read().split(' ')

#for doc files using pydoc
contents = '\n'.join([para.text for para in doc.paragraphs]).encode("ascii","ignore").decode("utf-8").split(' ')

#for pdf files using pypdf2
for i in range(0, pdf.getNumPages()):
    content += pdf.getPage(i).extractText() + "\n"
    content = " ".join(content.replace(u"\xa0", " ").strip().split())
contents = content.encode("ascii","ignore").decode("utf-8").split(' ')

#for xml files using lxml
tree = etree.parse(path)
contents = etree.tostring(tree, encoding='utf8', method='text')
contents = contents.decode("utf-8").split(' ')

但我注意到,即使读取 30 个每个大小低于 50kb 的文本文件并对其进行操作也需要 41 秒。但是如果我读取一个 56mb 的文本文件需要 9 秒。所以我猜是文件 I/O 让我慢了下来,而不是我的程序。

关于如何加快这个过程的任何想法?也许将每种文件类型分解为 4 个不同的线程?但是你会怎么做呢,因为他们共享同一个列表,当他们完成后,这个列表将被写入一个文件。

【问题讨论】:

  • 如果你的工作代码表现不佳,你真的应该尝试使用分析器来确定代码慢的地方,而不是猜测。
  • 您有喜欢使用的分析器吗?或者除了从程序开始到各处都坚持打印时间之外,我该怎么做?
  • 即使没有分析器,您也可以非常快速地验证 I/O 操作是否正确:只需测试 read 文件需要多长时间而不做任何事情,与多长时间它需要做你的处理。如果大致相同,那么您是对的,这绝对是 I/O 时间。如果它快很多……好吧,它可能仍然是 I/O 时间(例如,您正在使用的模块可能会进行很多低效的查找或小读取),但它可能不是,所以你需要配置文件。

标签: python performance io


【解决方案1】:

如果您在文件 I/O 上被阻止,正如您所怀疑的那样,可能您无能为力。

但是,如果您有很大的带宽但延迟很长,那么并行到不同的线程可能会有所帮助。尤其是在处理网络文件系统或多盘逻辑驱动器时。所以,试一试也无妨。

但没有理由对每个文件类型都这样做;只需使用一个池来处理所有文件。例如,使用futures 模块:*

import concurrent.futures

with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
    results = executor.map(process_file, list_of_filenames)

ThreadPoolExecutor 比基本线程池稍微聪明一些,因为它可以让你构建可组合的 futures,但在这里你不需要任何这些,所以我只是将它用作基本线程池,因为 Python 没有'没有其中之一。**

constructor 创建 4 个线程,以及所有队列以及管理将任务放在这些线程上并返回结果所需的任何其他内容。

然后,map 方法只是遍历list_of_filenames 中的每个文件名,通过对该文件名调用process_file 创建一个任务,将其提交到池中,然后等待所有任务完成。

也就是说,这和写法一样:

results = [process_file(filename) for filename in list_of_filenames]

...除了它使用四个线程并行处理文件。

如果不够清楚,文档中有一些很好的示例。


* 如果您使用的是 Python 2.x,则需要先安装 backport,然后才能使用它。或者您可以改用multiprocessing.dummy.Pool,如下所述。

** 实际上,在multiprocessing.dummy.Pool 中确实如此,但这并没有很清楚地记录在案。

【讨论】:

  • 不太确定这是在做什么。你介意解释一下吗,或者有时间我会查一下。感谢您的回复。
  • map(...) 应该是executor.map(...)?
  • @moarningsun:是的,当然。感谢您接听!
猜你喜欢
  • 2017-11-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-26
  • 1970-01-01
相关资源
最近更新 更多