それがマルチスレッドであるはずであるという事実にもかかわらず、それが1つのコアしか使用していないことに気づいたので、私は簡単な分析コードのいくつかを簡単にマルチスレッドする方法を探していました。
私はnumpyが複数のコア用に構成されていることを知っています。これは、numpy.dotを使用したテストですべてのコアを使用していることがわかります。これだけでは高速で実行できない理由はありますか?私の例で示した3よりも比率は2に近いですが、より大きな配列でも同様の動作が見られます。
私は同様の乱暴な速度の問題に関するたくさんの投稿を読んでおり、どうやらその方法は思っていたよりも複雑でした。どんな洞察も役立つでしょう。それはより読みやすく、コードが少ないので、平均を使用することを好みますが、ドットベースの手段に切り替えるかもしれません。
In [27]: data = numpy.random.Rand(10,10)
In [28]: a = numpy.ones(10)
In [29]: %timeit numpy.dot(data,a)/10.0
100000 loops, best of 3: 4.8 us per loop
In [30]: %timeit numpy.mean(data,axis=1)
100000 loops, best of 3: 14.8 us per loop
In [31]: numpy.dot(data,a)/10.0 - numpy.mean(data,axis=1)
Out[31]:
array([ 0.00000000e+00, 0.00000000e+00, 0.00000000e+00,
0.00000000e+00, 1.11022302e-16, 0.00000000e+00,
0.00000000e+00, 0.00000000e+00, 0.00000000e+00,
-1.11022302e-16])
それがマルチスレッドであるはずであるという事実にもかかわらず、それが1つのコアしか使用していないことに気づいたので、私は簡単な分析コードのいくつかを簡単にマルチスレッドする方法を探していました。
誰がそれがマルチスレッド化されているはずだと言っていますか?
numpy
は主に、単一コアで可能な限り高速に、必要に応じて可能な限り並列化できるように設計されています。しかし、それを並列化する必要があります。
特に、独立したサブオブジェクトを同時に操作することができ、遅い操作は可能な場合にGILを解放しますが、「可能な場合」では十分ではない場合があります。また、numpy
オブジェクトは、multiprocessing
の使用を容易にするために、プロセス間でできるだけ簡単に共有または渡されるように設計されています。
自動的に並列化されるいくつかの特殊なメソッドがありますが、コアメソッドのほとんどはそうではありません。特に、dot
は可能な場合はBLASの上に実装され、BLASはほとんどのプラットフォームで自動的に並列化されますが、mean
はプレーンCコードで実装されます。
詳細は numpyとscipyを使用した並列プログラミング を参照してください。
では、どのメソッドが並列化され、どのメソッドが並列化されていないのかをどのようにして知るのでしょうか?そして、そうでないもののうち、どのスレッドが適切に手動でスレッド化でき、どれがマルチプロセッシングを必要とするかをどのようにして知るのでしょうか?
それに対する良い答えはありません。知識に基づいた推測(XはおそらくATLASの上に実装されているようで、私のATLASのコピーは暗黙的にスレッド化されているようです)、またはソースを読み取ることができます。
しかし、通常、最善の方法は、それを試してテストすることです。コードが1つのコアの100%と他のコアの0%を使用している場合は、手動スレッドを追加します。現在、1つのコアの100%と他のコアの10%を使用しており、ほとんど実行されていない場合は、マルチスレッドをマルチプロセッシングに変更します。 (幸い、Pythonを使用すると、特にconcurrent.futures
のExecutorクラスまたはmultiprocessing
のPoolクラスを使用する場合にこれが非常に簡単になります。ただし、いくつかのことを考えて、大規模な配列がある場合は、共有と受け渡しの相対的なコストをテストします。
また、kwatfordが指摘するように、一部のメソッドが暗黙的に並列に見えないからといって、numpyの次のバージョン、またはBLASの次のバージョン、または別のプラットフォームで並列にならないというわけではありません。わずかに異なるものがインストールされているマシンでも。したがって、再テストの準備をしてください。そして、my_mean = numpy.mean
のようなことをしてから、どこでもmy_mean
を使用して、1行をmy_mean = pool_threaded_mean
に変更します。
基本的に、BLASライブラリには最適化されたドット積があるため、本質的に並列であるdot
を簡単に呼び出すことができます。彼らは他の操作を並列化するためにnumpyを拡張できることを認めますが、そのルートに進まないことを選びました。ただし、これらは、numpyコードを並列化する方法(基本的にはNコア(例:N = 4)に作業を分割する)、配列をNサブ配列に分割し、各サブ配列のジョブを独自のスレッドに送信する方法に関するいくつかのヒントを提供します。次に、結果を組み合わせます)。
http://wiki.scipy.org/ParallelProgramming を参照してください:
並列プリミティブを使用する
Numpyの大きな強みの1つは、配列演算を非常にきれいに表現できることです。たとえば、行列Aと行列Bの積を計算するには、次のようにします。
>>> C = numpy.dot(A,B)
Numpyはマトリックスドット積を実行する必要があることを知っているため、「BLAS」(基本的な線形代数サブルーチン)の一部として取得した最適化された実装を使用できるため、読み取りと書き込みが簡単でわかりやすいだけでなく、これは通常、キャッシュメモリとアセンブラの実装を利用して、ハードウェアで可能な限り高速に実行されるように注意深く調整されたライブラリになります。しかし、現在、多くのアーキテクチャにはマルチコアマシンを利用するBLASがあります。 numpy/scipyがこれらのいずれかを使用してコンパイルされている場合、dot()は何もせずに並列に計算されます(これが高速の場合)。同様に、反転、特異値分解、行列式など、他の行列演算も同様です。たとえば、オープンソースライブラリATLASでは、並列処理のレベル(スレッド数)をコンパイル時に選択できます。インテル独自のMKLライブラリーは、実行時に並列処理のレベルを選択する可能性を提供します。並列処理のレベルを実行時に選択できるGOTOライブラリもあります。これは商用製品ですが、ソースコードは学術目的で無料で配布されています。
最後に、scipy/numpyは次のような操作を並列化しません
_
>>> A = B + C
_
>>> A = numpy.sin(B)
>>> A = scipy.stats.norm.isf(B)
これらの操作は順次実行され、マルチコアマシンの利点はありません(以下を参照)。原則として、これは多くの作業をせずに変更できます。 OpenMPはC言語の拡張機能であり、これにより、コンパイラーは適切に注釈が付けられたループ(およびその他)の並列化コードを生成できます。誰かが座っていくつかのコアループをnumpy(そしておそらくscipy)に注釈を付けた場合、そしてOpenMPをオンにしてnumpy/scipyをコンパイルした場合、上記の3つすべてが自動的に並列に実行されます。もちろん、実際にはいくつかのランタイム制御が必要になります。たとえば、同じマルチプロセッサマシンで複数のジョブを実行することを計画している場合は、自動並列化をオフにすることができます。