インテル® MIC アーキテクチャー向けの高度な最適化 - 手動によるループアンロールの回避
この記事は、インテル® ソフトウェア・サイトに掲載されている「Avoid Manual Loop Unrolling」の日本語参考訳です。
一般に、インテル® コンパイラーは、ループ構造が手動でアンロールされていない場合に効率良くベクトル化されたコードを生成することができます。コンパイラーにアンロールを任せて、”#pragma unroll (n)” を使用してアンロールを制御したほうが良いでしょう。
もしコンパイラーが手動アンロールの “取り消し” 機能を持っていても、ベクトルのアライメント、ループコラプス、ほかのループ最適化との調整などがさらに複雑になります。単純なケースを除いて、ベクトルコードで最大のパフォーマンスを引き出せるように、このリファクタリング (内部構造の整理) はユーザーが行う必要があります。
手動によるループアンロールは、特定のプロセッサーやアーキテクチャー向けにチューニングされていることが多く、将来アプリケーションを移植する場合に適していません。一般に、コードは分かりやすく、簡潔に記述したほうが良いでしょう。そうすることで、コンパイラーがループ構造を最適化できる可能性が高まります。
以下に、ソースコードで手動アンロールが行われている Fortran の例を示します。
m = MOD(N,4) if ( m /= 0 ) THEN do i = 1 , m Dy(i) = Dy(i) + Da*Dx(i) end do if ( N < 4 ) RETURN end if mp1 = m + 1 do i = mp1 , N , 4 Dy(i) = Dy(i) + Da*Dx(i) Dy(i+1) = Dy(i+1) + Da*Dx(i+1) Dy(i+2) = Dy(i+2) + Da*Dx(i+2) Dy(i+3) = Dy(i+3) + Da*Dx(i+3) end do
このコードは、次のようにシンプルにしたほうが良いでしょう。
do i=1,N Dy(i)= = Dy(i) + Da*Dx(i) end do
こうすることで、コンパイラーが処理全体にわたって効率良いベクトルコードを生成できるようになり、さらに分かりやすいコードになります。
以下に、ソースコードで手動アンロールが行われている C++ の例を示します。
double accu1 = 0, accu2 = 0, accu3 = 0, accu4 = 0; double accu5 = 0, accu6 = 0, accu7 = 0, accu8 = 0; for (i = 0; i < NUM; i += 8) { accu1 = src1[i+0]*src2 + accu1; accu2 = src1[i+1]*src2 + accu2; accu3 = src1[i+2]*src2 + accu3; accu4 = src1[i+3]*src2 + accu4; accu5 = src1[i+4]*src2 + accu5; accu6 = src1[i+5]*src2 + accu6; accu7 = src1[i+6]*src2 + accu7; accu8 = src1[i+7]*src2 + accu8; } accu = accu1 + accu2 + accu3 + accu4 + accu5 + accu6 + accu7 + accu8;
このコードは、次のようにシンプルにしたほうが良いでしょう。
double accu = 0; for (i = 0; i < NUM; i++ ) { accu = src1[i]*src2 + accu; }
次のステップ
この記事は、「Programming and Compiling for Intel® Many Integrated Core Architecture」(英語) の一部「Avoid Manual Loop Unrolling」の翻訳です。インテル® Xeon Phi™ コプロセッサー上にアプリケーションを移植し、チューニングを行うには、本ガイドの各リンクのトピックを参照してください。アプリケーションのパフォーマンスを最大限に引き出すために必要なステップを紹介しています。
コンパイラーの最適化に関する詳細は、最適化に関する注意事項を参照してください。