コンピュータサイエンスでは、プロセッサアフィニティ( CPUピンニングまたはキャッシュアフィニティとも呼ばれる)は、プロセスまたはスレッドを中央処理装置(CPU)または複数のCPUにバインドおよびアンバインドすることを可能にし、プロセスまたはスレッドが任意のCPUではなく、指定されたCPUでのみ実行されるようにします。 [ 1 ]これは、対称型マルチプロセッシングオペレーティングシステムのネイティブな中央キュースケジューリングアルゴリズムの変更と見なすことができます。キュー内の各項目には、その関連プロセッサを示すタグがあります。リソース割り当て時には、各タスクは他のタスクよりも優先して関連プロセッサに割り当てられます。
スケジューリングアルゴリズムの実装は、プロセッサ親和性への準拠の点で異なります。特定の状況下では、一部の実装では、効率が向上する場合、タスクを別のプロセッサに変更することを許可する場合があります。たとえば、2 つのプロセッサ負荷の高いタスク (A と B) が 1 つのプロセッサに親和性があり、もう 1 つのプロセッサが未使用の場合、多くのスケジューラは、プロセッサの使用率を最大化するために、タスク B を 2 番目のプロセッサに移動します。すると、タスク B は 2 番目のプロセッサとの親和性を獲得し、タスク A は元のプロセッサとの親和性を維持します。[ 2 ]
ほとんどのオペレーティングシステムでは、プロセスまたはスレッドが実行を許可されている(または優先されている)プロセッサのセットは、システムのコアに対応するビットマスクであるアフィニティマスクとして表現されます。 [ 3 ]
プロセッサアフィニティを使用する理由はいくつかあります。
スレッドの実行は、割り込み中に他のプログラムやスレッドのためのスペースを確保するために、OS スケジューラによって中断されることがあります。スレッドが後で以前実行されていたプロセッサにディスパッチされた場合、CPU キャッシュに再利用できるデータが残っている可能性があり、キャッシュ ミスを減らすことができます。[ 4 ]プロセッサ アフィニティを設定すると、スレッドが常に同じプロセッサで実行されることが保証されますが、同時にプロセッサが再び使用可能になるまで待機させられます。この機能は、割り込みが少ない CPU 負荷の高いプロセスに特に役立ちます。通常のプログラムに同じことを行うと、割り込みがより頻繁に発生し、待機時間が長くなる傾向があるため、逆に遅くなる可能性があります。[ 5 ]プロセッサ アフィニティの実用的な例としては、グラフィック レンダリング ソフトウェアなどのシングル スレッド アプリケーションの複数のインスタンスを実行することが挙げられます。[ 6 ]
同時マルチスレッド(SMT、別名ハイパースレッディング、インテルの一般化された商標)を備えたCPUでは、物理コア上の2つ以上の「スレッド」(論理プロセッサ、「仮想コア」)がL1およびL2キャッシュを共有します。局所性の観点からは、それらは同一です。[ 7 ]
非均一メモリアクセス(NUMA) システムでは同様の問題が存在しますが、レイテンシは L1/L2 キャッシュミスではなく、L3 ミスとノード間メモリアクセスから発生します。プログラムのすべてのスレッドを同じ NUMA ノード (または少なくとも同じ CPU ソケット) に制限することで、L3 キャッシュを共有できるようになります。メモリがローカル NUMA ノードから割り当てられるようにするには、追加の設定が必要になる場合があります。[ 8 ] [ 9 ]
プロセッサアフィニティは、処理リソースの静的な分割も強制します。その結果、CPU 負荷の高いプロセスが使用する CPU コアの数を制限し、他のプログラムが使用できるコアを残すことができます。もちろん、これは最適ではありません。他のプログラムが実行されていない場合はリソースが完全に未使用のままになるだけでなく、実行が許可されている少数のコアで、他のプログラムが CPU 負荷の高いプログラムとリソースを競合することになるからです。リソースを分割するより高度な方法には、CPU 優先度設定、CPU 使用率シェア、ハード使用率制限などがあります。[ 10 ] [ 7 ]
また、SMT を備えた CPU では、SMT を認識しないスケジューラは、物理コアが空いている場合でも、ビジー状態のパートナーとビジー状態のコアで作業をスケジュールするという間違いを犯す可能性があります。これにより、2 つのスレッド間でリソースの不必要な競合が発生します。そのため、マルチスレッドの CPU 負荷の高いプログラムでは、スレッドが同じ物理コアを巡って競合しないように、スレッドのアフィニティを手動で割り当てることがよくあります。[ 11 ] [ 12 ]
Linuxでは、プロセスの CPU アフィニティは、taskset(1) プログラム[ 13 ]および sched_setaffinity(2) システムコール[ 14 ]を使用して変更できます。スレッドのアフィニティは、ライブラリ関数pthread_setaffinity_np(3) またはpthread_attr_setaffinity_np(3) を使用して変更できます。
SGIシステムでは、dplaceはプロセスをCPUのセットにバインドします。[ 15 ]
NetBSD 5.0、FreeBSD 7.2、DragonFly BSD 4.7 以降のバージョンではpthread_setaffinity_np、およびを使用できますpthread_getaffinity_np。[ 16 ] NetBSDでは、psrset ユーティリティ[ 17 ]を使用して、スレッドの親和性を特定の CPU セットに設定します。FreeBSD では、 cpuset [ 18 ]ユーティリティを使用して、CPU セットを作成し、これらのセットにプロセスを割り当てます。
DragonFly BSD 1.9 (2007) 以降のバージョンでは、usched_setシステムコールを使用してプロセスの親和性を制御できます。[ 19 ] [ 20 ] DragonFly BSD 3.1 (2012) 以降では、usched ユーティリティを使用して、プロセスを特定の CPU セットに割り当てることができます。[ 21 ]
Solarisでは、 pbind(1) [ 22 ]プログラムを使用して、プロセスとLWPのプロセッサへのバインドを制御できます。プログラムでアフィニティを制御するには、processor_bind(2) [ 23 ]を使用できます。プロセッサセットとローカリティグループの概念を使用するpset_bind(2) [ 24 ]やlgrp_affinity_get(3LGRP) [ 25 ]などのより汎用的なインターフェースも利用可能です。
AIXでは、bindprocessor コマンド[ 26 ] [ 27 ]および bindprocessor() API [ 26 ] [ 28 ]を使用してプロセスのバインディングを制御することが可能です。AIXスケジューラは SMT に対応しており、スループットを最大化するために POWER7/8/9 コアの SMT 状態を 1 ~ 8 スレッドに切り替えることができます。[ 29 ]
macOS には、プロセス、タスク、またはスレッドが実行できるプロセッサのセットを管理する API はありません。代わりに、スレッド アフィニティ API が提供され、カーネルにどのスレッドが同じ L2 キャッシュを共有するようにスケジュールされるべきか、つまり同じ物理 CPU コアで実行されるようにスケジュールされるべきかを指示します。[ 30 ] XNUカーネルは、内部的に各アフィニティ タグを、物理コアに対応する許可された論理コアのセットに変換します。タグが設定されると、スレッド アフィニティ 名前空間がまだ存在しない場合は作成されます。その後、既にバインドされているタグが最も少ないコアにバインドされます。タグは XNU バージョン 8792 ではコア間で移行しません。そのため、タグの数が物理コアの数を超えない限り、各タグは正確に 1 つの物理コアに対応します。名前空間とタグは、親プロセスと子プロセス間で継承されます。[ 31 ]
このAPIはarm64(Apple Silicon)では利用できず、ml_get_max_affinity_sets0を返すようにハードコードされています。[ 32 ]
Windows NTとその後継バージョンでは、スレッドとプロセスの CPU アフィニティは、SetThreadAffinityMask [ 33 ]および SetProcessAffinityMask [ 34 ] API 呼び出しを使用するか、タスクマネージャ インターフェイス (プロセス アフィニティのみ) を介して個別に設定できます。 [ 35 ]
Windowsで各OpenMPスレッドを異なる論理コアに強制的に割り当てるには、 <Windows.h>ヘッダーファイルを使用した以下のCコードを使用します。
#include <Windows.h> #include <omp.h>// OpenMPスレッドアフィニティを設定するvoid setThreadAffinity ( void ) { #pragma omp parallel default(shared) { DWORD_PTR mask = ( DWORD_PTR ) 1 << omp_get_thread_num (); SetThreadAffinityMask ( GetCurrentThread (), mask ); } }pthread_setaffinity_np(3) – NetBSD、 FreeBSD、 DragonFly BSDライブラリ関数マニュアルusched_set(2)kern/kern_usched.c § sys_usched_setusched(8)