2Tb DRAM を搭載した 80 コア (160HT) nehalem アーキテクチャでいくつかのテストを実行した後、軽度の HPC 問題に遭遇しました。
2 つ以上のソケットを持つサーバーは、各スレッドが「間違った」ソケット上のオブジェクトに関する情報を要求し始めるため、多くのストール (遅延) を開始します。つまり、要求は、1 つのソケット上のいくつかのオブジェクトで作業しているスレッドから情報をプルします。それは実際には他のソケットの DRAM にあります。
リモート ソケットが要求を返すのを待っていることはわかっていますが、コアは 100% 使用されているように見えます。
ほとんどのコードは非同期で実行されるため、コードを書き直すのははるかに簡単です。そのため、一方のソケットのスレッドから他方のスレッドへのメッセージを解析できます (ロックされた待機はありません)。さらに、各スレッドをメモリ プールにロックしたいので、ガベージ コレクターで時間 (~30%) を浪費する代わりにオブジェクトを更新できます。
したがって、質問:
Pythonで所定のメモリプールオブジェクトを使用してスレッドをコアに固定する方法は?
もう少しコンテキスト:
ZeroMQ を中間に配置し、各 ZMQworker が管理するメモリ プール間でメッセージをやり取りする技術を作成すると、Python はマルチコアを問題なく実行できます。ZMQ の 8M メッセージ/秒では、オブジェクトの内部更新に、パイプラインを満たすよりも時間がかかります。これはすべてここで説明されています: http://zguide.zeromq.org/page:all#Chapter-Sockets-and-Patterns
したがって、少し単純化しすぎて、80 個の ZMQworkerprocesses と 1 個の ZMQrouter を生成し、コンテキストに大量のオブジェクト (実際には 5 億 8400 万個のオブジェクト) をロードします。この「開始点」から、オブジェクトは相互作用して計算を完了する必要があります。
これがアイデアです:
- 「オブジェクト X」が「オブジェクト Y」と対話する必要があり、Python スレッドのローカル メモリ プールで利用できる場合、対話は直接行う必要があります。
- 「オブジェクト Y」が同じプールで利用できない場合は、ZMQrouter を介してメッセージを送信し、ルーターが後で応答を返すようにします。私のアーキテクチャはノンブロッキングであるため、特定の python スレッドで行われていることは、zmqRouters の応答を待たずに続行されます。同じソケット上にあるが異なるコア上のオブジェクトであっても、2 つのスレッドで同じメモリ オブジェクトを操作するよりも、クリーンなメッセージ交換を行うことを好むため、対話しないことをお勧めします。
これを行うには、次のことを知る必要があります。
- 特定の python プロセス (スレッド) が実行されているソケットを特定する方法。
- その特定のソケットのメモリ プールを Python プロセスに割り当てる方法 (メモリ プールの合計がメモリ プールをあるソケットから別のソケットにプッシュしないようにするための malloc 制限など)
- 私が考えていないこと。
しかし、これを行う方法についてのPythonドキュメントで参照を見つけることができず、Googleで間違ったことを探しているに違いありません。
アップデート:
「なぜ MPI アーキテクチャで ZeroMQ を使用するのですか?」という質問については、次のスレッドをお読みください: Spread vs MPI vs zeromq? 私が取り組んでいるアプリケーションは、MPI の方が適しているアーキテクチャでテストされていますが、分散展開用に設計されているためです。
更新 2:
質問について:
「Python(3) で所定のメモリ プールを使用してスレッドをコアに固定する方法」 答えはpsutilsにあります。
>>> import psutil
>>> psutil.cpu_count()
4
>>> p = psutil.Process()
>>> p.cpu_affinity() # get
[0, 1, 2, 3]
>>> p.cpu_affinity([0]) # set; from now on, this process will run on CPU #0 only
>>> p.cpu_affinity()
[0]
>>>
>>> # reset affinity against all CPUs
>>> all_cpus = list(range(psutil.cpu_count()))
>>> p.cpu_affinity(all_cpus)
>>>
ワーカーをコアにペグすることで、NUMA を効果的に活用できます (CPU タイプを調べて、それが NUMA アーキテクチャであることを確認してください!)
2 番目の要素は、メモリ プールを決定することです。これは、psutilsまたはリソース ライブラリでも実行できます。