メモリ帯域幅が制限された計算が共有メモリ環境(OpenMP、Pthreads、またはTBBを介したスレッドなど)で実行される場合、各スレッドがほとんどのメモリに物理的にアクセスするようにメモリを物理メモリに正しく分散させる方法のジレンマがあります「ローカル」メモリバス。インターフェイスは移植性がありませんが、ほとんどのオペレーティングシステムにはスレッドアフィニティを設定する方法があります(たとえばpthread_setaffinity_np()
、多くのPOSIXシステム、sched_setaffinity()
Linux、SetThreadAffinityMask()
Windows)。メモリ階層を決定するためのhwlocなどのライブラリもありますが、残念ながら、ほとんどのオペレーティングシステムにはNUMAメモリポリシーを設定する方法がまだ用意されていません。Linuxは顕著な例外であり、libnumaがありますアプリケーションがページの粒度でメモリポリシーとページ移行を操作できるようにします(2004年以降メインラインにあるため、広く利用可能です)。他のオペレーティングシステムでは、ユーザーが暗黙の「ファーストタッチ」ポリシーに従うことを期待しています。
「ファーストタッチ」ポリシーを使用すると、呼び出し側は、新しく割り当てられたメモリに最初に書き込むときに使用する予定の親和性でスレッドを作成および配布する必要があります。(非常に少数のシステムがあるように構成されているmalloc()
、それはちょうど彼らが実際に障害が発生しているときに別のスレッドによって、おそらく、それらを見つけることを約束、実際にページを検索します。)これは、使用してその割り当てを暗示しcalloc()
たり、すぐに使用して割り当てが後にメモリを初期化するmemset()
ことがフォルトする傾向があるので、有害です割り当てスレッドを実行しているコアのメモリバス上のすべてのメモリ。複数のスレッドからメモリにアクセスすると、最悪のメモリ帯域幅になります。同じことは、new
多くの新しい割り当ての初期化を要求するC ++ 演算子にも当てはまります(例:std::complex
)。この環境に関するいくつかの観察:
- 割り当ては「スレッド集合」にすることができますが、異なるスレッドモデルを使用してクライアントと対話しなければならないライブラリ(望ましくはそれぞれ独自のスレッドプール)には望ましくない割り当てがスレッドモデルに混在するようになりました。
- RAIIは慣用的なC ++の重要な部分であると考えられていますが、NUMA環境でのメモリパフォーマンスには積極的に有害であるようです。配置
new
は、malloc()
から割り当てられたメモリまたはからのルーチンで使用できますlibnuma
が、これにより割り当てプロセスが変更されます(これは必要だと思います)。 - 編集:演算子に関する私の以前の声明
new
は間違っていた、それは複数の引数をサポートすることができます、チェタンの応答を参照してください。ライブラリーまたはSTLコンテナーが指定されたアフィニティーを使用することへの懸念がまだあると思います。複数のフィールドがパックされている場合があり、たとえば、std::vector
正しいコンテキストマネージャをアクティブにして再割り当てすることを保証するのは不便です。 - 各スレッドは独自のプライベートメモリを割り当ててフォールトできますが、隣接する領域へのインデックス作成はより複雑になります。(スパース行列ベクトル積の検討行列とベクトルの行パーティションと、の所有されていない部分インデックス作成、xは、より複雑なデータ構造が必要Xは仮想メモリに連続していない)を
NUMAの割り当て/初期化の解決策は慣用的と見なされますか?他の重要な落とし穴を省きましたか?
(C ++の例がその言語に重点を置くことを意味するわけではありませんが、C ++ 言語は、Cのような言語にはないメモリ管理に関するいくつかの決定をエンコードします。物事が異なります。)