python - 为什么多线程不能加速我的程序?
问题描述
我有一个需要处理的大文本文件。我首先将所有文本读入一个列表,然后使用 ThreadPoolExecutor 启动多个线程来处理它。process_text() 中调用的两个函数在这里没有列出:is_channel 和 get_relations()。
我在 Mac 上,我的观察表明它并没有真正加快处理速度(具有 8 个内核的 cpu,只使用了 15% 的 cpu)。如果函数 is_channel 或 get_relations 存在性能瓶颈,那么多线程将无济于事。这是没有性能提升的原因吗?我应该尝试使用多处理而不是多线程来加速吗?
def process_file(file_name):
all_lines = []
with open(file_name, 'r', encoding='utf8') as f:
for index, line in enumerate(f):
line = line.strip()
all_lines.append(line)
# Classify text
all_results = []
with ThreadPoolExecutor(max_workers=10) as executor:
for index, result in enumerate(executor.map(process_text, all_lines, itertools.repeat(channel))):
all_results.append(result)
for index, entities_relations_list in enumerate(all_results):
# print out results
def process_text(text, channel):
global channel_text
global non_channel_text
is_right_channel = is_channel(text, channel)
entities = ()
relations = None
entities_relations_list = set()
entities_relations_list.add((entities, relations))
if is_right_channel:
channel_text += 1
entities_relations_list = get_relations(text, channel)
return (text, entities_relations_list, is_right_channel)
non_channel_text += 1
return (text, entities_relations_list, is_right_channel)
解决方案
应该做的第一件事是找出需要多少时间:
- 读取内存中的文件(T1)
- 进行所有处理 (T2)
- 打印结果(T3)
第三点(打印),如果你真的在做,可以减慢速度。只要您不将其打印到终端并且只是将输出通过管道传输到文件或其他东西就可以了。
根据时间安排,我们将了解:
- T1 >> T2 => IO 绑定
- T2 >> T1 => CPU 绑定
- T1 和 T2 很接近 => 两者都没有。
我的x >> y
意思是 x 明显大于 y。
基于以上和文件大小,您可以尝试几种方法:
基于线程
即使这可以通过两种方式完成,可以通过再次进行基准测试/查看时间来找出哪种方式更快。
方法 1 (T1 >> T2 甚至当 T1 和 T2 相似时)
- 运行代码以在线程中读取文件本身,并让它将行推送到 a
queue
而不是列表。
此线程在完成从文件读取时在末尾插入一个 None 。这对于告诉工人他们可以停下来很重要 - 现在运行处理工人并将他们传递给队列
- 工作人员循环读取队列并处理结果。与阅读器线程类似,这些工作人员将结果放入队列中。一旦线程遇到 None ,它就会停止循环并将 None 重新插入队列(以便其他线程可以自行停止)。
- 打印部分可以再次在一个线程中完成。
以上是单个生产者和多个消费者线程的示例。
方法2(这只是问题中代码片段已经完成的另一种方式)
- 将整个文件读入列表。
- 根据 no 将列表划分为索引范围。线程。
示例:如果文件总共有 100 行并且我们使用 10 个线程
,则 0-9、10-19、...。90-99 是索引范围
将完整列表和这些索引范围传递给线程以处理每个集合. 由于您没有修改原始列表,因此这是可行的。
这种方法可以比为每条单独的行运行工作人员提供更好的结果。
基于多处理
(CPU 绑定)
- 在处理之前将文件拆分为多个文件。
- 为每个文件运行一个新进程。
- 每个进程获取它应该读取和处理的文件的路径
- 这需要在最后合并所有结果/文件的附加步骤
进程创建部分可以在 python 中使用multiprocessing
模块
或从驱动程序脚本中完成,为每个文件生成一个 python 进程,如 shell 脚本
仅通过查看代码,它似乎受CPU 限制。因此,我更喜欢多处理来做到这一点。我在实践中使用了这两种方法。
- 多处理:处理存储在磁盘上的巨大文本文件(GB)时(就像你正在做的那样)。
- 线程(方法一):从多个数据库读取时。因为这比 CPU 更受 IO 限制(我使用了多个生产者和多个消费者线程)。
推荐阅读
- exe - 如何从旧的 InstallShield 安装程序的 exe 文件中获取 Setup.rul 文件?
- azure - 有什么方法可以连接到 Azure SQL DB Premium Read Scale-Out 中的特定只读副本?
- c# - 如何访问属性内的属性?
- java - Java War 文件不会在 Eclipse 中使用 WildFly 更新
- android - 无法仅针对特定文件夹开始测试
- python - 按钮文本不更新
- javascript - 承诺异步调用
- html - 在 powershell 中访问 groovy 变量
- javascript - 检查项目是否存在于 Firestore 中,否则保存(数组)
- android - 如何在 android RecyclerView 中检测 OverScroll?