OpenMP* 4.0 における SIMD およびアフィニティー機能の導入
法務上の注意書きと最適化に関する注意事項 本資料に掲載されている情報は インテル製品の概要説明を目的としたものです 本資料は 明示されているか否かにかかわらず また禁反言によるとよらずにかかわらず いかなる知的財産権のライセンスを許諾するものではありません 製品に付属の売買契約書 Intel's Terms and Conditions of Sale に規定されている場合を除き インテルはいかなる責任を負うものではなく またインテル製品の販売や使用に関する明示または黙示の保証 ( 特定目的への適合性 商品適格性 あらゆる特許権 著作権 その他知的財産権の非侵害性への保証を含む ) に関してもいかなる責任も負いません 性能に関するテストに使用されるソフトウェアとワークロードは 性能がインテル マイクロプロセッサー用に最適化されていることがあります SYSmark* や MobileMark* などの性能テストは 特定のコンピューター システム コンポーネント ソフトウェア 操作 機能に基づいて行ったものです 結果はこれらの要因によって異なります 製品の購入を検討される場合は 他の製品と組み合わせた場合の本製品の性能など ほかの情報や性能テストも参考にして パフォーマンスを総合的に評価することをお勧めします 2013 Intel Corporation. 無断での引用 転載を禁じます Intel インテル Intel ロゴ Intel Core Intel Xeon Phi Xeon は アメリカ合衆国および / またはその他の国における Intel Corporation の商標です * その他の社名 製品名などは 一般に各社の表示 商標または登録商標です 最適化に関する注意事項 インテル コンパイラーは 互換マイクロプロセッサー向けには インテル製マイクロプロセッサー向けと同等レベルの最適化が行われない可能性があります これには インテル ストリーミング SIMD 拡張命令 2 ( インテル SSE2) インテル ストリーミング SIMD 拡張命令 3 ( インテル SSE3) ストリーミング SIMD 拡張命令 3 補足命令 (SSSE3) 命令セットに関連する最適化およびその他の最適化が含まれます インテルでは インテル製ではないマイクロプロセッサーに対して 最適化の提供 機能 効果を保証していません 本製品のマイクロプロセッサー固有の最適化は インテル製マイクロプロセッサーでの使用を目的としています インテル マイクロアーキテクチャーに非固有の特定の最適化は インテル製マイクロプロセッサー向けに予約されています この注意事項で対象としている特定の命令セットに関する詳細は 該当製品のユーザーズガイドまたはリファレンス ガイドを参照してください 改訂 #20110804
OpenMP* API 業界標準の OpenMP 4.0 共有メモリー型並列プログラミング向けの C/C++ と Fortran 向け API 宣言子 (C/C++ ではプラグマ ) ベース ベンダーやプラットフォームに渡る移植性 様々なタイプの並列性をサポート
ハードウェアの進化 ( インテル ) イメージは実際のダイサイズを反映することを意図していません 64 ビットインテル Xeon プロセッサー インテル Xeon プロセッサー 5100 シリーズ インテル Xeon プロセッサー 5500 シリーズ インテル Xeon プロセッサー 5600 シリーズ インテル Xeon プロセッサー E5-200v2 シリーズ インテル Xeon Phi コプロセッサー 7120P 周波数 3.6GHz 3.0GHz 3.2GHz 3.3GHz 2.7GHz 1.238MHz コア 1 2 4 6 12 61 スレッド 2 2 8 12 24 244 SIMD 幅 128 (2 クロック ) 128 (1 クロック ) 128 (1 クロック ) 128 (1 クロック ) 256 (1 クロック ) 512 (1 クロック )
OpenMP 4.0 における並列性レベル クラスター コンピューターのグループ高速相互接続を介して通信 デバイス向けの OpenMP 4.0 コプロセッサー / アクセラレーター ノード ソケット コア ハイパースレッド スーパースカラー 特殊な計算デバイス特別な相互接続を介してローカルノードに接続 OpenMP 4.0 アフィニテプロセッサーのグループ OpenMP 4.0 アフィニティーィー共有メモリーを介して通信 コアのグループ共有キャッシュを介して通信 機能ユニットのグループレジスターを介して通信 機能ユニットを共有するスレッド コンテキストのグループ 機能ユニットを共有する命令グループ パイプライン ベクトル 機能ユニットを共有する命令のシーケンス 複数の機能ユニットを使用する単一命令 OpenMP 4.0 SIMD
OpenMP 4.0 SIMD
OpenMP 4.0 以前 プログラムは自動ベクトル化に依存...... もしくは ベクトル固有拡張を使用 プログラム モデル ( 例えば インテル Cilk Plus) コンパイラー プラグマ ( 例えば #pragma vector) 低レベルの構文 ( 例えば mm_add_pd()) #pragma omp parallel for #pragma vector always #pragma ivdep for (int i = 0; i < N; i++) { a[i] = b[i] +...; } コンパイラーが " 正しい " ことをするのを信頼する必要がある
OpenMP* SIMD 構文 ループの入れ子をベクトル化 ループを SIMD ベクトル レジスターに収まるようにチャンクに分割 ループ本体を並列化しない 構文 (C/C++) #pragma omp simd [ 節 [[,] 節 ], ] for ループ 構文 (Fortran)!$omp simd [ 節 [[,] 節 ], ] do ループ
例 void sprod(float *a, float *b, int n) { 例 float sum = 0.0f; #pragma omp simd reduction(+:sum) for (int k=0; k<n; k++) sum += a[k] * b[k]; return sum; } ベクトル化
データ共有節 private(var-list): var-list で指定された 初期化されないベクトル変数 x: 42???? firstprivate(var-list): var-list で指定された 初期化されたベクトル変数 x: 42 42 42 42 42 reduction(op:var-list): var-list で指定された変数をプライベートに作成し 構文の最後で op で指定されるリダクション操作を行う 12 5 8 17 x: 42
SIMD プラグマ / 宣言子の節 safelen(length) 依存性を損なうことなく同時に実行できる最大反復数 実際には 最大ベクトル長 linear (list[:linear-step]) 変数の値はループ反復数に関連する x i = x orig + i * linear-step aligned (list[:alignment]) list 変数のアライメントを指定する デフォルトのアライメントはアーキテクチャーに依存する collapse (n)
SIMD ワークシェア構文 入れ子になったループの並列化とベクトル化を行う スレッドチーム間でループ反復空間を分割する SIMD ベクトル レジスターに収まるようにループチャンクを分割 構文 (C/C++) #pragma omp for simd [ 節 [[,] 節 ], ] for ループ 構文 (Fortran)!$omp do simd [ 節 [[,] 節 ], ] do ループ
例 void sprod(float *a, float *b, int n) { 例 float sum = 0.0f; #pragma omp for simd reduction(+:sum) for (int k=0; k<n; k++) sum += a[k] * b[k]; return sum; } 並列化 スレッド 0 スレッド 1 スレッド 2 ベクトル化
SIMD 関数のベクトル化 float min(float a, float b) { SIMD return 関数のベクトル化 a < b? a : b; } float distsq(float x, float y) { return (x - y) * (x - y); } void example() { #pragma omp parallel for simd for (i=0; i<n; i++) { d[i] = min(distsq(a[i], b[i]), c[i]); } }
SIMD 関数のベクトル化 SIMD 並列ループから呼び出すように 1 つ以上の関数を宣言する 構文 (C/C++): #pragma omp declare simd [ 節 [[,] 節 ], ] [#pragma omp declare simd [ 節 [[,] 節 ], ]] [ ] 関数の定義または宣言 構文 (Fortran):!$omp declare simd (proc-name-list)
SIMD 関数のベクトル化 #pragma omp declare simd float min(float a, float b) { return a < b? a : b; } #pragma omp declare simd float distsq(float x, float y) { return (x - y) * (x - y); } vec8 min_v(vec8 a, vec8 b) { return a < b? a : b; } vec8 distsq_v(vec8 x, vec8 y) { return (x - y) * (x - y); } void example() { #pragma omp parallel for simd for (i=0; i<n; i++) { d[i] = min(distsq(a[i], b[i]), c[i]); } } vd = min_v(distsq_v(va, vb, vc))
SIMD 関数のベクトル化 simdlen (length) 指定されたベクトル長 (length) をサポートする関数を生成 uniform (argument-list) 引数 (argumrnt-list) は 指定されたループ反復間の定数値 inbranch 関数は常に if 文内部から呼び出される notinbranch 関数は if 文内部から呼び出されない linear (argument-list[:linear-step]) aligned (argument-list[:alignment]) reduction(operator:list) 前と同じ
SIMD 構文とパフォーマンス 5.00x 4.50x 4.00x 3.66x 4.34x ICC auto-vec ICC SIMD directive 相対的スピードアップ ( 高値が良い ) 3.50x 3.00x 2.50x 2.00x 1.50x 2.04x 2.13x 1.47x 2.40x 1.00x 0.50x 0.00x Mandelbrot Volume Rendering BlackScholes Fast Walsh Perlin Noise SGpp M. Klemm, A. Duran, X. Tian, H. Saito, D. Caballero, and X. Martorell. 近年のマルチコア SIMD アーキテクチャー向けに OprnMP ベクトル構文を拡張 2012 年 6 月 イタリア ローマで開催されたインテル ワークショップの資料ページ 59-72 より LNCS 7312
デバイス向けの OpenMP 4.0
デバイスモデル OpenMP 4.0 によるアクセラレーター / コプロセッサーのサポート デバイスモデル : 1 つのホスト 同種の複数のアクセラレーター / コプロセッサー コプロセッサー ホスト
デバイス向けの OpenMP 4.0 - 構文 ホストからデバイスへ制御 [ とデータ ] を転送 構文 (C/C++) #pragma omp target [data] [ 節 [[,] 節 ], ] 構造化ブロック 構文 (Fortran)!$omp target [data] [ 節 [[,] 節 ], ] 構造化ブロック 節 device(scalar-integer-expression) map(alloc to from tofrom:list) if(scalar-expr)
実行モデル target 構文は制御フローをターゲットデバイスへ転送 制御の転送はシーケンシャルで同期される 転送節はデータフローの方向を制御 配列表記は 配列の長さを表現するために使用される target data 構文はデバイスのデータ環境にスコープを生成 制御の転送は行われない 転送節はデータフローの方向を制御 デバイスのデータ環境は ターゲットのデータ領域が有効な間適用される target update は ターゲットのデータ領域からデータの転送を要求する際に使用する
実行モデル データ環境は字句でスコープされる データ環境は波括弧を閉じた時点で破棄される 割り当てられたバッファー / データは自動的に解放される pa ホスト 2 to( ) デバイス 1 alloc( ) 4 from( ) #pragma omp target map(alloc:...) map(to:...) map(from:...) {...} 3
例 #pragma omp target data device(0) map(alloc:tmp[:n]) map(to:input[:n)) map(from:res) { #pragma omp target device(0) #pragma omp parallel for for (i=0; i<n; i++) tmp[i] = some_computation(input[i], i); update_input_array_on_the_host(input); #pragma omp target update device(0) to(input[:n]) #pragma omp target device(0) #pragma omp parallel for reduction(+:res) for (i=0; i<n; i++) res += final_computation(input[i], tmp[i], i) } ホストターゲットホストターゲットホスト
teams 構文 複数レベルの並列デバイスをサポート 構文 (C/C++): #pragma omp teams [ 節 [[,] 節 ], ] 構造化ブロック 構文 (Fortran):!$omp teams [ 節 [[,] 節 ], ] 構造化ブロック 節 num_teams(integer-expression) num_threads(integer-expression) default(shared none) private(list), firstprivate(list) shared(list), reduction(operator :list)
コプロセッサーへ SAXPY をオフロードする int main(int argc, const char* argv[]) { float *x = (float*) malloc(n * sizeof(float)); SAXPY float *y = (float*) malloc(n * sizeof(float)); // Define scalars n, a, b & initialize x, y #pragma omp target data map(to:x[0:n]) { #pragma omp target map(tofrom:y) #pragma omp 全てが同じことを行う teams num_teams(num_blocks) num_threads(nthreads) for (int i = 0; i < n; i += num_blocks){ for (int j = i; j < i + num_blocks; j++) { y[j] = a*x[j] + y[j]; } } } free(x); free(y); return 0; }
コプロセッサーへ SAXPY をオフロードする int main(int argc, const char* argv[]) { float *x = (float*) malloc(n * sizeof(float)); float *y = (float*) malloc(n * sizeof(float)); // Define scalars n, a, b & initialize x, y SAXPY コプロセッサー / アクセラレーター #pragma omp target data map(to:x[0:n]) { #pragma omp target map(tofrom:y) #pragma omp teams num_teams(num_blocks) num_threads(bsize) 全てが同じことを行う #pragma omp distribute for (int i = 0; i < n; i += num_blocks){ ワークシェア (barrier なし ) #pragma omp parallel for for (int j = i; j < i + num_blocks; j++) { y[j] = a*x[j] + y[j]; } } } free(x); free(y); return 0; } ワークシェア (barrier あり )
コプロセッサーへ SAXPY をオフロードする int main(int SAXPY 複合構文 argc, const char* argv[]) { float *x = (float*) malloc(n * sizeof(float)); float *y = (float*) malloc(n * sizeof(float)); // Define scalars n, a, b & initialize x, y #pragma omp target map(to:x[0:n]) map(tofrom:y) { #pragma omp teams distribute parallel for num_teams(num_blocks) num_threads(bsize) for (int i = 0; i < n; ++i){ y[i] = a*x[i] + y[i]; } } free(x); free(y); return 0; }
OpenMP 4.0 アフィニティー
NUMA はここに属する... ( ほとんど ) すべてのマルチソケット計算サーバーは NUMA システムである 異なるメモリー位置へのアクセス レイテンシーは一定ではない 異なるメモリー位置の帯域幅が異なる可能性がある 例 : インテル Xeon E5-2600v2 シリーズ プロセッサー Xeon E5-2600v2 Xeon E5-2600v2
スレッド アフィニティー - なぜ重要なのか? GB/ 秒 [ 高値がより良い ] 100.00 90.00 80.00 70.00 60.00 50.00 40.00 30.00 20.00 10.00 0.00 STREAM Triad, インテル Xeon E5-2697v2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 スレッド / コア数 compact, par scatter, par compact, seq scatter, seq
スレッド アフィニティー - プロセッサーのバインド バインドの方針は マシンとアプリケーションに依存する スレッドを離して配置 例 異なるパッケージ ( おそらく ) メモリー帯域幅を向上させる ( おそらく ) 統合されたキャッシュサイズを改善 ( おそらく ) 同期構文のパフォーマンスを低下させる スレッドを近づけて配置 例 キャッシュを共有する可能性がある 2 つのコアに隣接 ( おそらく ) 同期構文のパフォーマンスを向上させる ( おそらく ) 利用可能なメモリー帯域幅とキャッシュサイズ ( スレッドごとの ) を低下させる
OpenMP* 4.0 におけるスレッド アフィニティー OpenMP 4.0 は 配置のコンセプトを導入... 1 つ以上のプロセッサー上で動作する一連のスレッド ユーザーによって定義される 事前定義された配置 : スレッド コア ソケット ハイパースレッドごとに 1 つの位置物理コアごとに 1 つの位置プロセッサー パッケージごとに 1 つの位置... そしてアフィニティーのポリシーは... spread close master OpenMP スレッドをすべての位置に広く配置 OpenMP スレッドをマスター スレッドの近辺にパック OpenMP スレッドをマスター スレッドを併置... そしてこれらの設定を制御する 環境変数 OMP_PLACES と OMP_PROC_BIND 並列領域向けに proc_bind 節
スレッド アフィニティーの例 例 ( インテル Xeon Phi コプロセッサー ): 外部領域を分配し 内部領域を近く保つ OMP_PLACES=cores(8) #pragma omp parallel proc_bind(spread) #pragma omp parallel proc_bind(close) p0 p1 p2 p3 p4 p5 p6 p7 p0 p1 p2 p3 p4 p5 p6 p7 p0 p1 p2 p3 p4 p5 p6 p7
まとめ OpenMP 4.0 は OpenMP における大きな飛躍 新しい種類の並列性を導入 コプロセッサー デバイスによる異種システム構成をサポート インテル Composer XE 2013 SP1 におけるサポート SIMD 構文 (combined 構文を除く ) デバイス向けの OpenMP (combined 構文を除く ) OpenMP アフィニティー