SQUIDで利用可能なintelコンパイラでは、「最適化」と「ベクトル化」の2種類の方法でプログラムを効率化することができます。基本的には、コンパイルオプションの指定のみで、ある程度の効果を得ることが出来ますので、まずこちらの"おすすめコンパイラオプション"をお試しください。自身でプログラム中に指示行を追加することで、さらに効率化を図ることが出来ます。

 
最適化
実行時間の短縮、コードサイズの削減を目的とした処理の効率化を行います。
※弊害として、計算結果が変わることがあります。

 
ベクトル化
ループ中で繰り返される規則的に並んだ配列データ(ベクトルデータと呼びます)の演算に対して、コンパイラがベクトル命令を適用します。

 

基本的な使用例

    $ module load BaseCPU/2021
    $ ifort -O0 -qopt-report-phase=all -qopt-report=2 source_file

    # :最適化レベル
    # :コンパイラメッセージ

     

    最適化レベル(-Oオプション)

      -O0 すべての最適化・ベクトル化を無効にする
      -O1 -O2のうち、コードサイズを増大させる最適化を無効にする。ベクトル化を無効にする
      -O2 ベクトル化の有効化、最適化(組み込み関数のインライン展開、不変な変数・関数の削除、ループアンロールなど)の実施
      -O3 プリフェッチ、ループ変数、パディングなどを行う(-O2よりも実行速度が遅くなる可能性あり)
      コンパイル速度も遅くなります

       

    最適化レポート出力(-qopt-reportオプション)

      コンパイラが行った最適化、ベクトル化の内容やそれらを阻害している要因などのレポートを出力します。
      レポートはソースファイル名.optrptというファイル名で出力されます。

       

    レポート出力フェーズ指定(-qopt-report-phase)

      レポートで出力する内容を指定します。
      何も指定しなければ全てのレポートを出力します。

      -qopt-report-phase=all 全てのフェーズをレポート。(デフォルト)
      -qopt-report-phase=vec ベクトル化に関するフェーズをレポート。
      -qopt-report-phase=loop ループの最適化に関するフェ-ズをレポート。
      -qopt-report-phase=par 自動並列化に関するフェーズをレポート。
        ※この他にもいくつかのフェーズが存在します。
        ※カンマで区切ることで複数指定することが可能です。(例:-qopt-report-phase=par,vec)

       

    レポート出力レベル指定(-qopt-report)

      レポートで出力するレベル(どの程度詳細な内容にするか)を指定します。
      -qopt-reportで指定できるレベルの上限はフェーズによって異なります。
      たとえば、vecフェーズの場合0~5、loopフェーズの場合0~2となります。
      上限を超えたレベルを指定した場合、自動的に各フェーズの上限レベルに設定されます。
      以下は-qopt-report-phase=vecの場合の例です。

      -qopt-report=0 レポートを出力しません。
      -qopt-report=1 ベクトル化されたループを出力。
      -qopt-report=2 レベル1の内容+ベクトル化されなかったループとその簡単な理由を出力。
      -qopt-report=3 レベル2の内容+ベクトル化機能によるループのサマリー情報を出力。(デフォルト)
      -qopt-report=4 レベル3の内容+ベクトル化されたループとされなかったループの詳細な情報を出力。
      -qopt-report=5 レベル4の内容+判明した依存関係または想定される依存関係の詳細な情報を出力。

      最適化レポート出力の詳細については以下のページをご覧ください。
      -qopt-reportマニュアル
       

おすすめコンパイラオプション(すべてFortran言語コンパイラを基準に書いています)

     

    プログラムを初めて実行する方へ

      規定値レベルの最適化を行います。

      $ ifort -O2 source_file

       

    正常終了するプログラムを高速化したい方へ

      プログラムが高速化されます。また演算結果が変わる可能性がありますので、-O2オプション指定時と結果が変わらないか確認してください。場合によっては遅くなることもあります。

      $ ifort -O3 source_file

     

    汎用CPUノード群向けの高速化を行いたい方へ

    演算結果が変わらないことをご確認の上、-O2を-O3に変更すると、更に高速化される可能性があります。
     

      AVX-512命令を使用し、zmmレジスタを必要最小限に使用する

      $ ifort -O2 -xCORE-AVX512 -qopt-zmm-usage=low source_file

       

      AVX-512命令を使用し、zmmレジスタを制限無しに使用する

      $ ifort -O2 -xCORE-AVX512 -qopt-zmm-usage=high source_file

       

      AVX-512命令を使用せずAVX2命令を使用し、Icelakeプロセッサ向けに最適化する

      $ ifort -O2 -xCORE-AVX2 -mtune=icelake-client source_file

       

    Xeon Phiノード群向けの高速化を行いたい方へ

    演算結果が変わらないことをご確認の上、-O2を-O3に変更すると、更に高速化される可能性があります。
     

      AVX-512命令を使用する

      $ ifort -O2 -xMIC-AVX512 source_file

       

    デバッグしたい方へ

      $ ifort -g -traceback source_file
      #(デバッグ情報、トレースバック情報が出力されます)

      $ ifort -check uninit -check bounds source_file
      #(実行時に初期化漏れ、配列外参照のチェック、実行時間が遅くなります。)

     

指示行の使用について

    コンパイラに対する最適化・ベクトル化の「ヒント」や、強制的な最適化・ベクトル化の「指示」をプログラムに埋め込むことができます。指示行は、本来最適化・ベクトル化できない依存関係がある箇所に対しても働くため、結果が変わってしまうことがあります。あくまでユーザの責任で追加する必要があります。また、FortranとCで、書き方がやや異なります。
     

    指示行一覧

    !DEC$ IVDEP  (Fortran)
    #pragma ivdep (C)

    依存関係がないというヒントをコンパイラに与えます。
    ベクトル化の効果が無いと判断した場合はベクトル化は行いません。

     

    !DEC$ vector always  (Fortran)
    #pragma vector always (C)

    効率性ヒューリスティックを無視します。

     

    !DEC$ VECTOR NONTEMPORAL  (Fortran)
    #pragma vector nontemporal (C)

    ストリーミング・ストアを使用するようにヒントを与えます。

     

    !DEC$ VECTOR [UN]ALIGNED  (Fortran)
    #pragma vector [un]aligned (C)

    アライメントされている[されていない] ことを表明します。

     

    !DEC$ NOVECTOR  (Fortran)
    #pragma novector (C)

    対象ループのベクトル化を無効にします。

     

    !DEC$ DISTRIBUTE POINT  (Fortran)
    #pragma distribute point (C)

    この位置でループを分割するようにヒントを与えます。

     

    !DEC$ LOOP COUNT ()  (Fortran)
    #pragma loop count () (C)

    予測される反復数のヒントを与えます。

     

    !DEC$ SIMD  (Fortran)
    #pragma simd (C)

    ベクトル化を強制します。

     
     

    使用例

    プログラム作成者にはベクトル化できることが分かっていても、コンパイラでは判断できない場合があります。

     

    例えば、次のプログラムですが、

    コンパイラは、不明な変数Xにより「依存関係の可能性」を想定し、DO文のベクトル化が妨げられます。
    下記のように、SIMD指示行を挿入し、コンパイラにヒントを与えることで、ベクトル化を行うことが可能です。

     

    コンパイル時に-qopt-report-phase=vec、-qopt-report=5を指定することで、ベクトル化を阻害する原因となるデータを含んだ、
    すべてのベクトル化レポートを出力することが可能です。
    基本的には、このレポートを元に指示行を挿入していくことをお勧めします。

     

    【TIPS】ベクトル化を阻害する要因
    ベクトル化を阻害する要因には以下があります。

      ・データの依存関係
      ・関数、サブルーチンの呼び出し
      ・構造体へのアクセス
      ・条件分岐
      ・ループの終了条件が不明
      ・ポインタアクセス

     

    さらに知りたい場合は、下記の資料をご覧ください。
    閲覧するためには、当センターの利用者番号(アカウント)が必要です。
     

    intel Fortranコンパイラマニュアル

    intel C++コンパイラマニュアル

    【TIPS】ループアンロール
    高速化技法の1つ。ループ命令を展開することで、並列実行可能なコードを増やし、高速化を図ります。ただし、ループを展開する分だけコード量が増加します。

    do j = 1, n
    do k = 1, n
    do i = 1, n
    a(i,j) = a(i,j) + b(i,k) * (k,j)
    enddo
    enddo
    enddo

    do j = 1, n
    do k = 1, n, k
    do i = 1, n
    a(i,j) = a(i,j) + b(i,k) * (k,j)
    & + b(i,k+1) * (k+1,j)
    & + b(i,k+2) * (k+2,j)
    & + b(i,k+3) * (k+3,j)
    enddo
    enddo
    enddo

    配列aに対してのロード・ストア命令が4分の1になり、メモリアクセスが減少するため、高速化が期待できます。

    【TIPS】ロード・ストア命令
    プログラムは、メモリからレジスタという記憶装置へデータを読み込み計算を行います。
     
    ロード命令:メモリからレジスタへデータを読み込むこと
    ストア命令:レジスタ内のデータをメモリへ書き込むこと
     
    を指しています。
     
    上記の例では、kのループ数が減少することで、配列aにアクセスする回数が減るため、ロード・ストア命令の回数が減少します。