この章では、OpenMP API を使用するプログラムに影響するコンパイラおよび実行時オプションを説明します。
並列化されたプログラムをマルチスレッド環境で実行するには、プログラムのスレッド数に 1 より大きい数値を設定する必要があります。そのためには、プログラム実行前に OMP_SET_NUM_THREADS 環境変数に値を設定します。スレッド数は、1 より大きい値を指定して omp_set_num_threads() 関数を呼び出すか、num_threads 節に PARALLEL 指令を指定することにより、実行中のプログラムから設定することもできます。
Sun Studio のコンパイラおよび OpenMP についての最新情報は、Sun の開発者向けポータルサイト (http://developers.sun.com/sunstudio) を参照してください。
OpenMP の指令を使用して明示的に並列化を有効にするには、cc、CC、または f95 のオプションフラグ -xopenmp を指定してプログラムをコンパイルします。このフラグには、キーワードの引数を 1 つ指定することができます。f95 コンパイラでは、-xopenmp と -openmp を同義語として使用することができます。
-xopenmp フラグには、次のキーワードサブオプションを指定することができます。
|
OpenMP プラグマの認識を有効にします。 -xopenmp=parallel の最小限の最適化レベルは -xO3 です。 コンパイラは、必要に応じて最適化のレベルを低いレベルから -xO3 に上げ、警告を出力します。 |
|
|
OpenMP プラグマの認識を有効にします。 最適化のレベルが -xO3 より低い場合でも、コンパイラは最適化のレベルを上げません。 -xO2 -openmp=noopt のように、最適化レベルを明示的に -xO3 よりも下げると、コンパイラはエラーを出力します。 -openmp=noopt を使用して最適化レベルを指定しない場合、OpenMP プラグマが認識され、プログラムが並列化されますが、最適化は行われません。 |
|
|
このオプションはサポートされていません。 OpenMP スタブライブラリは、ユーザーの便宜上の理由で提供されています。 OpenMP ライブラリルーチンを呼び出しても OpenMP プラグマを無視するような OpenMP プログラムをコンパイルするには、-xopenmp オプションを指定しないでコンパイルします。その後、libompstubs.a ライブラリを使ってオブジェクトファイルをリンクします。 次に例を示します。% cc omp_ignore.c -lompstubs libompstubs.a と OpenMP 実行時ライブラリの libmtsk.so 両方とのリンクはサポートされていません。両方とリンクすると、予期しない動作になることがあります。 |
|
|
OpenMP プラグマの認識を無効にし、最適化レベルを変更しません |
その他の注
コマンド行で -xopenmp を指定しない場合、コンパイラでは -xopenmp=none (OpenMP プラグマ の認識を無効にする) を指定したと見なされます。
-xopenmp をキーワードサブオプションなしで指定した場合、コンパイラでは -xopenmp=parallel を指定したと見なされます。
-xopenmp=parallel または -xopenmp=noopt を指定すると、_OPENMP プリプロセッサトークンが YYYYMM (C/C++ では 200805L、Fortran 95 では 200805) として定義されます。
dbx を使用して OpenMP プログラムをデバッグする場合、-xopenmp=noopt -g を使用してコンパイルします。
-xopenmp のデフォルトの最適化レベルは、将来のリリースで変更される可能性があります。適切な最適化レベルを明示的に指定することによって、コンパイル警告メッセージの表示を防止することができます。
Fortran 95 では、-xopenmp、-xopenmp=parallel、-xopenmp=noopt を指定すると、-stackvar が自動的に追加されます。
別々の手順で OpenMP プログラムをコンパイルしてリンクする場合は、コンパイル時およびリンク時にそれぞれ -xopenmp を含めます。
コンパイラの並列化メッセージを表示するには、-xvpara C/C++ オプションまたは -vpara Fortran 95 オプションを使用します。
Solaris プラットフォーム上で最適なパフォーマンスおよび機能を得るために、最新の OpenMP 実行時ライブラリ libmtsk.so が実行中のシステムにインストールされていることを確認してください。
OpenMP 仕様では、OpenMP プログラムの実行を制御する環境変数が 4 つ定義されています。これらの環境変数を次の表に示します。詳細については、openmp.org にある OpenMP API Version 3.0 の仕様を参照してください。
表 2–1 OpenMP 環境変数
これ以外にも、OpenMP プログラムの実行に影響を与える多重処理に関する環境変数がありますが、OpenMP 仕様には含まれていません。これらの環境変数を次の表に示します。
表 2–2 並列処理関係の環境変数
環境変数 |
関数 |
|
---|---|---|
PARALLEL |
従来のプログラムとの互換性のため、PARALLEL 環境変数を設定すると、OMP_NUM_THREADS を設定したのと同じ効果が得られます。ただし、PARALLEL と OMP_NUM_THREADS の両方に設定する場合は、同じ値を設定する必要があります。 |
|
SUNW_MP_WARN |
OpenMP の実行時ライブラリで出力される警告メッセージを制御します。TRUE に設定した場合は、実行時ライブラリの警告メッセージが stderr に出力されます。FALSE に設定した場合は、警告メッセージが無効になります。デフォルトは FALSE です。 OpenMP 実行時ライブラリは、不正な入れ子やデッドロックなど、共通の OpenMP 違反を調べることができます。ただし、実行時チェックを使用するとプログラムの実行時にオーバーヘッドが加わります。第 3 章実装によって定義される動作を参照してください。 SUNW_MP_WARN を TRUE に設定している場合、実行時ライブラリは stderr に警告メッセージを出力します。また、警告メッセージを認証するためにプログラムでコールバック関数が登録されている場合も、実行時ライブラリは警告メッセージを出力します。次の関数を呼び出すことにより、プログラムでユーザーコールバック関数を登録できます。
コールバック関数のアドレスは、sunw_mp_register_warn() に引数として渡されます。この関数は、コールバック関数の登録に成功した場合 0 を、失敗した場合 1 を返します。 プログラムでコールバック関数が登録されている場合、libmtsk はエラーメッセージを含むローカライズされた文字列にポインタを渡し、登録済みの関数を呼び出します。メモリーの割り当て先は、コールバック関数から戻ると無効になります。 次に例を示します。 setenv SUNW_MP_WARN TRUE |
|
SUNW_MP_THR_IDLE |
OpenMP プログラムのバリアで待ち状態または新しい並列領域の実行待ち状態のアイドルスレッドの状態を制御します。次のいずれかの値を設定できます。SPIN、SLEEP、SLEEP(times)、SLEEP(timems)、または SLEEP(timemc)。time には時間を整数で指定し、s、ms、および mc には時間の単位 (それぞれ、秒、ミリ秒、マクロ秒) を指定します。 SPIN を指定すると、アイドルスレッドは、バリアで待機中または新しい並列領域の実行待ちの間スピンをします。時間引数なしで SLEEP を指定すると、アイドルスレッドはすぐにスリープ状態になります。時間引数付きで SLEEP を指定すると、スレッドは指定した時間スピンを継続し、そのあとスリープ状態になります。 デフォルトのアイドルスレッド状態は、スリープ状態ですが、ある程度の時間スピンして待機したあとスリープ状態になることがあります。SLEEP、SLEEP(0)、SLEEP(0s)、SLEEP(0ms)、および SLEEP(0mc) はすべて同じです。 SUNW_MP_THR_IDLE および OMP_WAIT_POLICY の両方が設定されている場合、両方の値の間に一貫性が保たれている必要があります。 次に例を示します。
|
|
この環境変数は、Solaris および Linux システムの両方で使用されます。SUNW_MP_PROCBIND 環境変数を使用して OpenMP プログラムのスレッドを実行中のシステムの仮想プロセッサに結合できます。プロセッサに結合することでパフォーマンスを向上することができますが、同じ仮想プロセッサに複数のスレッドが結合されると、パフォーマンスが低下します。詳細は、「2.3 プロセッサ結合」を参照してください。 |
||
スレッドプールの最大数を指定します。プールにあるのは、OpenMP ライブラリが作成した非ユーザースレッドだけです。マスタースレッドやユーザーのプログラムが明示的に作成したスレッドは含まれません。この環境変数をゼロに設定すると、スレッドのプールは空になり、すべての並列領域は 1 つのスレッドによって実行されます。指定しない場合のデフォルトは、1023 です。詳細は、「「4.2 入れ子並列処理の制御」」を参照してください。 SUNW_MP_MAX_POOL_THREADS はプログラム全体で使用される非ユーザーの OpenMP スレッドの最大数を指定し、OMP_THREAD_LIMIT はプログラム全体で使用されるユーザーおよび非ユーザーの OpenMP スレッドの最大数を指定するものである点に留意してください。SUNW_MP_MAX_POOL_THREADS と OMP_THREAD_LIMIT の両方が設定されている場合は、両社の値の間に一貫性が保たれている必要があります。たとえば、OMP_THREAD_LIMIT の値は SUNW_MP_MAX_POOL_THREADS の値よりも 1 だけ大きい数値に設定します。 |
||
有効な入れ子になった並列領域の深さの最大数を指定します。この環境変数で指定した数を超える有効な入れ子を持つ並列領域は、1 つのスレッドによって実行されます。IF 節が False になっている OpenMP 並列領域の場合は、その並列領域は無効であると見なされます。指定しない場合のデフォルトは、4 です。詳細は、「4.2 入れ子並列処理の制御」を参照してください。 SUNW_MP_MAX_NESTED_LEVELS と OMP_MAX_ACTIVE_LEVELS の両方を設定する場合は、両者は同じ値に設定される必要がある点に留意してください。 |
||
STACKSIZE |
各スレッドのスタックサイズを設定します。値はキロバイト単位で指定します。デフォルトのスレッドスタックサイズは、32 ビット SPARC V8 および x86 プラットフォームで 4M バイト、64 ビット SPARC V9 および x86 プラットフォームで 8M バイトです。 次に例を示します。 setenv STACKSIZE 8192 スレッドのスタックサイズを 8M バイトに設定します。 STACKSIZE 環境変数は、接尾辞 B、K、M、または G の付いた数値も受け付けます。これらの接尾辞はそれぞれ、バイト、キロバイト、メガバイト、ギガバイトを表します。デフォルトはキロバイトです。 STACKSIZE と OMP_STACKSIZE の両方を設定する場合は、両者は同じ値に設定される必要がある点に留意してください。 |
|
チャンクのサイズを決定する重み係数を設定します。このチャンクサイズは、GUIDED スケジューリングによってループ中のスレッドに割り当てられます。値には、正の浮動小数点数を指定します。この値は、同一プログラム中で GUIDED スケジューリングが設定されたループすべてに適用されます。指定がない場合のデフォルト値は 2.0 です。 |
プロセッサ結合では、プログラマはプログラムの実行を通じてプログラム内のスレッドを同じプロセッサで実行すべきであることを、オペレーティングシステムに指示します。この機能は Linux 上では使用できません。
static スケジュール指定ととともにプロセッサ結合を使用すると、並列領域またはワークシェアリング領域の前回呼び出し以降、その領域内のスレッドがアクセスするデータがローカルキャッシュに存在する、特定のデータ再利用パターンを持つアプリケーションにメリットがあります。
ハードウェアから見ると、コンピュータシステムは 1 つまたは複数の「物理」プロセッサから構成されています。オペレーティングシステムから見ると、これらの「物理」プロセッサはそれぞれ、プログラム内のスレッドを実行可能な 1 つまたは複数の「仮想」プロセッサにマッピングされます。n 個の仮想プロセッサを使用する場合、n 個のスレッドを同時に実行するようスケジューリングできます。システムによっては、仮想プロセッサは実際のプロセッサやコアなどの場合もあります。
たとえば、UltraSPARC IV の物理プロセッサにはそれぞれ 2 つのコアがあります。Solaris OS からは、それぞれのコアがスレッドの実行をスケジュールできる仮想プロセッサに見えます。一方、UltraSPARC T1 物理プロセッサには 8 つのコアがあり、各コアはスレッドを 4 つ同時に処理できます。Solaris OS から見ると、スレッドの実行をスケジューリング可能な仮想プロセッサは 32 個あります。Solaris オペレーティングシステムでは、psrinfo(1M) コマンドを使用して仮想プロセッサの数を特定できます。Linux システムでは、ファイル /proc/cpuinfo に利用可能なプロセッサの情報が記述されています。
オペレーティングシステムがスレッドをプロセッサに結合すると、スレッドは実質的に「物理」プロセッサではなく、特定の「仮想」プロセッサに結合されます。
OpenMP プログラム内のスレッドを特定の仮想プロセッサに結合するには、SUNW_MP_PROCBIND 環境変数を設定します。SUNW_MP_PROCBIND には、次のいずれかの値を指定できます。
文字列「TRUE」または「FALSE」 (小文字の「true」または「false」も可)。 たとえば、次を見てください。 % setenv SUNW_MP_PROCBIND "false"
非負整数。 次に例を示します。% setenv SUNW_MP_PROCBIND "2"
1 つ以上の空白で区切った 2 つ以上の非負整数のリスト。 次に例を示します。% setenv SUNW_MP_PROCBIND "0 2 4 6"
ハイフン 1 つ ("-") で区切った 2 つの非負整数 n1 と n2。n1 は n2 以下である必要があります。 次に例を示します。% setenv SUNW_MP_PROCBIND "0-6"
前述の非負整数は論理識別子 (ID) を表しています。論理 ID は「仮想」プロセッサ ID とは異なります。その違いを次に示します。
仮想プロセッサ ID
システム内の各仮想プロセッサは一意のプロセッサ ID を持ちます。Solaris OS の psrinfo(1M) コマンドを使用すると、システム内のプロセッサに関するプロセッサ ID などの情報を表示できます。さらに、prtdiag(1M) コマンドを使用すると、システム構成および診断情報を表示できます。
Solaris の最近のリリースでは psrinfo -pv を使用すると、システム内のすべての物理プロセッサ、および各物理プロセッサに関連付けられた仮想プロセッサを一覧表示できます。
仮想プロセッサ ID は、連番になることも、ID 番号が飛ぶこともあります。たとえば、8 個の UltraSPARC IV プロセッサ (16 コア) を持つ Sun Fire 4810 では、仮想プロセッサ ID が 0、1、2、3、8、9、10、11、512、513、514、515、520、521、522、523 のようになります。
論理 ID
前述のとおり、SUNW_MP_PROCBIND に指定された非負整数は論理 ID です。論理 ID は、0 から始まる連続した整数です。システムで利用可能な仮想プロセッサの数が n の場合、論理 ID は、0、1、…、n-1 のように psrinfo(1M) に示された順番になります。次の Korn シェルスクリプトを使用すると、仮想プロセッサ ID から論理 ID へのマッピングを表示できます。
#!/bin/ksh NUMV= `psrinfo | fgrep "on-line" | wc -l ` set -A VID `psrinfo | cut -f1 ` echo "Total number of on-line virtual processors = $NUMV" echo let "I=0" let "J=0" while [[ $I -lt $NUMV ]] do echo "Virtual processor ID ${VID[I]} maps to logical ID ${J}" let "I=I+1" let "J=J+1" done |
1 つの物理プロセッサが複数の仮想プロセッサにマッピングされているシステムでは、同じ物理プロセッサに属す仮想プロセッサにどの論理 ID が対応しているかを知っておくと便利です。次の Korn シェルスクリプトを最近のリリースの Solaris で使用すると、この情報が表示されます。
#!/bin/ksh NUMV= `psrinfo | grep "on-line" | wc -l ` set -A VLIST `psrinfo | cut -f1 ` set -A CHECKLIST `psrinfo | cut -f1 ` let "I=0" while [ $I -lt $NUMV ] do let "COUNT=0" SAMELIST="$I" let "J=I+1" while [ $J -lt $NUMV ] do if [ ${CHECKLIST[J]} -ne -1 ] then if [ `psrinfo -p ${VLIST[I]} ${VLIST[J]} ` = 1 ] then SAMELIST="$SAMELIST $J" let "CHECKLIST[J]=-1" let "COUNT=COUNT+1" fi fi let "J=J+1" done if [ $COUNT -gt 0 ] then echo "The following logical IDs belong to the same physical processor:" echo "$SAMELIST" echo " " fi let "I=I+1" done |
SUNW_MP_PROCBIND に指定された値の解釈
SUNW_MP_PROCBIND に指定された値が TRUE の場合、スレッドは仮想プロセッサに論理 ID が 0 のプロセッサから順にラウンドロビン方式で結合されます。(TRUE を指定した場合は、SUNW_MP_PROCBIND に値 0 を指定したのと同じです。)
SUNW_MP_PROCBIND に指定された値が非負整数の場合、その整数はスレッドの結合先の仮想プロセッサの開始論理 ID を表します。スレッドは、指定された論理 ID を持つプロセッサから順にラウンドロビン方式で仮想プロセッサに結合されます。論理 ID が n-1 のプロセッサに結合した後は、論理 ID が 0 のプロセッサに続きます。
SUNW_MP_PROCBIND に指定された値が非負の整数の 2 項目以上のリストの場合、スレッドはラウンドロビン方式で指定された論理 ID を持つ仮想プロセッサに結合されます。指定された以外の論理 ID を持つプロセッサは使用されません。
SUNW_MP_PROCBIND に指定された値が、マイナス記号 (-) で区切られた 2 つの非負整数の場合、スレッドは最初の論理 ID から 2 番目の論理 ID の範囲の仮想プロセッサに、ラウンドロビン式で結合されます。この範囲に含まれない論理 ID を持つプロセッサは使用されません。
SUNW_MP_PROCBIND に指定された値が前述のどの形式にも当てはまらないか、不正な論理 ID が指定された場合は、エラーメッセージが出力され、プログラムの実行が終了します。
microtasking ライブラリ libmtsk で作成されるスレッドの数は、環境変数、ユーザーのプログラム内の API 呼び出し、および num_threads 節によって異なります。SUNW_MP_PROCBIND は、スレッドの結合先となる仮想プロセッサの論理 ID を指定します。スレッドは、その一連のプロセッサにラウンドロビン式で結合されます。プログラム内で使用しているスレッドの数が、SUNW_MP_PROCBIND で指定された論理 ID の数よりも少ない場合、一部の仮想プロセッサはそのプログラム内で使用されません。SUNW_MP_PROCBIND で指定された論理 ID の数よりもスレッドの数が多い場合、一部の仮想プロセッサには複数のスレッドが結合されます。
OS プロセッサセットとの相互作用
Solaris プラットフォーム上では psrset ユーティリティーを、Linux プラットフォーム上では taskset コマンドを使うと、プロセッサセットを指定できます。SUNW_MP_PROCBIND ではプロセッサセットは考慮されません。プログラマがプロセッサセットを使用する場合、SUNW_MP_PROCBIND の設定と、使用しているプロセッサセットとの整合性の確認は、プログラマの責任で行ってください。この確認を怠ると、Linux システム上では SUNW_MP_PROCBIND の設定がプロセッサセットの設定に上書きされ、Solaris システム上ではエラーメッセージが表示されます。
実行プログラムは、各スレーブスレッド用の個別スタックのほか、プログラムを実行する初期スレッド用のメインメモリースタックを保持します。スタックは、サブプログラムまたは関数参照の呼び出し中、引数および自動変数を保持するために使用される一時的なメモリーアドレス空間です。
デフォルトのメインスタックのサイズは 8M バイトです。f95 にオプション -stackvar を指定して Fortran プログラムをコンパイルすると、自動変数であるかのようにスタック上にローカル変数と配列が割り当てられます。OpenMP プログラムでの -stackvar 指定は、明示的に並列化されたプログラムで必要になります。これは、オプティマイザのループでの呼び出しの並列化機能を向上させるためです (-stackvar フラグについては、『Fortran ユーザーズガイド』を参照)。ただし、スタックに十分なメモリーが割り当てられていない場合は、スタックのオーバーフローが発生する可能性があります。
メインスタックのサイズを表示または設定するには、C シェルの limit コマンド、または ksh、sh の ulimit コマンドを使用します。
OpenMP プログラムの各スレーブスレッドは、それぞれスレッドスタックを持ちます。このスタックは最初の (メイン) スレッドスタックに似ていますが、そのスレッドに固有のものです。スレッドの PRIVATE 配列および変数 (スレッドにローカル) は、スレッドスタックに割り当てられます。デフォルトのサイズは、32 ビット SPARC V8 および x86 プラットフォームで 4M バイト、64 ビット SPARC V9 および x86 プラットフォームで 8M バイトです。ヘルパースレッドスタックのサイズは、OMP_STACKSIZE 環境変数で設定されます。
demo% setenv OMP_STACKSIZE 16384 <-Set thread stack size to 16 Mb (C shell) demo$ OMP_STACKSIZE=16384 <-Same, using Bourne/Korn shell demo$ export OMP_STACKSIZE |
最適なスタックサイズを判定するには、試行とエラーを経る必要があるかもしれません。スタックサイズがスレッドに対して小さすぎて実行できない場合、エラーメッセージが出力されないまま、隣接するスレッドでデータ破壊やセグメントエラーが発生する可能性があります。スタックオーバーフローが発生するかどうか不確かな場合、-xcheck=stkovf コンパイラオプションを指定して Fortran や C、C++ プログラムをコンパイルすると、スタックオーバーフローのセグメント例外を発生させることができます。この場合、データ破壊が発生する前にプログラムの実行が停止します (注: -xcheck=stkovf コンパイラオプションは、SPARC システム上でのみ使用できます)。
Sun Studio スレッドアナライザツールを使用して、OpenMP プログラムのデータ競合やデッドロックをチェックできます。詳細は、スレッドアナライザのマニュアルおよび tha(1) のマニュアルページを参照してください。