インテルのランタイム・ライブラリーには、OpenMP* スレッドを物理処理ユニットにバインドする機能があります。このインターフェイスは、KMP_AFFINITY 環境変数により制御されます。システム (マシン) のトポロジー、アプリケーション、オペレーティング・システムによってスレッド・アフィニティーはアプリケーションの速度に大きく影響します。
スレッド・アフィニティーは、特定のスレッド (仮想実行ユニット) をマルチプロセッサー・コンピューターの物理処理ユニットのサブセットに限定します。マシンのトポロジーにより、スレッド・アフィニティーはプログラムの実行速度に大きな影響を与えます。
スレッド・アフィニティーは、Windows* システムとスレッド・アフィニティー対応カーネルを持つ Linux* システムのバージョンでサポートされていますが、macOS* ではサポートされていません。
インテルの OpenMP* ランタイム・ライブラリーには、OpenMP* スレッドを物理処理ユニットにバインドする機能があります。このバインド機能は、3 種類のインターフェイスで使用できます。これらのインターフェイスは総称して、インテルの OpenMP* スレッド・アフィニティー・インターフェイスと呼ばれます。
高レベルのアフィニティー・インターフェイスでは、環境変数を使用してマシントポロジーを特定し、マシンの物理的位置に基づいて OpenMP* スレッドをプロセッサーに割り当てます。このインターフェイスは、KMP_AFFINITY 環境変数により全体が制御されます。
中レベルのアフィニティー・インターフェイスでは、環境変数を使用してどのプロセッサー(整数 ID) が OpenMP* スレッドにバインドされるかを明示的に指定します。このインターフェイスは、gcc の GOMP_AFFINITY 環境変数と互換性があり、KMP_AFFINITY 環境変数を使用して起動することもできます。GOMP_AFFINITY 環境変数は、Linux* システムでのみサポートされています。ただし、Windows* または Linux* のユーザーにも KMP_AFFINITY 環境変数によって同様の機能が提供されます。
低レベルのアフィニティー・インターフェイスでは、API を使用して OpenMP* スレッドに OpenMP* ランタイム・ライブラリーを呼び出しさせ、実行するプロセッサー・セットを明示的に指定することができます。このインターフェイスは、Linux* システムの sched_setaffinity および関連関数や Windows* システムの SetThreadAffinityMask および関連関数と本質的に似ています。また、KMP_AFFINITY 環境変数の特定のオプションを指定して、低レベル API インターフェイスの動作を指定できます。例えば、KMP_AFFINITY アフィニティー・タイプを無効にすると、低レベルのアフィニティー・インターフェイスが無効になります。また、KMP_AFFINITY や GOMP_AFFINITY 環境変数を使用して、初期アフィニティー・マスクを設定してから、低レベル API インターフェイスでマスクを取得することができます。
次の用語がこのセクションで使用されています。
マシン上のプロセシング要素の合計数は、OS スレッド・コンテキストの数と呼ばれます。
各プロセシング要素は、オペレーティング・システム・プロセッサーまたは OS proc と呼ばれます。
各 OS プロセッサーには、OS proc ID と呼ばれる一意の整数の識別子が関連付けられます。
パッケージとは、シングルまたはマルチコアのプロセッサー・チップを指します。
OpenMP* グローバルスレッド ID (GTID) は、インテルの OpenMP* ランタイム・ライブラリーにより認識されるすべてのスレッドを一意に特定する整数です。ライブラリーを最初に初期化するスレッドは、GTID 0 です。その他すべてのスレッドがライブラリーによって作成される通常の場合で、入れ子構造の並列性がないときは、n-threads-var - 1 (範囲は 1 から ntheads-var - 1 まで) の新しいスレッドが GTID により作成されます。各スレッドの GTID は omp_get_thread_num() 関数によって返される OpenMP* スレッド番号と同じです。高レベルおよび中レベルのインターフェイスは、この概念に大きく基づいています。したがって、有用性は入れ子構造の並列性を含むプログラムに限定されます。低レベルのインターフェイスでは、GTID の概念を利用していないため、任意の並列化レベルを含むプログラムで使用できます。
一部の環境変数はインテル製マイクロプロセッサーおよび互換マイクロプロセッサーで利用可能ですが、インテル製マイクロプロセッサーにおいてより多くの最適化が行われる場合があります。
KMP_AFFINITY 環境変数は、後述の「低レベルのアフィニティー API」で説明されているように、最初の並列領域の前、または omp_get_max_threads()、omp_get_num_procs()、アフィニティー API 呼び出しを含む特定の API 呼び出しの前に設定する必要があります。
KMP_AFFINITY 環境変数には、次の一般的な構文が使用されます。
構文 |
---|
KMP_AFFINITY=[<modifier>,...]<type>[,<permute>][,<offset>] |
例えば、マシン・トポロジー・マップをリストするには、KMP_AFFINITY=verbose,none を指定し、modifier に verbose、type に none を使用します。
次の表は、サポートされる引数のリストです。
引数 |
デフォルト |
説明 |
---|---|---|
noverbose respect granularity=core |
オプション。キーワードと指定子から構成されます。
<proc-list> の構文は、中レベルのアフィニティー・インターフェイスで説明されています。 注複数のプロセッサー・グループがある Windows* では、プロセス・アフィニティー・マスクが単一のプロセッサー・グループに等しい場合、norespect アフィニティー修飾子が仮定されます (これは Windows* 上でのデフォルトです)。そうでない場合は、respect アフィニティー修飾子が使用されます。 |
|
none |
必須です。使用するスレッド・アフィニティーを示します。
logical と physical は推奨されていないタイプですが、下位互換性のためにサポートされています。 |
|
0 |
オプション。正の整数値です。type 値の explicit、none、disabled との使用は無効です。 |
|
0 |
オプション。正の整数値です。type 値の explicit、none、disabled との使用は無効です。 |
"type" は、唯一必須の引数です。
type = none (デフォルト)
OpenMP* スレッドは特定のスレッド・コンテキストにバインドされません。ただし、オペレーティング・システムでアフィニティーがサポートされる場合は、コンパイラーは OpenMP* スレッド・アフィニティー・インターフェイスを使用してマシンのトポロジーを特定します。KMP_AFFINITY=verbose,none を指定して、マシンのトポロジーマップをリストします。
type = balanced
scatter と同様に、すべてのコアに少なくとも 1 つのスレッドが配置されるまで、別のコアにスレッドを配置します。ただし、ランタイムが同じコアで複数のハードウェア・スレッド・コンテキストを使用する際、balanced では OpenMP* スレッドの番号が互いに隣接していることが保証されるのに対して、scatter では保証されません。シングルソケットのシステムの場合のみ CPU 上でサポートされます。
OpenMP* 環境変数 OMP_PROC_BIND=spread は、KMP_AFFINITY=balanced と似ており、マルチソケット CPU システムを含むすべてのプラットフォームで利用できます。
type = compact
compact を指定すると、フリー・スレッド・コンテキストの OpenMP* スレッド <n>+1 は、OpenMP* スレッド <n> が割り当てられたスレッド・コンテキストにできる限り近いスレッド・コンテキストに割り当てられます。例えば、トポロジーマップで、ルートにより近いノードほど、スレッドをソートしたときに上位になります。
type = disabled
disabled を指定すると、スレッド・アフィニティー・インターフェイスを完全に無効にします。これにより、OpenMP* ランタイム・ライブラリーはアフィニティー・インターフェイスがオペレーティング・システムでサポートされていないかのように動作します。これには、kmp_set_affinity や kmp_get_affinity などのような、効果がなく、非ゼロのエラーコードが返される低レベル API インターフェイスが含まれます。
type = explicit
explicit を指定すると、proclist= modifier (このアフィニティー・タイプには必須) を使用して明示的に指定された OS proc ID のリストに OpenMP* スレッドが割り当てられます。「OS プロセッサー ID (GOMP_CPU_AFFINITY) を明示的に指定する」を参照してください。
type = scatter
scatter を指定すると、システム全体にわたってスレッドが均等に分配されます。scatter は、compact の逆です。そのため、マシンのトポロジーマップをソートするとノードリーフは最上位になります。
推奨されていないタイプ: logical と physical
logical と physical は廃止予定で、将来のリリースではサポートされなくなります。両方とも、下位互換性のためにサポートされています。
logical および physical では、整数値が 1 つしかない場合は、permute 指定子ではなく、offset 指定子と解釈されます。これに対し、compact と scatter では、整数値が 1 つのみの場合は permute 指定子と解釈されます。
logical を指定すると、OpenMP* スレッドを連続した論理プロセッサー (ハードウェア・スレッド・コンテキストとも呼ぶ) に割り当てます。このタイプは、permute 指定子が使えないことを除いては、compact と同等です。そのため、KMP_AFFINITY=logical,n は KMP_AFFINITY=compact,0,n と (granularity=fine 修飾子の有無にかかわらず) 同等です。
physical を指定すると、OpenMP* スレッドを連続した物理プロセッサー (コア) に割り当てます。コアごとに 1 つのスレッド・コンテキストしかないシステムの場合、logical と同じです。コアごとに複数のスレッド・コンテキストがあるシステムの場合、physical は permute 指定子が 1 に指定された compact と同じです。つまり、KMP_AFFINITY=physical,n は、KMP_AFFINITY=compact,1,n と (granularity=fine 修飾子の有無にかかわらず) 同等です。これは、コンパイラーがマップをソートしたときにマシンのトポロジーマップの最も内側のレベルを最も外側 (おそらくスレッド・コンテキスト・レベル) に入れ替えることを意味します。このタイプでは permute 指定子はサポートされません。
compact と scatter の例
次の図は、2 つのプロセッサーを搭載しているマシンを示しています。それぞれのプロセッサーには 2 つのコアがあります。各コアはインテル® ハイパースレッディング・テクノロジー (インテル® HT テクノロジー) 対応です。
また、次の図は、KMP_AFFINITY=granularity=fine,compact を指定したときに、OpenMP* スレッドがハードウェア・スレッド・コンテキストにバインドされる様子も示しています。
上図のシステムで scatter を指定すると、次の図のように、OpenMP* スレッドがスレッド・コンテキストに割り当てられます。これは、KMP_AFFINITY=granularity=fine,scatter を指定した結果です。
compact と scatter の両方とも permute と offset が指定できます。ただし、1 つの整数だけを指定した場合は、コンパイラーは値を permute 指定子として解釈します。permute と offset の両方ともデフォルトは 0 です。
permute 指定子はマシンのトポロジーマップをソートしたときに最上位にするレベルを制御します。permute の値により、指定された番号の最上位レベルは最下位にマッピングされ、順位が入れ替わります。ツリーのルートノードは、ソート操作では個別のレベルとは見なされません。
offset 指定子は、スレッド割り当ての開始地点を示します。
次の図は、KMP_AFFINITY=granularity=fine,compact,0,5 を指定した結果を示しています。
前述の例、連続したループ反復間でデータ共有を示す OpenMP* アプリケーションを実行するハードウェア構成について考えてみます。KMP_AFFINITY=compact を指定した際に行われるように、通信オーバーヘッド、キャッシュラインの無効オーバーヘッド、ページ・スラッシングが最小限になるように連続したスレッドを隣接してバインドさせます。ここで、利用可能な OpenMP* スレッドを活用していないアプリケーションに多くの並列領域があるとします。通常、スレッドは、同じコアにある別のアクティブなスレッドとの間にリソースの競合がない場合に実行速度が速くなるため、他のコアを使用せずに複数のスレッドを同じコアにバインドする状態は避けたほうが良いでしょう。通常、スレッドは、同じコアにある別のアクティブなスレッドとの間にリソースの競合がない場合に実行速度が速くなるため、他のコアを使用せずに複数のスレッドを同じコアにバインドする状態は避けたほうが良いでしょう。次の図は、KMP_AFFINITY=granularity=fine,compact,1,0 を設定してこの方法を示したものです。
OpenMP* スレッド n+1 は、別のコアの OpenMP* スレッド n にできるだけ近いスレッド・コンテキストにバインドされます。いったん、それぞれのコアに 1 つの OpenMP* スレッドが割り当てられると、後続の OpenMP* スレッドは利用可能なコアに同じ順番で異なるスレッド・コンテキストに割り当てられます。
タイプの前に付ける修飾子はオプションです。修飾子を指定しない場合は、noverbose、respect、granularity=core が自動で使用されます。
修飾子は左から右の順に解釈され、競合する可能性があります。その場合、以降の競合する修飾子は無視されます。例えば、KMP_AFFINITY=verbose,noverbose,scatter は、KMP_AFFINITY=verbose,scatter を指定するのと同じです。
modifier = noverbose (デフォルト)
詳細なメッセージは出力しません。
modifier = verbose
サポートされるアフィニティーに関するメッセージを出力します。メッセージには、パッケージ数、各パッケージにあるコア数、各コアにあるスレッド・コンテキスト数、物理スレッド・コンテキストにバインドされた OpenMP* スレッドについての情報が含まれます。
物理スレッド・コンテキストにバインドされた OpenMP* スレッドについての情報は、ハードウェア・スレッド・コンテキストとオペレーティング・システム (OS) プロセッサー (proc) ID 間のマッピングの形式で間接的に示されます。各 OpenMP* スレッドのアフィニティー・マスクは、OS プロセッサー ID セットとして出力されます。
例えば、インテル® ハイパースレッディング・テクノロジーを無効にした 2 つのプロセッサーを搭載したデュアルコア・システムで KMP_AFFINITY=verbose,scatter を指定すると、プログラムの実行時に次のようなメッセージが出力されます。
Verbose, scatter を指定した場合のメッセージ |
---|
... KMP_AFFINITY: Initial OS proc set respected: 0,1,2,3 KMP_AFFINITY: affinity capable, using hwloc. KMP_AFFINITY: 4 available OS procs KMP_AFFINITY: Uniform topology KMP_AFFINITY: 2 sockets x 2 cores/socket x 1 threads/core (4 total cores) KMP_AFFINITY: OS proc to physical thread map: KMP_AFFINITY: OS proc 0 maps to socket 0 core 0 thread 0 KMP_AFFINITY: OS proc 2 maps to socket 0 core 1 thread 0 KMP_AFFINITY: OS proc 1 maps to socket 3 core 0 thread 0 KMP_AFFINITY: OS proc 3 maps to socket 3 core 1 thread 0 KMP_AFFINITY: pid 79739 tid 79739 thread 0 bound to OS proc set 0 KMP_AFFINITY: pid 79739 tid 79740 thread 2 bound to OS proc set 2 KMP_AFFINITY: pid 79739 tid 79741 thread 3 bound to OS proc set 3 KMP_AFFINITY: pid 79739 tid 79742 thread 1 bound to OS proc set 1 |
verbose 修飾子を指定すると、いくつかの標準的、一般的なメッセージが生成されます。次の表は、メッセージの説明です。
メッセージ |
説明 |
---|---|
"affinity capable (アフィニティー可)" |
すべてのコンポーネント (コンパイラー、オペレーティング・システム、ハードウェア) でスレッドのバインドが可能なようにアフィニティーがサポートされていることを示します。 |
"decoding x2APIC ids" |
スレッドを各オペレーティング・システムのプロセッサーにバインドし、cpuid 命令の出力をデコードすることにより、マシントポロジーが特定されたことを示します。 |
"using hwloc" |
Portable Hardware Locality* (hwloc) ライブラリーを使用してマシントポロジーが特定されることを示します。 |
"using /proc/cpuinfo (/proc/cpuinfo 使用)" |
Linux* のみ。cpuinfo がマシントポロジーを特定するのに使用されることを示します。 |
"using flat (flat 使用)" |
オペレーティング・システムのプロセッサー ID は、物理パッケージ ID と同じと見なされています。マシントポロジーを特定するこの方法は、その他の方法がどれも使用できず、また実際のマシントポロジーを正しく検出しない可能性がある場合に使用されます。 |
"uniform topology" |
マシントポロジーのマップは、どの階層においてもリーフがそろった完全なツリーです。 |
次に、オペレーティング・システムのプロセッサーからスレッド・コンテキスト ID へのマッピングが出力されます。OpenMP* スレッドのスレッド・コンテキスト ID へのバインドは、アフィニティー・タイプが none でない限り、次に出力されます。詳細は、「マシントポロジーの特定」を参照してください。
modifier = granularity
OpenMP* スレッドを特定のパッケージやコアにバインドすると、インテル® ハイパースレッディング・テクノロジー (インテル® HT テクノロジー) 対応のインテル® プロセッサーを搭載したシステムでパフォーマンス・ゲインを期待できます。しかし、一般的に各 OpenMP* スレッドを特定のコアにある特定のスレッド・コンテキストにバインドしても効果は期待できません。粒度は、トポロジーマップ内で OpenMP* スレッドのフロートが許可される最下位レベルを示します。
この修飾子は、次の指定子をサポートします。
指定子 |
説明 |
---|---|
core |
デフォルト。異なるスレッド・コンテキスト間でフロートするため、すべての OpenMP* スレッドをコアにバインドできます。 |
fine または thread |
最も細かい粒度レベルです。各 OpenMP* スレッドが 1 つのスレッド・コンテキストにバインドされます。この 2 つの指定子は、機能的に同じです。 |
tile、die、node、group、または socket |
タイル、ダイ、NUMA ノード、グループ、またはソケットを構成するコアのスレッド・コンテキスト間でフロートするため、すべての OpenMP* スレッドをタイル、ダイ、NUMA ノード、グループ、またはソケットにバインドできます。 |
2 つのプロセッサーを搭載したデュアルコア・システムで、インテル® ハイパースレッディング・テクノロジー (インテル® HT テクノロジー) が有効な場合に、KMP_AFFINITY=verbose,granularity=core,compact を指定すると、プログラムの実行時に次のようなメッセージが出力されます。
Verbose, granularity=core,compact を指定した場合のメッセージ |
---|
KMP_AFFINITY: Initial OS proc set respected: 0-7 KMP_AFFINITY: decoding x2APIC ids. KMP_AFFINITY: 8 available OS procs KMP_AFFINITY: Uniform topology KMP_AFFINITY: 2 sockects x 2 cores/socket x 2 threads/core (4 total cores) KMP_AFFINITY: OS proc to physical thread map: KMP_AFFINITY: OS proc 0 maps to socket 0 core 0 thread 0 KMP_AFFINITY: OS proc 4 maps to socket 0 core 0 thread 1 KMP_AFFINITY: OS proc 2 maps to socket 0 core 1 thread 0 KMP_AFFINITY: OS proc 6 maps to socket 0 core 1 thread 1 KMP_AFFINITY: OS proc 1 maps to socket 3 core 0 thread 0 KMP_AFFINITY: OS proc 5 maps to socket 3 core 0 thread 1 KMP_AFFINITY: OS proc 3 maps to socket 3 core 1 thread 0 KMP_AFFINITY: OS proc 7 maps to socket 3 core 1 thread 1 KMP_AFFINITY: pid 40880 tid 40880 thread 0 bound to OS proc set 0,4 KMP_AFFINITY: pid 40880 tid 40881 thread 1 bound to OS proc set 0,4 KMP_AFFINITY: pid 40880 tid 40882 thread 2 bound to OS proc set 2,6 KMP_AFFINITY: pid 40880 tid 40883 thread 3 bound to OS proc set 2,6 KMP_AFFINITY: pid 40880 tid 40884 thread 4 bound to OS proc set 1,5 KMP_AFFINITY: pid 40880 tid 40885 thread 5 bound to OS proc set 1,5 KMP_AFFINITY: pid 40880 tid 40886 thread 6 bound to OS proc set 3,7 KMP_AFFINITY: pid 40880 tid 40887 thread 7 bound to OS proc set 3,7 |
各 OpenMP* スレッドのアフィニティー・マスクは、OpenMP* スレッドがバインドされたオペレーティング・システムのプロセッサー・セットとしてリストされます (上を参照)。
次の図は、OpenMP* スレッドがバインドされた上記のリストのマシントポロジーを示しています。
これに対し、KMP_AFFINITY=verbose,granularity=fine,compact または KMP_AFFINITY=verbose,granularity=thread,compact を指定すると、プログラムの実行時に各 OpenMP* スレッドが 1 つのハードウェア・スレッド・コンテキストにバインドされます。
Verbose, granularity=fine,compact を指定した場合のメッセージ |
---|
KMP_AFFINITY: Initial OS proc set respected: 0-7 KMP_AFFINITY: decoding x2APIC ids. KMP_AFFINITY: 8 available OS procs KMP_AFFINITY: Uniform topology KMP_AFFINITY: 2 sockets x 2 cores/socket x 2 threads/core (4 total cores) KMP_AFFINITY: OS proc to physical thread map: KMP_AFFINITY: OS proc 0 maps to socket 0 core 0 thread 0 KMP_AFFINITY: OS proc 4 maps to socket 0 core 0 thread 1 KMP_AFFINITY: OS proc 2 maps to socket 0 core 1 thread 0 KMP_AFFINITY: OS proc 6 maps to socket 0 core 1 thread 1 KMP_AFFINITY: OS proc 1 maps to socket 3 core 0 thread 0 KMP_AFFINITY: OS proc 5 maps to socket 3 core 0 thread 1 KMP_AFFINITY: OS proc 3 maps to socket 3 core 1 thread 0 KMP_AFFINITY: OS proc 7 maps to socket 3 core 1 thread 1 KMP_AFFINITY: pid 40895 tid 40895 thread 0 bound to OS proc set 0 KMP_AFFINITY: pid 40895 tid 40896 thread 1 bound to OS proc set 4 KMP_AFFINITY: pid 40895 tid 40897 thread 2 bound to OS proc set 2 KMP_AFFINITY: pid 40895 tid 40898 thread 3 bound to OS proc set 6 KMP_AFFINITY: pid 40895 tid 40899 thread 4 bound to OS proc set 1 KMP_AFFINITY: pid 40895 tid 40900 thread 5 bound to OS proc set 5 KMP_AFFINITY: pid 40895 tid 40901 thread 6 bound to OS proc set 3 KMP_AFFINITY: pid 40895 tid 40902 thread 7 bound to OS proc set 7 |
OpenMP* をハードウェア・コンテキストにバインドする例は、最初の例に示されています。
granularity=fine を指定すると、各 OpenMP* スレッドが 1 つの OS プロセッサーにバインドされます。これは、granularity=thread と等価で、現在、最も微細な粒度レベルです。
modifier = respect (デフォルト)
プロセスの元のアフィニティー・マスク、厳密には、OpenMP* ランタイム・ライブラリーを初期化するスレッドに指定されたアフィニティー・マスクが順守されます。動作は、Linux* と Windows* で異なります。
Windows*: プロセスの元のアフィニティー・マスクが順守されます。
Linux*: OpenMP* ランタイム・ライブラリーを初期化するスレッドのアフィニティー・マスクが順守されます。
前の例と同じ、インテル® ハイパースレッディング・テクノロジー (インテル® HT テクノロジー) が有効なシステムで、KMP_AFFINITY=verbose,compact を指定して、最初のアフィニティー・マスク {4,5,6,7} (各コアでスレッド・コンテキストは 1) を起動すると、コンパイラーでは、インテル® ハイパースレッディング・テクノロジー (インテル® HT テクノロジー) が無効の 2 つのプロセッサーを搭載したデュアルコアのマシンであると判断されます。
Verbose,compact を指定した場合のメッセージ |
---|
KMP_AFFINITY: Initial OS proc set respected: 4-7 KMP_AFFINITY: decoding x2APIC ids. KMP_AFFINITY: 4 available OS procs KMP_AFFINITY: Uniform topology KMP_AFFINITY: 2 sockets x 2 cores/socket x 1 threads/core (4 total cores) KMP_AFFINITY: OS proc to physical thread map: KMP_AFFINITY: OS proc 4 maps to socket 0 core 0 thread 1 KMP_AFFINITY: OS proc 6 maps to socket 0 core 1 thread 1 KMP_AFFINITY: OS proc 5 maps to socket 3 core 0 thread 1 KMP_AFFINITY: OS proc 7 maps to socket 3 core 1 thread 1 KMP_AFFINITY: pid 41032 tid 41032 thread 0 bound to OS proc set 4 KMP_AFFINITY: pid 41032 tid 41033 thread 1 bound to OS proc set 6 KMP_AFFINITY: pid 41032 tid 41034 thread 2 bound to OS proc set 5 KMP_AFFINITY: pid 41032 tid 41035 thread 3 bound to OS proc set 7 |
マシンで利用可能なスレッド・コンテキストは 4 個であるため、デフォルトではコンパイラーにより OpenMP* parallel 構造で 4 個のスレッドが作成されます。
次の図は、OMP_NUM_THREADS=8 で 4 個の OpenMP* スレッドを要求した場合の対応するマシンのトポロジーマップとスレッドの配置です。
ローカルの cpuid 情報を使用してマシントポロジーを特定する場合、インテル® ハイパースレッディング・テクノロジー (インテル® HT テクノロジー) をサポートしていないマシンとサポートしているけれども無効になっているマシンとの区別がつきません。そのため、そのレベルの要素 (ノード) に兄弟がいない場合は、コンパイラーはそのレベルをマップに含めません (ただし、パッケージレベルは常に含まれます)。前述したように、パッケージレベルは、マシンに 1 つのパッケージしかない場合でも、常にトポロジーマップに表示されます。
modifier = norespect
プロセスのアフィニティー・マスクは順守されません。OpenMP* スレッドはすべてのオペレーティング・システムのプロセッサーにバインドされます。
physical と logical のみがアフィニティー・タイプとしてサポートされていた以前のバージョンの OpenMP* ランタイム・ライブラリーでは、norespect がデフォルトで、修飾子としては認識されませんでした。
compact と scatter がアフィニティー・タイプに追加されてから、デフォルトは respect に変更されました。そのため、アプリケーションが不完全な初期スレッド・アフィニティー・マスクを指定した場合、新しいコンパイラー・バージョンではスレッドのバインドが変更されることがあります。
modifier = nowarnings
アフィニティー・インターフェイスからの警告メッセージを出力しません。
modifier = warnings (デフォルト)
アフィニティー・インターフェイスからの警告メッセージを出力します (デフォルト)。
IA-32 およびインテル® 64 アーキテクチャー・システムでは、パッケージに APIC (Advanced Programmable Interrupt Controller) がある場合、コンパイラーは cpuid 命令を使用して package id、core id、および thread context id を入手します。通常の状態では、システム上の各スレッド・コンテキストには起動時に固有の APIC ID が割り当てられます。コンパイラーは、OS スレッド・コンテキスト数 (マシン上のプロセシング要素の合計数) とともに cpuid 命令によって取得されるその他の情報を取得し、APIC ID から package ID、core ID、および thread context ID を識別する方法を特定します。
cpuid 命令で APIC ID を指定する方法はいくつかあります - リーフ 4 で指定する以前の方法とリーフ 11 とリーフ 31 で指定するより新しい方法。リーフ 4 で利用可能な一意の APIC ID は 256 のみです。リーフ 11 とリーフ 31 ではそのような制限はありません。
通常、パッケージ上のすべての core id とコア上のすべての thread context id は連続しています。ただし、上の図で示されるように、package id の番号付けでは連続していないことも一般的です。
コンパイラーがいずれの方法を使用してもトポロジーを特定できないが、アフィニティーがオペレーティング・システムでサポートされている場合は、警告メッセージが出力され、トポロジーは flat と想定されます。例えば、ある flat トポロジーでは、オペレーティング・システムがプロセス N をパッケージ N にマップし、1 コアにつき 1 スレッドのみ、そして各パッケージには 1 コアがあると想定されます。
マシントポロジーが上記のように特定できない場合は、手動で /proc/cpuinfo を一時ファイルにコピーして、エラーを修正し、「KMP_CPUINFO_FILE と /proc/cpuinfo」のセクションで説明されているように、KMP_CPUINFO_FILE=<temp_filename> 環境変数を介して、マシントポロジーを OpenMP* ランタイム・ライブラリーに指定します。
マシントポロジーの特定に使用する方法にかかわらず、マシン上の各コアで 1 コアにつき 1 スレッドしかない場合は、スレッド・コンテキスト・レベルはトポロジーマップには表示されません。マシン上の各パッケージで 1 パッケージにつき 1 コアしかない場合は、コアレベルはトポロジーマップには表示されません。各パッケージには異なる数のコアが含まれている可能性があり、各コアは異なる数のスレッド・コンテキストをサポートしている可能性があるため、トポロジーマップは完全なツリー状とは限りません。
パッケージレベルは、マシンに 1 つのパッケージしかない場合でも、常にトポロジーマップに表示されます。
の OpenMP* ランタイム・ライブラリーが Linux* システム上のマシントポロジーを検出する方法の 1 つに、/proc/cpuinfo の内容を解析する方法があります。このファイル (または Linux* ファイルシステムにマップされているデバイス) の内容が不十分であったり、エラーを含む場合は、内容を書き込み可能な一時ファイル <temp_file> にコピーし、修正するか必要な情報を追加して、さらに KMP_CPUINFO_FILE=<temp_file> を設定します。
これを行うことにより、OpenMP* ランタイム・ライブラリーは、/proc/cpuinfo に含まれる情報を読み取る代わりに、また APIC ID をデコードしてマシントポロジーを検出する代わりに、KMP_CPUINFO_FILE が示す <temp_file> を読み取ります。つまり、<temp_file> に含まれる情報が、その他の方法よりも優先されます。/proc/cpuinfo を持たない Windows* システムでは、KMP_CPUINFO_FILE インターフェイスを使用することができます。
/proc/cpuinfo または <temp_file> にはマシン上の各プロセシング要素のエントリーのリストが含まれています。各プロセッサー要素にはエントリー (分かりやすい名前と各行の値) のリストが含まれています。空白行は各プロセッサー要素を区切ります。次のフィールドのみが、<temp_file> または /proc/cpuinfo にある各エントリーからマシントポロジーを特定するのに使用されます。
フィールド |
説明 |
---|---|
processor: |
プロセシング要素の OS ID を指定します。OS ID は一意でなければなりません。processor フィールドと physical id フィールドは、このインターフェイスの使用に唯一必須のものです。 |
physical id: |
物理チップ ID であるパッケージ ID を指定します。各パッケージには複数のコアが含まれています。パッケージレベルは常にインテル® コンパイラーの OpenMP* ランタイム・ライブラリーのマシン・トポロジー・モデルにあります。 |
core id: |
コア ID を指定します。コア ID がない場合は、デフォルトの 0 に設定されます。マシン上の各パッケージがシングルコアしかない場合は、(コア ID フィールドのいくつかが非ゼロの場合でも) コアレベルはマシンのトポロジーマップにはありません。 |
apicid: |
スレッド ID を指定します。コア ID がない場合は、デフォルトの 0 に設定されます。マシン上の各コアでシングルスレッドしかない場合は、(スレッド ID フィールドのいくつかが非ゼロの場合でも) スレッドレベルはマシンのトポロジーマップにはありません。 |
node_n id : |
これは、/proc/cpuinfo の拡張です。不均等メモリーアクセス (NUMA: Non-Uniform Memory Access) システムにおける異なるレベルのメモリー相互接続でノードを指定するのに使用できます。任意のレベル n がサポートされています。node_0 レベルがパッケージレベルに最も近いレベルで、複数のパッケージがレベル 0 で 1 つのノードを構成します。レベル 0 の複数のノードは、レベル 1 で 1 つのノードを構成します。 |
各エントリーは、示されているとおりに正確に小文字で、オプションのスペース、コロン (:)、さらにオプションのスペース、そして整数 ID を記述します。これ以外のフィールドは無視されます。
多くの Linux* バリアントで thread id フィールドが /proc/cpuinfo に含まれておらず、siblings とラベル付けされたフィールドがノードごとのスレッド数またはパッケージごとのノード数を指定することはよくあることことです。ただし、インテルの OpenMP* ランタイム・ライブラリーは、siblings とラベルされたフィールドを無視するため、thread id フィールドと siblings フィールドを区別できます。このような状況が発生した場合は、警告メッセージの「物理ノード/パッケージ/コア/スレッド ID が一意ではありません。」が表示されます (指定されたタイプが nowarnings の場合を除く)。
64 ビットの Windows* オペレーティング・システムでは、複数のプロセッサー・グループが 64 を超えるプロセッサーに対応できます。各グループのサイズは、64 プロセッサーの最大値までに制限されます。
複数のプロセッサー・グループが検出されると、デフォルトでは 2 レベルのツリー (レベル 0 はグループ内のプロセッサー、レベル 1 は異なるグループ) であると判断されます。そして、グループにバインドされた OpenMP* スレッド数がグループ内のプロセッサー数と等しくなるまで、スレッドがグループに割り当てられ、後続のスレッドは次のグループに割り当てられます。
デフォルトでは、スレッドはグループ内のすべてのプロセッサー間を移動することができ、粒度はグループの数と同じになります [granularity=group]。このバインドを無効にし、compact、scatter など、別のアフィニティー・タイプを明示的に使用することができます。その場合は、スレッドが異なるグループの複数のプロセッサーにバインドされないように、十分に細かい粒度を使用する必要があります。
KMP_TOPOLOGY_METHOD 環境変数を設定して、特定のマシン・トポロジー・モデル・メソッドを OpenMP* で使用するように強制できます。
値 |
説明 |
---|---|
cpuid_leaf11 |
cpuid 命令のリーフ 11 により指定された APIC ID をデコードします。 |
cpuid_leaf4 |
cpuid 命令のリーフ 4 により指定された APIC ID をデコードします。 |
cpuinfo |
KMP_CPUINFO_FILE を指定しない場合は、/proc/cpuinfo を解析してトポロジーを特定するように強制できます (Linux* のみ)。 前述のように、KMP_CPUINFO_FILE を指定する場合は、それを使用します (Windows* または Linux*)。 |
group |
2 レベルのマップ (レベル 0 はグループ内の異なるプロセッサーを指定、レベル 1 は異なるグループを指定) として判断します (64 ビットの Windows* のみ)。 |
flat |
プロセッサーの flat (線形) リストとして判断します。 |
hwloc |
Portable Hardware Locality (hwloc) ライブラリーと同様に判断します。最も詳細なモデルで、NUMA ノード、パッケージ、コア、ハードウェア・スレッド、キャッシュ、Windows* プロセッサー・グループなどを含みます。 |
GOMP_CPU_AFFINITY 環境変数は、後述の「低レベルのアフィニティー API」で説明されているように、最初の並列領域の前、または omp_get_max_threads()、omp_get_num_procs()、アフィニティー API 呼び出しを含む特定の API 呼び出しの前に設定する必要があります。
ライブラリーにハードウェア・トポロジーを検出させて自動的に OpenMP* スレッドをプロセシング要素に割り当てる代わりに、オペレーティング・システム (OS) プロセッサー (proc) ID のリストを使用して、明示的に割り当てを指定することができます。ただし、これには OS proc ID がどのプロセシング要素を表現しているかについての知識が必要です。
-qopenmp-lib compat コンパイラー・オプションで有効にされたインテルの OpenMP* 互換ライブラリーを使用する際、Linux* システムでは GOMP_AFFINITY 環境変数を使用して OS プロセッサー ID を指定します。その構文は、libgomp のものと同じです (<proc_list> が GOMP_AFFINITY 環境文字列全体を生成すると想定)。
値 |
説明 |
---|---|
<proc_list> := |
<entry> | <elem> , <list> | <elem> <whitespace> <list> |
<elem> := |
<proc_spec> | <range> |
<proc_spec> := |
<proc_id> |
<range> := |
<proc_id> - <proc_id> | <proc_id> - <proc_id> : <int> |
<proc_id> := |
<positive_int> |
このリストに指定されている OS プロセッサーは、その後 OpenMP* グローバルスレッド ID の順に OpenMP* スレッドに割り当てられます。リストにある要素よりも多くの OpenMP* スレッドが作成された場合は、リストサイズを基にする割り当てが行われます。つまり、OpenMP* グローバルスレッド ID n は、リスト要素 n mod <list_size> にバインドされます。
前の図の例と同じように OS proc ID が割り当てられた前述のマシン、インテル® ハイパースレッディング・テクノロジー (インテル® HT テクノロジー) が有効になっていないデュアルコア、デュアルパッケージ・マシンについて考えてみましょう。アプリケーションが 4 (デフォルト) ではなく、マシンを超過してしまう 6 つの OpenMP* スレッドを生成するとします。GOMP_AFFINITY=3,0-2, の場合、次の図に示されているように、gcc でコンパイルして、libgomp でリンクしたときと同じように OpenMP* スレッドがバインドされます。
同じ構文が、OS proc ID リストを KMP_AFFINITY 環境変数文字列の proclist=[<proc_list>] 修飾子に指定するのに使用できます。若干の差異として、gcc の OpenMP* ランタイム・ライブラリー libgomp と全く同じセマンティクスを持つために、GOMP_AFFINITY 環境変数は granularity=fine を示唆します。KMP_AFFINITY 環境変数で granularity= 指定子を使用せずに OS proc リストを指定すると、デフォルトの granularity は変更されません。つまり、OpenMP* スレッドはシングルコアの異なるスレッド・コンテキスト間でフロートすることができます。このように、GOMP_AFFINITY=<proc_list> は、KMP_AFFINITY="granularity=fine,proclist=[<proc_list>],explicit" のエイリアスです。
KMP_AFFINITY 環境変数文字列では、構文はオペレーティング・システムのプロセッサー ID セットを処理するために拡張されます。ユーザーは OpenMP* スレッドが実行 (「フロート」) できるオペレーティング・システムのプロセッサー ID セットを角括弧で指定できます。
値 |
説明 |
---|---|
<proc_list> := |
<proc_id> | { <float_list> } |
<float_list> := |
<proc_id> | <proc_id> , <float_list> |
これにより、機能的には granularity= specifier と同様で、より柔軟性を発揮します。OpenMP* スレッドが実行される OS プロセッサーには、マシントポロジーで隣接している OS プロセッサーは除外されますが、その他の離れた OS プロセッサーは含まれます。次の図に示すように KMP_AFFINITY="granularity=verbose,fine,proclist=[3,0,{1,2},{1,2}],explicit" を使用して、OS プロセッサー 1 と OS プロセッサー 2 の間で OpenMP* スレッド 2 と 3 を「フロート」させることができます。
verbose も指定された場合、アプリケーションが実行された出力結果には以下が含まれます。
KMP_AFFINITY="granularity=verbose,fine,proclist=[3,0,{1,2},{1,2}],explicit" |
---|
KMP_AFFINITY: Initial OS proc set respected: 0,1,2,3 KMP_AFFINITY: decoding x2APIC ids. KMP_AFFINITY: 4 available OS procs KMP_AFFINITY: Uniform topology KMP_AFFINITY: 2 sockets x 2 cores/socket x 1 threads/core (4 total cores) KMP_AFFINITY: OS proc to physical thread map: KMP_AFFINITY: OS proc 0 maps to socket 0 core 0 thread 0 KMP_AFFINITY: OS proc 2 maps to socket 0 core 1 thread 0 KMP_AFFINITY: OS proc 1 maps to socket 3 core 0 thread 0 KMP_AFFINITY: OS proc 3 maps to socket 3 core 1 thread 0 KMP_AFFINITY: pid 41464 tid 41464 thread 0 bound to OS proc set 3 KMP_AFFINITY: pid 41464 tid 41465 thread 1 bound to OS proc set 0 KMP_AFFINITY: pid 41464 tid 41466 thread 2 bound to OS proc set 1,2 KMP_AFFINITY: pid 41464 tid 41467 thread 3 bound to OS proc set 1,2 KMP_AFFINITY: pid 41464 tid 41468 thread 4 bound to OS proc set 3 KMP_AFFINITY: pid 41464 tid 41469 thread 5 bound to OS proc set 0 |
ユーザーがプログラムの実行開始前に環境変数を設定して (または並列領域に到達する前に kmp_settings インターフェイスを使用して) OpenMP* スレッドを OS proc にバインドさせなくても、それぞれの OpenMP* スレッドでは、実行し、kmp_set_affinity API 呼び出しでバインドさせる適切な OS proc セットを決定できます。
このアフィニティー・インターフェイスを使用することで、スレッドを実行するハードウェア・リソースを完全に制御することができます。そのためには、論理 CPU (OS によって制御されるハードウェア・スレッドの列挙子) がどのように実行マシンの物理ハードウェアへマップされるかを詳しく理解する必要があります。マップ方法はマシンによって異なるため、マシン固有の情報をコードに記述すると、別のマシンで実行するときに不適切なアフィニティーを強制することになります。この詳細レベルで最適化を検討している場合、コードを別のマシンに移動する可能性が高いでしょう。
このインターフェイスを使用することで、メッセージ・パッシング・インターフェイス (MPI) などのプログラムの起動メカニズムによって設定されるリソースの制約も無視できます。特に、同じノード上の複数の OpenMP* プロセスが同じハードウェア・スレッドを使用しないようにできます。これも、パフォーマンスの低下を引き起こすアフィニティーを強制する可能性があります。OpenMP* ランタイムはこの問題の発生を防ぐことも、問題が発生した際に警告を出力することもしません。このインターフェイスはエキスパート向けです。注意して使用してください。
移植性に優れ、低レベルの知識を必要としない、できるだけ高いレベルのアフィニティー設定を利用することを推奨します。
タイプ名 kmp_affinity_mask_kind が omp_lib.h または omp_lib.mod で定義される Fortran API インターフェイス:
構文 |
説明 |
---|---|
integer function kmp_set_affinity(mask) |
現在の OpenMP* スレッドのアフィニティー・マスクを mask に設定します。ここで、mask は以下にリストされた API 呼び出しで作成された OS proc ID のセットで、スレッドは、そのセットの OS proc でのみ実行します。成功した場合はゼロ (0)、エラーコードの場合は非ゼロを返します。 |
integer function kmp_get_affinity(mask) |
現在の OpenMP* スレッドのアフィニティー・マスクを取得し、kmp_create_affinity_mask() への呼び出しで前もって初期化されているはずの mask に格納します。成功した場合はゼロ (0)、エラーコードの場合は非ゼロを返します。 |
integer function kmp_get_affinity_max_proc() |
マシン上の最大 OS proc ID に 1 を足した値が返されます。すべての OS proc ID が 0 (包括的) と kmp_get_affinity_max_proc() (排他的) の間で保証されます。 |
subroutine kmp_create_affinity_mask(mask) |
新しい OpenMP* スレッド・アフィニティー・マスクを割り当て、mask を OS proc の空のセットに初期化します。セット自身として、実際のセットへのポインターとして、またはセットを示すテーブルへのインデックスとしてなど、kmp_affinity_mask_t のオブジェクトの使用は自由です。実際の表現が何であるかといった仮定をしてはなりません。 |
subroutine kmp_destroy_affinity_mask(mask) |
OpenMP* スレッド・アフィニティー・マスクの割り当てを解除するkmp_create_affinity_mask() への各呼び出しで、対応する kmp_destroy_affinity_mask() への呼び出しがなければなりません。 |
integer function kmp_set_affinity_mask_proc(proc, mask) |
OS proc ID proc をセット mask に追加します (追加されていない場合)。成功した場合はゼロ (0)、エラーコードの場合は非ゼロを返します。 |
integer function kmp_unset_affinity_mask_proc(proc, mask) |
OS proc ID proc がセット mask にある場合、それを削除します。成功した場合はゼロ (0)、エラーコードの場合は非ゼロを返します。 |
integer function kmp_get_affinity_mask_proc(proc, mask)integer proc |
OS proc ID proc がセット mask にある場合は 1 を返します。ない場合は、0 を返します。 |
OpenMP* スレッドが kmp_set_affinity() への呼び出しに成功して自身のアフィニティー・マスクを設定したら、後続の kmp_set_affinity() への呼び出しでリセットされない限り、少なくとも並列領域が終わるまでは、そのスレッドは対応する OS proc セットにバインドされたままです。
並列領域間では、アフィニティー・マスク (および OS proc バインド機能に対応する OpenMP* スレッド) はスレッド・プライベートのデータ・オブジェクトと見なされ、「OpenMP* アプリケーション・プログラム・インターフェイス」で説明されているように、同様の永続性を持ちます。詳細は、OpenMP* API 仕様 (http://www.openmp.org) を参照してください。該当部分の一部を以下に抜粋 (参考訳) します。
アフィニティー・マスクとスレッドバインド機能が 2 つの連続したアクティブな並列領域間で永続するには、次の 3 つの条件がすべて満たされなければなりません。
別の明示的な並列領域の内側に入れ子構造の並列領域がないこと。
両方の並列領域の実行に使用されているスレッド数が同じであること。
囲まれたタスク領域にある dyn-var 内部制御変数の値が、両方の並列領域の入口で false であること。」
そのため、プログラムの実行が上記の OpenMP* 仕様の 3 つの規則に従っており、唯一の目的が各スレッドのアフィニティー・マスクを設定することであるプログラムの開始時点で並列領域を作成すると、低レベルのアフィニティー API 呼び出しにより、ユーザーは KMP_AFFINITY 環境変数の動作と同じことができます。
次に、低レベルのインターフェイスの使用例を示します。このコードは、実行スレッドを特定の論理 CPU にバインドします。
例 |
---|
|
このプログラムは、OS proc ID のターゲットマシンの物理プロセシング要素へのマッピングに関する知識に基づいて記述されたものです。別のマシンや異なる OS がインストールされている場合、プログラムは実行されますが、OpenMP* スレッドの物理プロセシング要素へのバインド動作は異なります。また、明示的に効率の悪い分配を強制してしまう可能性があります。