プログラムは次のとおりです。
#!/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回のテストであり、参照のみです。
マルチプロセッシング機能を複数回使用していたため、最近メモリの問題が発生しました。そのため、プロセスを生成し続け、メモリに残します。
私が今使用しているソリューションは次のとおりです。
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
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つの例に明らかな違いはありません。
ループ内にプールを作成し、pool.close()
を使用してループの最後でプールを閉じます。
処理中の非常に大きな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