インテル® コンパイラーのプラグマ/ディレクティブ
この記事は、インテル® ソフトウェア・サイトに掲載されている「Getting Started with Intel® Composer XE 2013, Compiler Pragmas and Directives」の日本語参考訳です。
はじめに
コンパイラー・オプションを使って、ソースファイルをどのように処理し、オブジェクト・ファイルや実行ファイルの特性をどのように制御するかを指定することができます。ただし、コンパイラー・オプションはソースファイル全体に適用されます。そのため、特定のループ、変数、関数、プロシージャーを制御するにはどうしたら良いでしょうか? ここで、プラグマとディレクティブが役に立ちます。この記事では、プラグマ/ディレクティブのドキュメントの入手について、そしてインテル® Xeon® プロセッサーまたはインテル® Xeon Phi™ コプロセッサーでパフォーマンスに最も貢献するこれらのサブセットを紹介します。
トピック
C/C++ の ‘pragma’ はインテル® C++ コンパイラーで処理されるプリプロセッサー・ディレクティブで、コンパイル時にコンパイラーの動作を指示します。Fortran の ‘directive’ はインテル® Fortran コンパイラーで処理されるディレクティブで、同様にコンパイル時にコンパイラーの動作を指示します。両者の構文は当然異なりますが、指定する動作はほとんど同じです。
C++ のプラグマ: 特定のケースにコンパイラーへ指示を与えるプリプロセッサー・ディレクティブです。例えば、novector プラグマを使用して、ループをベクトル化しないように指示できます。C++ 言語では #pragma が標準ですが、各プラグマはマシンに依存したり、オペレーティング・システムに依存し、コンパイラーによっても異なります。
いくつかのプラグマは、コンパイラー・オプションと同じ機能を提供します。またプラグマは、コンパイラー・オプションより優先されます。
一部のプラグマはインテル® プロセッサーおよび互換プロセッサーで利用可能ですが、インテル® プロセッサーにおいてより多くの最適化が行われる場合があります。詳細は、各プラグマの説明を参照してください。
インテル® C/C++ Composer XE コンパイラーで認識される C++ のプラグマのドキュメント: 『インテル® C++ コンパイラー XE ユーザー・リファレンス・ガイド』に記述されています。ドキュメントの場所は、「インテル® コンパイラーの基本的な使用方法」を参照してください。『ユーザー・リファレンス・ガイド』の [目次] から「コンパイラー・リファレンス・ガイド」 - 「プラグマ」 - 「概要: インテル® C++ コンパイラー・プラグマ」を開きます。
Fortran のディレクティブ: コンパイルの動作を指示する特別なコマンドです。インテル® Fortran Composer XE により認識されるディレクティブはコンパイラー固有のものです。ディレクティブはコンパイラー・オプションよりも優先されます。
インテル® Fortran Composer XE コンパイラーで認識されるディレクティブのドキュメント: 『インテル® Fortran コンパイラー XE ユーザー・リファレンス・ガイド』に記述されています。ドキュメントの場所は、「インテル® コンパイラーの基本的な使用方法」を参照してください。『ユーザー・リファレンス・ガイド』の [目次] から「コンパイラー・リファレンス・ガイド」 - 「Language Reference (英語)」 - 「Directive Enhanced Compilation」 - 「Directive Enhanced Compilation Overview」を開きます。
重要なプラグマ/ディレクティブを以下に紹介します。上記のドキュメントをブラウザーで開き、各プラグマ/ディレクティブの説明もお読みください。以下の説明では、C++ と Fortran の両方を C++/Fortran の順で示します。
- simd/SIMD – ベクトル化を強制します。SIMD は強力なツールです。SIMD の概要については、「Vectorization: Pragma/Directive SIMD」 (英語) をご覧ください。IVDEP を使用している場合は、SIMD への移行を検討してください。
- ivdep/IVDEPIVDEP プラグマ/ディレクティブは、ベクトル化の対象ループにベクトル依存性が存在していることが推定されても、それを無視するようにコンパイラーに指示します。通常、コンパイラーは正しいコードを生成するため、推定される依存性を証明された依存性として扱い、ベクトル化を行わないようにしますが、このプラグマはその決定を無視します。このプラグマは、推定されるループの依存性が安全で、無視できる場合にのみ使用してください。これは、ベクトル依存性が存在していることが推定されたループにのみ影響することに注意してください。依存性が 100% 確実であれば、コンパイラーはこのディレクティブを無視します。より細かい制御には、simd/SIMD を利用することを検討してください。
- loop_count/LOOP COUNT – 実行されるループの反復についてコンパイラーに指示します。多くのループには、変数で設定され、コンパイラーにのみランタイムで知らされる境界があるため、これは重要です。コンパイラーはループのベクトル化が “効果的” かどうか、つまり、対象のループをベクトル化することで、その作成のオーバーヘッドを超えるパフォーマンスの向上が得られるかどうかを判断するために、ループの反復数とループ内のワークを決定しようとします。しかし、コンパイラーはループの反復数を決定できないことがよくあるため、そのようなループのベクトル化を拒否する場合があります。-vec-report では、このようなベクトル化されなかったループを “有益ではない” または “有益ではない可能性がある” としてレポートします。この指示句は、ループの反復回数に関するヒントをコンパイラーに与え、有効性解析を支援します。
- vector/VECTOR と novector/NOVECTOR – ループのベクトル化に関してコンパイラー・オプションより優先し、ベクトル化が可能な場合はベクトル化を強制します。コンパイラーはループに依存関係があることを確実視すると、この指示句を無視します。novector ディレクティブは、ベクトル化により精度に問題が生じる際、ループのベクトル化を防ぐのによく使用されます。
- inline、forceinline、noinline/INLINE、FORCEINLINE、NOINLINE – コンパイラーのインライン展開ヒューリスティックにおいて細粒度の制御を提供します。
- inline、forceinline、noinline/INLINE、FORCEINLINE、NOINLINE – ループアンロール (ジャム) を有効/無効にします。unroll は、カウントされたループをアンロールする回数をコンパイラーに伝えます。これは、ベクトル化を助けます。unroll_and_jam ディレクティブは、入れ子構造の中で、最内ループから 1 つまたは複数外側のループを部分的にアンロールして、結果として残ったループを融合 (ジャム) します。この変換により、ループでの再利用率を向上できます。unroll_and_jam ディレクティブは最適化レベル -O3 (/O3) でのみ有効です。
- nofusion/NOFUSION – ループ融合の最適化を抑止します。
- distribute_point – 指定された位置でループ分配を行うようにコンパイラーに指示します。プラグマがループの内部に配置されると、コンパイラーはそのポイントでループを分配します。ループ伝播の依存はすべて無視されます。
プラグマ/ディレクティブはほかにもありますが、上記は主要な最適化を目的としたものです。パフォーマンスを高めたいと考えるプログラマーはよく理解し、精通しておくと良いでしょう。
まとめ
SIMD ディレクティブは、ループのベクトル化において最大の制御をプログラマーに提供します。IVDEP および VECTOR はコンパイラーに推奨またはヒントを提供する控えめで古いオプションです。ただし、コンパイラーはループに依存関係があると判断すると、IVDEP および VECTOR を無視する可能性があります。「Vectorization: Pragma/Directive SIMD」 (英語) の資料もぜひご覧ください。
次のステップ
この記事は、「Programming and Compiling for Intel® Many Integrated Core Architecture」(英語) の一部「Getting Started with Intel® Composer XE 2013, Compiler Pragmas and Directives」の翻訳です。インテル® Xeon Phi™ コプロセッサー上にアプリケーションを移植し、チューニングを行うには、本ガイドの各リンクのトピックを参照してください。アプリケーションのパフォーマンスを最大限に引き出すために必要なステップを紹介しています。
コンパイラーの最適化に関する詳細は、最適化に関する注意事項を参照してください。