簡體   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