web-dev-qa-db-ja.com

Python Multiprocessingを使用した高メモリ使用量

私はPython Multiprocessingモジュールを使用してメモリ使用量に関する投稿をいくつか見ました。しかし、質問はここでの問題に答えているようには見えません。私を助けることができます。

問題

マルチプロセッシングを使用してタスクを並行して実行していますが、ワーカープロセスによるメモリ消費が無限に増加することに気付きました。私が気づいたことを再現する小さなスタンドアロンの例があります。

_import multiprocessing as mp
import time

def calculate(num):
    l = [num*num for num in range(num)]
    s = sum(l)
    del l       # delete lists as an  option
    return s

if __== "__main__":
    pool = mp.Pool(processes=2)
    time.sleep(5)
    print "launching calculation"
    num_tasks = 1000
    tasks =  [pool.apply_async(calculate,(i,)) for i in range(num_tasks)]
    for f in tasks:    
        print f.get(5)
    print "calculation finished"
    time.sleep(10)
    print "closing  pool"
    pool.close()
    print "closed pool"
    print "joining pool"
    pool.join()
    print "joined pool"
    time.sleep(5)
_

システム

Windowsを実行していますが、タスクマネージャーを使用してメモリ使用量を監視しています。私はPython 2.7.6。

観察

2つのワーカープロセスによるメモリ消費量を以下にまとめました。

_+---------------+----------------------+----------------------+
|  num_tasks    |  memory with del     | memory without del   |
|               | proc_1   | proc_2    | proc_1   | proc_2    |
+---------------+----------------------+----------------------+
| 1000          | 4884     | 4694      | 4892     | 4952      |
| 5000          | 5588     | 5596      | 6140     | 6268      |
| 10000         | 6528     | 6580      | 6640     | 6644      |
+---------------+----------------------+----------------------+
_

上記の表では、タスクの数を変更し、すべての計算の最後で、join-poolを実行する前に消費されるメモリを観察しようとしました。 'del'オプションと 'without del'オプションは、それぞれcalculate(num)関数内の_del l_行のコメントを解除するかコメントするかどうかです。計算前のメモリ消費量は約4400です。

  1. リストを手動でクリアすると、ワーカープロセスのメモリ使用量が少なくなります。ガベージコレクターがこれを処理してくれると思いました。ガベージコレクションを強制する方法はありますか?
  2. タスクの数が増えると、どちらの場合もメモリ使用量が増え続けるのは不可解です。メモリ使用量を制限する方法はありますか?

この例に基づいたプロセスがあり、長期間実行することを意図しています。一晩実行した後、このワーカープロセスが大量のメモリ(〜4GB)を占有していることがわかります。 joinを実行してメモリを解放することはオプションではなく、join- ingを使用しない方法を見つけようとしています。

これは少し不思議なようです。誰かが似たようなものに遭遇しましたか?この問題を修正するにはどうすればよいですか?

36
Goutham

私は多くの研究を行いましたが、問題自体を解決する解決策を見つけることができませんでした。ただし、サーバー側で長時間実行されるコードでは特に価値があるメモリブローアウトをわずかなコストで防止するための適切な作業があります。

解決策は、基本的に、一定数のタスクの後に個々のワーカープロセスを再起動することでした。 pythonのPoolクラスは、maxtasksperchildを引数として取ります。maxtasksperchild=1000したがって、各子プロセスで実行される1000のタスクを制限します。 maxtasksperchildの数に達すると、プールはその子プロセスを更新します。最大タスクに慎重な数を使用すると、消費される最大メモリと、バックエンドプロセスの再起動に関連する起動コストのバランスを取ることができます。 Poolの構築は次のように行われます:

pool = mp.Pool(processes=2,maxtasksperchild=1000)

私はここに完全なソリューションを置いているので、他の人に役立つでしょう!

import multiprocessing as mp
import time

def calculate(num):
    l = [num*num for num in range(num)]
    s = sum(l)
    del l       # delete lists as an  option
    return s

if __== "__main__":

    # fix is in the following line #
    pool = mp.Pool(processes=2,maxtasksperchild=1000)

    time.sleep(5)
    print "launching calculation"
    num_tasks = 1000
    tasks =  [pool.apply_async(calculate,(i,)) for i in range(num_tasks)]
    for f in tasks:    
        print f.get(5)
    print "calculation finished"
    time.sleep(10)
    print "closing  pool"
    pool.close()
    print "closed pool"
    print "joining pool"
    pool.join()
    print "joined pool"
    time.sleep(5)
61
Goutham