繁体   English   中英

为什么多线程不能加速我的程序?

[英]Why does not multithreading speed up my program?

我有一个需要处理的大文本文件。 我首先将所有文本读入一个列表,然后使用 ThreadPoolExecutor 启动多个线程来处理它。 process_text() 中调用的两个函数在这里没有列出:is_channel 和 get_relations()。

我在 Mac 上,我的观察表明它并没有真正加快处理速度(具有 8 个内核的 cpu,只使用了 15% 的 cpu)。 如果 function 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)

应该做的第一件事是找出需要多少时间:

  1. 读取 memory (T1) 中的文件
  2. 进行所有处理 (T2)
  3. 打印结果(T3)

第三点(打印),如果你真的在做,可以减慢速度。 只要您不将其打印到终端并且只是将 output 传送到文件或其他东西,就可以了。

根据时间安排,我们将了解:

  • T1 >> T2 => IO 绑定
  • T2 >> T1 => CPU 绑定
  • T1 和 T2 很接近 => 两者都没有。
    x >> y我的意思是 x 明显大于 y。

基于以上和文件大小,您可以尝试几种方法:

基于线程

即使这可以通过两种方式完成,可以通过再次进行基准测试/查看时间来找出哪种方式更快。

方法 1 (T1 >> T2 甚至当 T1 和 T2 相似时)

  • 运行代码以在线程中读取文件本身,并将行推送到queue而不是列表。
    此线程在完成从文件读取时在末尾插入一个 None 。 这对于告诉工人他们可以停下来很重要
  • 现在运行处理工人并将他们传递给队列
  • 工作人员循环读取队列并处理结果。 与阅读器线程类似,这些工作人员将结果放入队列中。 一旦线程遇到 None ,它就会停止循环并将 None 重新插入队列(以便其他线程可以自行停止)。
  • 打印部分可以再次在一个线程中完成。

以上是单个生产者多个消费者线程的示例。

方法2 (这只是问题中代码片段已经完成的另一种方式)

  • 将整个文件读入列表。
  • 根据 no 将列表划分为索引范围。 线程。
    示例:如果文件总共有 100 行,我们使用 10 个线程
    然后 0-9, 10-19, .... 90-99 是索引范围
    将完整列表和这些索引范围传递给线程以处理每个集合。 由于您没有修改原始列表,因此这是可行的。
    这种方法可以比为每条单独的行运行工作人员提供更好的结果。

基于多处理

(CPU 绑定)

  • 在处理之前将文件拆分为多个文件。
  • 为每个文件运行一个新进程。
  • 每个进程获取它应该读取和处理的文件的路径
  • 这需要在最后合并所有结果/文件的附加步骤
    进程创建部分可以使用multiprocessing模块在 python 内完成
    或从驱动程序脚本为每个文件生成 python 进程,如 shell 脚本

仅通过查看代码,它似乎受CPU 限制 因此,我更喜欢多处理来做到这一点。 我在实践中使用了这两种方法。

  • 多处理:处理存储在磁盘上的巨大文本文件(GB)时(就像你正在做的那样)。
  • 线程(方法一):从多个数据库读取时。 因为这比 CPU 绑定的 IO 更多(我使用了多个生产者和多个消费者线程)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM