私はPythonのmultiprocessing
モジュールについてもっと学び、プロセス間の通信のためのさまざまな手法を評価しようとしています。 Pipe
配列を転送するためのQueue
、Array
、およびmultiprocessing
(すべてnumpy
から)のパフォーマンスを比較するベンチマークを作成しましたプロセス。完全なベンチマークは ここ で見つけることができます。 Queue
のテストの抜粋は次のとおりです。
def process_with_queue(input_queue, output_queue):
source = input_queue.get()
dest = source**2
output_queue.put(dest)
def test_with_queue(size):
source = np.random.random(size)
input_queue = Queue()
output_queue = Queue()
p = Process(target=process_with_queue, args=(input_queue, output_queue))
start = timer()
p.start()
input_queue.put(source)
result = output_queue.get()
end = timer()
np.testing.assert_allclose(source**2, result)
return end - start
Linuxラップトップでこのテストを実行したところ、配列サイズが1000000の場合に次の結果が得られました。
Using mp.Array: time for 20 iters: total=2.4869s, avg=0.12435s
Using mp.Queue: time for 20 iters: total=0.6583s, avg=0.032915s
Using mp.Pipe: time for 20 iters: total=0.63691s, avg=0.031845s
Array
は共有メモリを使用し、おそらくピクルスを必要としないため、パフォーマンスが非常に悪いのを見て少し驚きましたが、numpy
には制御できないコピーがあるはずだと思います。
ただし、Macbookで同じテスト(配列サイズ1000000に対して)を実行したところ、次の結果が得られました。
Using mp.Array: time for 20 iters: total=1.6917s, avg=0.084587s
Using mp.Queue: time for 20 iters: total=2.3478s, avg=0.11739s
Using mp.Pipe: time for 20 iters: total=8.7709s, avg=0.43855s
もちろん、システムが異なればパフォーマンスも異なるため、実際のタイミングの違いはそれほど驚くべきことではありません。 isが驚くべきことは、相対的なタイミングの違いです。
これを説明できるのは何ですか?これは私にとってかなり驚くべき結果です。 LinuxとWindowsの間、またはOSXとWindowsの間でこのような大きな違いが見られても驚くことではありませんが、これらはOSXとLinuxの間で非常によく似た動作をするだろうと思いました。
この質問 WindowsとOSXのパフォーマンスの違いに対処します。これはより期待されているようです。
Array
からmultiprocessing
を使用すると、 CタイプPythonライブラリ を使用してC呼び出しを行い、配列のメモリを設定します。これは、OSXよりもLinuxの方が比較的時間がかかります。 pypyを使用してOSXでこれを観察することもできます。メモリの設定は、OSXでpython3を使用する(Clangを使用する)よりも、pypy(およびGCCとLLVM)を使用する方がはるかに時間がかかります。
主な違いは、multiprocessing
の実装にあります。これは、OSXではWindowsとは異なる動作をします。最も重要な違いは、multiprocessing
が新しいプロセスを開始する方法です。これを行うには、spawn
、fork
、またはforkserver
の3つの方法があります。 Windowsでのデフォルト(およびサポートされている)の方法はspawn
です。 * nix(OSXを含む)でのデフォルトの方法はfork
です。これは、multiprocessing
ドキュメントの コンテキストと開始メソッド セクションに記載されています。
結果が異なるもう1つの理由は、反復回数が少ないことです。
反復回数を増やし、時間単位ごとに処理される関数呼び出しの数を計算すると、3つのメソッド間で比較的一貫した結果が得られます。
timeit
タイマー関数を削除し、コードをcProfile
プロファイラーでラップしました。
このラッパー関数を追加しました:
_def run_test(iters, size, func):
for _ in range(iters):
func(size)
_
そして、main()
のループを次のように置き換えました。
_for func in [test_with_array, test_with_pipe, test_with_queue]:
print(f"*** Running {func.__name__} ***")
pr = cProfile.Profile()
pr.enable()
run_test(args.iters, args.size, func)
pr.disable()
ps = pstats.Stats(pr, stream=sys.stdout)
ps.strip_dirs().sort_stats('cumtime').print_stats()
_
私が見ているのは、キューはパイプよりも高速であり、パイプは配列よりも高速です。プラットフォーム(OSX/Linux/Windows)に関係なく、キューはパイプより2〜3倍高速です。 OSXとWindowsでは、PipeはArrayよりも約1.2倍と1.5倍高速です。しかし、Linuxでは、PipeはArrayよりも約3.6倍高速です。言い換えると、Linuxでは、ArrayはWindowsやOSXよりも比較的低速です。これはおかしい。
CProfileデータを使用して、OSXとLinuxのパフォーマンス比を比較しました。 _sharedctypes.py
_のArray
とRawArray
の2つの関数呼び出しに時間がかかります。これらの関数は、配列シナリオでのみ呼び出されます(パイプまたはキューでは呼び出されません)。 Linuxでは、これらの呼び出しはほぼ70%の時間かかりますが、OSXでは42%の時間しかかかりません。したがって、これが大きな要因です。
コードに ズームインすると、Array
(84行目)がRawArray
を呼び出し、RawArray
(54行目)は特別なことを何もしないことがわかります。 、_ctypes.memset
_( ドキュメント )の呼び出しを除く。容疑者がいます。それをテストしましょう。
次のコードは、timeitを使用して、1MBのメモリバッファを「A」に設定するパフォーマンスをテストします。
_import timeit
cmds = """\
import ctypes
s=ctypes.create_string_buffer(1024*1024)
ctypes.memset(ctypes.addressof(s), 65, ctypes.sizeof(s))"""
timeit.timeit(cmds, number=100000)
_
これをMacBookProとLinuxサーバーで実行すると、LinuxではOSXよりも実行速度が大幅に低下することが確認されます。 pypy が [〜#〜] gcc [〜#〜] とApples [〜#〜] llvm [〜#〜]を使用してコンパイルされたOSX上にあることを知っている 、これは、 Clang に対して直接コンパイルされたOSX上にあるPythonよりもLinuxの世界に似ています。通常、PythonプログラムはCPythonよりもpypyで高速に実行されますが、上記のコードはpypyで6.4倍遅く実行されます(同じハードウェア上で!)。
私のCツールチェーンとCライブラリの知識は限られているので、深く掘り下げることはできません。したがって、私の結論は次のとおりです。LinuxではCライブラリへのメモリ呼び出しが配列の速度を低下させるため、OSXとWindowsは配列の方が高速です。
次に、OSXとWindowsのデュアルブートMacBookProでこれを実行しました。利点は、基盤となるハードウェアが同じであるということです。 OSだけが違います。反復回数を1000に、サイズを10.000に増やしました。
結果は次のとおりです。
私たちはそれを見ることができます:
spawn
を使用)は、OSX(fork
を使用)よりも多くの呼び出しを必要とします。すぐにはわかりませんが、注意すべき点は、呼び出しあたりの平均時間を見ると、3つのマルチプロセッシングメソッド(配列、キュー、パイプ)間の相対パターンが同じ(下のグラフを参照)。言い換えると、OSXとWindowsのアレイ、キュー、パイプのパフォーマンスの違いは、次の2つの要因によって完全に説明できます。1。Python2つのプラットフォーム間のパフォーマンス。 2.両方のプラットフォームがマルチプロセッシングを処理するさまざまな方法。
言い換えると、呼び出し数の違いは、multiprocessing
ドキュメントの コンテキストと開始メソッド セクションで説明されています。実行時間の違いは、OSXとWindowsのPythonのパフォーマンスの違いで説明されています。以下のグラフに示すように、これら2つのコンポーネントを除外すると、アレイ、キュー、パイプの相対的なパフォーマンスは、OSXとWindowsで(多かれ少なかれ)同等です。