インテル® コンパイラーによる AVX 最適化入門: 第4回 AVX への最適化について(その3)

同カテゴリーの次の記事

OpenMP*、インテル® TBB、インテル® Cilk™ Plus におけるマスタースレッドとワーカースレッドの浮動小数点設定の違い

第1回 第2回 第3回 最終回

インテル® コンパイラーによる AVX 最適化入門の最終第4回は、既存のプログラムを AVX 向けに自動ベクトル化し直すという、開発現場で起こりうるシナリオでまとめます。

ステップ 1:再コンパイルして現時点での性能を見る

仮に既存のプログラムが第 1 世代インテル® Core™ i7 プロセッサー・ファミリー向けに O3 で最適化されていたとしますと、コンパイラーの呼び出しコマンドの例は以下のようになります。

  • icl /O3 /QxSSE4.2 source1.c source2.c … (Windows の場合)
  • icc -O3 -xSSE4.2 source1.c source2.c … (Linux / Mac OS X の場合)

ここで、本ミニ連載第1回で述べたように AVX 向けに再コンパイルしますと、コンパイラーの呼び出しコマンドの例は以下のようになります。

  • icl /O3 /QxAVX source1.c source2.c … (Windows の場合)
  • icc -O3 -xAVX source1.c source2.c … (Linux / Mac OS X の場合)

それぞれに対して自動ベクトル化を抑制するオプション (/Qvec-, -no-vec) を追加するとコンパイラーの呼び出しコマンドの例は以下のようになります。

  • icl /O3 /QxSSE4.2 /Qvec- source1.c source2.c … (Windows の場合)
  • icl /O3 /QxAVX /Qvec- source1.c source2.c … (Windows の場合)
  • icc -O3 –xSSE4.2 –no-vec source1.c source2.c … (Linux / Mac OS X の場合)
  • icc -O3 -xAVX –no-vec source1.c source2.c … (Linux / Mac OS X の場合)

以下の 4 通りの実行コードを生成することができましたので、これらを第 2 世代インテル® Core™ i7 プロセッサーのシステムで実行し、それぞれの相対的性能をグラフに表現します。

  • SSE4.2 + 自動ベクトル化なし
  • AVX + 自動ベクトル化なし
  • SSE4.2 + 自動ベクトル化あり
  • AVX + 自動ベクトル化あり

理想的には図 1 のようなグラフになっていて欲しいわけですが、読者の方の実際のプログラムではいかがでしょうか?

緑色の矢印は自動ベクトル化以外の部分の AVX 最適化の効果を表し、水色の部分は自動ベクトル化の効果を表しています。(浮動小数点演算の)ベクトル長が2倍になったわけですので2倍の自動ベクトル化効果(水色の矢印の長さ)を狙いたいところですが、現実はそれほど簡単ではありませんのであくまでも「目安」としてとらえて下さい。

本ミニ連載では水色の部分の伸び率を大きくする(2倍に近づける)にはどのような手法が有効であるか述べてきましたので、以下では水色の部分の伸び率が小さいという仮定のもとで解説を進めます。

「水色の矢印の長さがはじめから極端に短い」場合には、次の「ホットスポットを特定する」の後にコンパイラー最適化入門を参照して長さを伸ばすところからはじめましょう。

図 1 相対的実行時間と最適化の効果の一例

ステップ 2:ホットスポットを特定する

多くのプログラムにはホットスポットと呼ばれる、頻繁に実行されて多くの実行時間を費やし、プログラム全体のコード量に占める割合の低い特定部位があります。限られた時間を有効に使って最大限の効果を挙げるコツは、このホットスポットに重点をおいて作業することです。

図 2 に簡単な例を示します。仮にプログラムの実行時間の8割をホットスポット(青い部分)が占めたとします。ホットスポットに手をつけずにそれ以外の部分(すなわちプログラムの大部分)を倍速化した例では実行時間が 1 割減少、ホットスポット以外には手をつけずにホットスポット(特定の小さい部分)だけ 1.3 倍に高速化した例では実行時間が 2 割減少することになりので、こちらのほうが費用対効果が優れていると考えられます。

図 2 ホットスポットの高速化と全体の実行時間に与える影響

ホットスポットを特定するためのツールの紹介や使い方などは本稿の守備範囲外ですので、パフォーマンス最適化特集を参照下さい。以下では一般的なプロファイラーという呼称を使います。

ステップ 3:ホットスポットはベクトル化されているか?

ホットスポットの特定で得られたソースコード上の場所とコンパイラーのベクトル化レポートを比較して、ホットスポットがベクトル化されているか調べます。関数やサブルーチンのインライン展開が行われると、実行ファイルのデバッグ情報やベクトル化レポートに表示されるファイル名と行番号が呼び出し側のものになってしまい難易度が高くなってしまうのが難点です。このような場合、インライン展開を無効にして検証するとよいでしょう。

ホットスポットが AVX でベクトル化されていない場合、SSE4.2 の場合にベクトル化されているかどうか確認します。SSE4.2 でもベクトル化されていない場合にはコンパイラー最適化入門を参照してベクトル化を試みてください。SSE4.2 でベクトル化されて AVX ではベクトル化されないというケースは稀なはずですが、もしありましたらインテル社のプレミアサポートまでバグリポートをお送りください。

ステップ 4:ホットスポットのベクトル化されたコードは実行されているか?

プロファイラーの中にはアセンブリー言語レベルで命令の実行頻度を表示してくれるものがあります。その様なツールを用いて「同じループ」の SSE4.2 実行プロファイルと AVX 実行プロファイルを比較した時、AVX の実行プロファイルでスカラー演算命令の実行頻度が高くなっている(またその結果としてベクトル命令の実行頻度が下がる)ことがあります。

本ミニ連載第1回の末尾に掲載した関数 foo() を例にとりますと、以下の表に示した「ループのベクトル実行部」の直前と直後の「ループのスカラー実行部」に相当します。本来ならば実行頻度が低いと思われるその部分が性能に大きな影響を及ぼす理由は「ループの反復回数が小さい」です。主な対処法は以下の通りです。

  • 可能ならば「ループの反復回数を増やす」最適化をする。
  • 本ミニ連載第2回で紹介した配列データ配置の最適化をする。
  • 配列データ配置の最適化ができない場合には vector unaligned プラグマでスカラー実行部(その1)の生成を抑制する。
  • ループの反復回数が変数の場合には loop count プラグマの利用をお勧め(本ミニ連載第3回)。
  • nounroll プラグマが有効である場面もある。
..B1.8:vmovss    (%rdi,%rcx,4), %xmm0  vaddss    (%rsi,%rcx,4), %xmm0, %xmm1 vmovss    %xmm1, (%rdi,%rcx,4) incq      %rcx cmpq      %r8, %rcx jb        ..B1.8 ループのスカラー実行部(その1)
..B1.11:vmovups   (%rsi,%r8,4), %xmm0  vmovups   32(%rsi,%r8,4), %xmm3 vinsertf128 $1, 16(%rsi,%r8,4), %ymm0, %ymm1 vinsertf128 $1, 48(%rsi,%r8,4), %ymm3, %ymm4 vaddps    (%rdi,%r8,4), %ymm1, %ymm2 vaddps    32(%rdi,%r8,4), %ymm4, %ymm5 vmovups   %ymm2, (%rdi,%r8,4) vmovups   %ymm5, 32(%rdi,%r8,4) addq      $16, %r8 cmpq      %rdx, %r8 jb        ..B1.11 ループのベクトル実行部
..B1.13:cmpq      %rax, %rdx  jae       ..B1.17 ..B1.15: vmovss    (%rdi,%rdx,4), %xmm0 vaddss    (%rsi,%rdx,4), %xmm0, %xmm1 vmovss    %xmm1, (%rdi,%rdx,4) incq      %rdx cmpq      %rax, %rdx jb        ..B1.15 ループのスカラー実行部(その2)
ステップ 5:ホットスポットの実行命令数は?

SSE4.2 の実行プロファイルと AVX の実行プロファイルとでベクトルループの一反復あたりの実行命令数を比較します。AVX のベクトルループ一反復あたりの仕事量が SSE4.2 の 2 倍だと仮定すると、期待される状況は以下に示す通りです。

  • 浮動小数点 SIMD 演算命令 (addps, vaddps など) 実行数は同じ(一命令あたりの仕事量が倍)
  • 整数 SIMD 演算命令 (paddd, vpaddd など) 実行数は 2 倍
  • SIMD 以外の整数命令実行数はほぼ同じ
  • SIMD データの並べ替え命令(vinsertf128, vextractf128, shufps, vshufps など)実行数はほぼ同じが望ましい
  • 「YMM のロードおよびストア命令実行数」x2 + 「XMM のロードおよびストア命令実行数」は2倍
ステップ 6:ホットスポットのメモリーアクセス時間は?

SSE4.2 の実行プロファイルと AVX の実行プロファイルとでは理論的にはキャッシュのヒット率の差はほとんどないはずなのですが、まずはそれを確かめてください。ヒット率が(誤差の範囲内で)ほぼ同じであれば、ロードおよびストア命令の実行にかかった時間を比較します。もし AVX のロードおよびストア命令の実行にかかる時間のほうが大幅に長いようでしたら配列データの配置が最適化されていない可能性が高いです。本ミニ連載第2回を参照下さい。

第4回(最終回)のまとめ

ミニ連載の最終回となる今回は、SSE4.2 向けに自動ベクトル化されているコードを AVX へ最適化するという作業の流れの主要部分を解説しました。実際にはプログラムごとに違うシナリオで作業が先に進んでいくわけですが、今回は入門編ということですので、この先は別の機会に譲ります。お付き合いいただき、ありがとうございました。

著者紹介

齋藤 秀樹氏。インテル コーポレーション ソフトウェア開発製品部門 シニア・スタッフ・エンジニア。2000 年の入社以来インテル® コンパイラーの性能評価および並列化の実装に従事するとともに、SPEC High Performance Group にて SPEC OMP、SPEC HPC2002、SPEC MPI2007 などのベンチマーク開発にも携わる。2007 年よりベクトル化の開発主任を担当し現在に至る。並列計算システムの分野で長年の経験と実績。多くの米国特許、論文および国際学会発表等の共著を持つ。

関連記事