2016-07-18 20 views
1

私はpymongoのパフォーマンスの改善を見たいと思っていますが、私は何も観察していません。スレッドを使用してpymongoのパフォーマンスを改善するにはどうすればよいですか?

私のサンプルデータベースには400,000レコードがあります。基本的には、スレッド化されシングルスレッド化されたパフォーマンスが同等であることがわかりました。

pymongoはクエリ中にGILを解放しませんか?

シングルパフォーマンス・:リアル0m0.618s

Multiproc:リアル0m0.144s

マルチスレッド:リアル0m0.656s

正規コード:今すぐ

choices = ['foo','bar','baz'] 


def regular_read(db, sample_choice): 
    rows = db.test_samples.find({'choice':sample_choice}) 
    return 42 # done to remove calculations from the picture 

def main(): 
    client = MongoClient('localhost', 27017) 
    db = client['test-async'] 
    for sample_choice in choices: 
     regular_read(db, sample_choice) 

if __name__ == '__main__': 
    main() 

$ time python3 mongotest_read.py 

real 0m0.618s 
user 0m0.085s 
sys 0m0.018s 

私はマルチプロセッシングを使用私はいくつかの改善を見ることができます。

from random import randint, choice 

import functools 
from pymongo import MongoClient 
from concurrent import futures 

choices = ['foo','bar','baz'] 
MAX_WORKERS = 4 

def regular_read(sample_choice): 
    client = MongoClient('localhost', 27017,connect=False) 
    db = client['test-async'] 
    rows = db.test_samples.find({'choice':sample_choice}) 
    #return sum(r['data'] for r in rows) 
    return 42 

def main(): 
    f = functools.partial(regular_read) 
    with futures.ProcessPoolExecutor(MAX_WORKERS) as executor: 
     res = executor.map(f, choices) 

    print(list(res)) 
    return len(list(res)) 

if __name__ == '__main__': 
    main() 

$ time python3 mongotest_proc_read.py 
[42, 42, 42] 

real 0m0.144s 
user 0m0.106s 
sys 0m0.041s 

しかし、ProcessPoolExecutorからThreadPoolExecutorに切り替えると、スピードはシングルスレッドモードに戻ります。

...

def main(): 
    client = MongoClient('localhost', 27017,connect=False) 
    f = functools.partial(regular_read, client) 
    with futures.ThreadPoolExecutor(MAX_WORKERS) as executor: 
     res = executor.map(f, choices) 

    print(list(res)) 
    return len(list(res)) 

$ time python3 mongotest_thread_read.py 
[42, 42, 42] 

real 0m0.656s 
user 0m0.111s 
sys 0m0.024s 

...

+0

私はまた、それぞれのスレッドにそれ自身のMongoClientを与えてみました - 結果は同じです。 – MPaz

答えて

9

PyMongoは、ネットワークを介してデータを送受信しながら、GILをドロップし、標準的なPythonのソケットモジュールを使用します。しかし、それはMongoDBやボトルネックとなるネットワークではありません。それはPythonです。

CPUを大量に使用するPythonプロセスは、スレッドを追加することによって拡張されません。実際にはコンテキスト切り替えやその他の非効率性のためにわずかに減速します。 Pythonで複数のCPUを使用するには、サブプロセスを開始します。

"find"はCPUを集中させなければならないのはわかりませんが、Pythonインタプリタは私たちの直感と矛盾するほど遅いです。クエリが高速で、localhost上のMongoDBに遅延がない場合、MongoDBはPythonクライアントよりも優れた性能を発揮します。スレッドをサブプロセスに置き換えたばかりの実験では、Pythonのパフォーマンスがボトルネックであることが確認されています。

最大のスループットを確保するには、C拡張が有効になっていることを確認してください。pymongo.has_c() == True。これで、PyMongoはPythonクライアントライブラリが実現できる速さで動作し、より多くのスループットをマルチプロセッシングに利用できます。

予想される実際のシナリオで時間がかかるクエリや、ネットワークの待ち時間のあるリモートMongoDBが必要な場合は、マルチスレッドを使用するとパフォーマンスが向上することがあります。

関連する問題