web-dev-qa-db-ja.com

Pythonのmultiprocessing.poolでメモリ使用量が増え続けています

プログラムは次のとおりです。

#!/usr/bin/python

import multiprocessing

def dummy_func(r):
    pass

def worker():
    pass

if __name__ == '__main__':
    pool = multiprocessing.Pool(processes=16)
    for index in range(0,100000):
        pool.apply_async(worker, callback=dummy_func)

    # clean up
    pool.close()
    pool.join()

メモリ使用量(VIRTとRESの両方)がclose()/ join()まで成長し続けていることがわかりましたが、これを解決する解決策はありますか? 2.7でmaxtasksperchildを試しましたが、それも助けにはなりませんでした。

Apply_async()を〜6M回呼び出すより複雑なプログラムがあり、〜1.5Mポイントですでに6G + RESを取得しています。他のすべての要因を避けるために、プログラムを上記のバージョンに単純化しました。

編集:

このバージョンは、すべての人の入力に感謝します。

#!/usr/bin/python

import multiprocessing

ready_list = []
def dummy_func(index):
    global ready_list
    ready_list.append(index)

def worker(index):
    return index

if __name__ == '__main__':
    pool = multiprocessing.Pool(processes=16)
    result = {}
    for index in range(0,1000000):
        result[index] = (pool.apply_async(worker, (index,), callback=dummy_func))
        for ready in ready_list:
            result[ready].wait()
            del result[ready]
        ready_list = []

    # clean up
    pool.close()
    pool.join()

メインプロセスはシングルスレッドであると信じているので、ロックをかけませんでした(コールバックは、ドキュメントごとのイベント駆動型のようなものです)。

V1のインデックス範囲をv2と同じ1,000,000に変更し、いくつかのテストを行いました-v2がv1よりも〜10%速い(33秒対37秒)のは奇妙なことです。 v2は間違いなくメモリ使用量の勝者であり、300M(VIRT)と50M(RES)を超えることはありませんでしたが、v1は370M/120Mでしたが、最高は330M/85Mでした。すべての数値は3〜4回のテストであり、参照のみです。

25
C.B.

マルチプロセッシング機能を複数回使用していたため、最近メモリの問題が発生しました。そのため、プロセスを生成し続け、メモリに残します。

私が今使用しているソリューションは次のとおりです。

def myParallelProcess(ahugearray)
 from multiprocessing import Pool
 from contextlib import closing
 with closing( Pool(15) ) as p:
    res = p.imap_unordered(simple_matching, ahugearray, 100)
 return res

I❤with

19
deddu

map_asyncの代わりにapply_asyncを使用して、過度のメモリ使用を回避します。

最初の例では、次の2行を変更します。

for index in range(0,100000):
    pool.apply_async(worker, callback=dummy_func)

pool.map_async(worker, range(100000), callback=dummy_func)

topでメモリ使用量を確認する前に、すぐに終了します。リストを大きなリストに変更して、違いを確認します。ただし、map_asyncは、__len__メソッドがない場合、最初に渡されたイテラブルをリストに変換してその長さを計算します。膨大な数の要素のイテレータがある場合、itertools.isliceを使用して、それらを小さなチャンクで処理できます。

より多くのデータを含む実際のプログラムでメモリの問題が発生し、最終的に原因はapply_asyncであることがわかりました。

追記、メモリ使用量に関して、2つの例に明らかな違いはありません。

6
Fish Monitor

ループ内にプールを作成し、pool.close()を使用してループの最後でプールを閉じます。

5
Ullullu

処理中の非常に大きな3D点群データセットがあります。マルチプロセッシングモジュールを使用して処理を高速化しようとしましたが、メモリ不足エラーが発生し始めました。いくつかの調査とテストの後、サブプロセスが空にすることができるよりもはるかに速く処理されるタスクのキューを埋めていると判断しました。チャンク、またはmap_asyncなどを使用して負荷を調整できたはずですが、周囲のロジックに大きな変更を加えたくありませんでした。

私が思いついた愚かな解決策は、pool._cache断続的に長さがあり、キャッシュが大きすぎる場合は、キューが空になるのを待ちます。

私のメインループには、すでにカウンターとステータスティッカーがありました。

# Update status
count += 1
if count%10000 == 0:
    sys.stdout.write('.')
    if len(pool._cache) > 1e6:
        print "waiting for cache to clear..."
        last.wait() # Where last is assigned the latest ApplyResult

したがって、プールに10k挿入するたびに、100万を超える操作がキューに入れられているかどうかをチェックします(メインプロセスで使用されるメモリは約1G)。キューがいっぱいになったら、最後に挿入されたジョブが終了するのを待ちます。

今、私のプログラムは、メモリを使い果たすことなく何時間も実行できます。メインプロセスは、ワーカーがデータの処理を継続している間、時々一時停止します。

ところで、_cacheメンバーは、マルチプロセッシングモジュールプールの例に記載されています。

#
# Check there are no outstanding tasks
#

assert not pool._cache, 'cache = %r' % pool._cache
4
kitsu.eb

これは 私が投稿した質問 に似ていると思いますが、同じ遅延があるかどうかはわかりません。私の問題は、マルチプロセッシングプールから消費するよりも高速に結果を生成していたため、メモリに蓄積されることでした。それを避けるために、 セマフォ を使用して、プールへの入力を調整し、消費する出力よりも先に進まないようにしました。

1
Don Kirkby