簡體   English   中英

Python多處理隊列比pool.map慢

[英]Python multiprocessing queues slower than pool.map

我最近開始嘗試多處理以加快任務速度。 我創建了一個腳本,該腳本執行模糊字符串匹配,並使用不同的算法來計算分數(我想比較不同的匹配技術)。 您可以在這里找到完整的源代碼: https : //bitbucket.org/bergonzzi/fuzzy-compare/src 作為輸入,它需要將2個文件組合成對(file1的每一行與file2的每一行)。 對於每對,計算模糊匹配分數。

我做了3個版本。 使用我的存儲庫中提供的樣本數據(合並成對后包含697.340個項目)運行,我有以下計時:

  • 簡單的單一流程-0:00:47
  • 使用Pool.map()進行多進程-0:00:13
  • 使用隊列的多進程(生產者/消費者模式)-0:01:04

我試圖理解為什么Pool.map()版本比Queue版本要快得多,而Queue版本實際上比簡單的單進程版本要慢。

我什至嘗試使用Queue的理由是,Pool.map()版本會一直保留結果,直到一切都完成了,並且只在最后寫入文件。 這意味着對於大文件,它最終會占用大量內存。 我正在談論這個版本 (鏈接到它,因為要在此處粘貼很多代碼)。

為了解決這個問題,我將其重構為生產者/消費者模式 (或至少嘗試過)。 在這里,我首先通過組合兩個輸入文件來制作作業,並將它們放入消費者處理的隊列(計算模糊匹配分數)。 完成的作業將放入出隊列。 然后,我只有一個過程從此隊列中抓取已完成的項目並將它們寫入文件。 這樣,從理論上講,我不需要那么多的內存,因為結果將被刷新到磁盤上。 似乎工作正常,但速度慢得多。 我還注意到,在Mac OSX上查看“活動監視器”時,我正在生成的4個進程似乎沒有用完100%的CPU(Pool.map()版本不是這種情況)。

我注意到的另一件事是,我的生產者功能似乎正確填充了隊列,但是使用者進程似乎等待直到隊列被填充,而不是等到第一項到達后才開始工作。 我可能在那兒做錯了...

作為參考,這里是Queue版本的一些相關代碼(盡管最好查看上面鏈接中的repo中的完整代碼)。

這是我的生產者函數:

def combine(list1, list2):
    '''
    Combine every item of list1 with every item of list 2,
    normalize put the pair in the job queue.
    '''
    pname = multiprocessing.current_process().name
    for x in list1:
        for y in list2:
            # slugify is a function to normalize the strings
            term1 = slugify(x.strip(), separator=' ')
            term2 = slugify(y.strip(), separator=' ')
            job_queue.put_nowait([term1, term2])

這是writer函數:

def writer(writer_queue):
    out = open(file_out, 'wb')
    pname = multiprocessing.current_process().name
    out.write(header)
    for match in iter(writer_queue.get, "STOP"):
        print("%s is writing %s") % (pname, str(match))
        line = str(';'.join(match) + '\n')
        out.write(line)
    out.close()

這是執行實際計算的輔助函數(從代碼中摘錄了大部分代碼,因為在這里沒有什么區別,完整的源代碼在倉庫中):

def score_it(job_queue, writer_queue):
    '''Calculate scores for pair of words.'''
    pname = multiprocessing.current_process().name

    for pair in iter(job_queue.get_nowait, "STOP"):
        # do all the calculations and put the result into the writer queue
        writer_queue.put(result)

這是我設置流程的方式:

# Files
to_match = open(args.file_to_match).readlines()
source_list = open(args.file_to_be_matched).readlines()

workers = 4
job_queue = multiprocessing.Manager().Queue()
writer_queue = multiprocessing.Manager().Queue()
processes = []

print('Start matching with "%s", minimum score of %s and %s workers') % (
    args.algorithm, minscore, workers)

# Fill up job queue
print("Filling up job queue with term pairs...")
c = multiprocessing.Process(target=combine, name="Feeder", args=(to_match, source_list))
c.start()
c.join()

print("Job queue size: %s") % job_queue.qsize()

# Start writer process
w = multiprocessing.Process(target=writer, name="Writer", args=(writer_queue,))
w.start()

for w in xrange(workers):
    p = multiprocessing.Process(target=score_it, args=(job_queue, writer_queue))
    p.start()
    processes.append(p)
    job_queue.put("STOP")

for p in processes:
    p.join()

writer_queue.put("STOP")

我在這里已經讀到了很多有關多處理有時會變慢的信息​​,我知道這與創建和管理新流程的開銷有關。 同樣,當要完成的工作不夠“龐大”時,可能看不到多處理的效果。 但是在這種情況下,我認為工作量很大,而且Pool.map()版本似乎證明了這一點,因為它的速度要快得多。

在管理所有這些進程並傳遞隊列對象時,我做錯什么了嗎? 如何對其進行優化,以便在處理結果時將其寫入文件,以最大程度地減少運行文件時所需的內存量?

謝謝!

我認為您的計時問題是您的多線程隊列版本缺少優化。 您發表的評論實質上是在工作線程開始從中獲取作業之前,您的job_queue已填滿。 我相信這樣做的原因是您在#Fill up job queue中具有c.join()。 這樣可以防止主線程繼續執行直到作業隊列已滿。 我將c.join()移到p.join()之后。 您還需要找出一種將停止標志放入隊列末尾的方法。 合並功能可能是放置此內容的好地方。 在數據用完后要合並后,添加x個停止標志的方法。

需要注意的另一件事:您正在開始於p進程的for循環范圍內覆蓋w變量。 作為樣式/可讀性/等問題,我將w更改為其他變量名稱。 如果您不使用它,則下划線可以作為一個很好的一次性變量名。

for w in xrange(workers):

應該成為

for _ in xrange(workers):

長話短說,如果將c.join()移至末尾,則應該獲得更准確的計時。 當前,唯一的多線程處理是字符串的模糊匹配。 擁有生產者/消費者線程的優點之一是,消費者線程不必等到生產者線程完成后,因此最終將使用較少的內存。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM