インテル(R) コンパイラーでは、よく知られている高度な最適化手法が数多く採用されています。これらの手法は IA-32 ベース・システムおよび Itanium(R) ベース・システム上でハイパフォーマンスを実現するインテル(R) プロセッサー機能を活用できるよう設計されています。
インテル・コンパイラーは、サポートされる言語に対して共通の中間表現を持ちます。そのため、(ソース言語に関係なく) 単一の高レベルコード変換により、プラグマに基づく並列化および最適化手法の多くを適用することができます。
インテル・コンパイラーのコード変換および最適化は、次の機能に分類することができます。
コードの再構築とプロシージャー間の最適化 (IPO)
OpenMP ベース、および自動処理による並列化とベクトル化
高レベルの最適化 (HLO) とスカラー最適化 (ループ・コントロールやデータ変換などのメモリー最適化を含む)、部分冗長性排除 (PRE)、部分不要ストア排除 (PDSE)
低レベルのマシンコード生成と最適化 (レジスター割り当てや命令スケジューリングなど)
次の図は、異なる機能の相互関係を示しています。
並列化の方法としては、OpenMP プラグマによるものと、自動データ依存性解析または制御フロー解析を基に行われるものがあります。これらはいずれも高レベルコード変換を行うことによって、マルチプロセッサー・システム、デュアルコア・プロセッサー・システム、およびハイパースレッディング・テクノロジー (HT テクノロジー) 対応システムにおける中粒度/粗粒度の並列処理を利用し、パフォーマンスとスループットの向上を実現します。
インテル・コンパイラーは、共通の中間言語表現 (IL0) を採用しており、アプリケーションはフロントエンドでこの中間言語表現に変換されます。コンパイラーで行われる最適化処理の多くは、IL0 の中間コードに対して行われます。
IL0 は、OpenMP プラグマも表現できるように拡張されています。OpenMP による並列化を IL0 レベルで行うように実装しているため、異なる言語やアーキテクチャーで共通して、OpenMP の並列化を行えるようになっています。インテル・コンパイラーによって生成されたコードは高レベルのマルチスレッド・ライブラリー API を参照します。このため、インテル・コンパイラーでは OpenMP によるコード変換は、土台となるオペレーティング・システムには関係なく行えます。
インテル・コンパイラーは、OpenMP による並列化と高度な最適化を統合し、(最適化された単一プロセッサー・コードよりも実行速度の速い) 効率的なマルチスレッド・コードを生成します。インテル・コンパイラーでは最適化を行う順序を効果的に設計しており、IPO によるインライン展開、コードの再構築、Igoto 最適化、定数伝播といった最適化をすべて OpenMP で並列化を行う前に実行することにより、OpenMP プログラムの正しいセマンティクス、および並列化に必要な情報が保持されるようになっています。
また、ベクトル化の自動処理、ループ変換、PRE、PDSE など、OpenMP による並列化以降に行われるすべての最適化処理を効果的に適用し、キャッシュの局所性を高め、計算回数とメモリー参照回数を最小限にするように設計されています。例えば、2 重に入れ子された OpenMP 並列ループの場合、並列化手法では最内ループのループ構造、メモリー参照動作、およびシンボルテーブルの情報は保持しながら、外側のループに対してマルチスレッド・コードを生成することができます。このため、後から最内ループに対してレジスター内のベクトル化を行うことができ、インテル・プロセッサーに備わっているハイパースレッディング・テクノロジーやストリーミング SIMD 拡張命令といった機能をフルに利用できるようになります。
インテル・コンパイラーでは、OpenMP による並列化は次の段階で構成されます。
OpenMP の並列化セクションを並列化ループに、ワークシェアリング・セクションをワークシェアリング・ループにそれぞれ変換するプリパス。
OpenMP を意識したコントロール・フロー・グラフをベースに領域の階層グラフを作成するワークリージョン・グラフ・ビルダー。
ループ・コントロール変数、ループ下限、ループ上限、ループ・プリヘッダー、ループヘッダー、および制御式で構成されるループ構造を構築するループ解析フェーズ。
共有変数およびプライベート変数の解析を行う変数分類フェーズ。
コンパイラーの中間コードのレベルで、Guide (マルチスレッド化されたランタイム・ライブラリー API) をベースにしてマルチスレッド・コードを生成するマルチスレッド・コード・ジェネレーター。
プライベート化を実行して firstprivate、private、lastprivate、reduction の各変数を扱うプライベート化処理機能。
スレッド・ローカル・ストレージにキャッシュするコードを生成して、threadprivate 変数を処理するポストパス。
OpenMP (コンパイラー・ベースのスレッド手法) は、土台となるスレッド・ライブラリーに高レベルのインターフェイスを提供します。OpenMP による並列化では、プラグマを使用して、コンパイラーに並列化を指示します。提供されたプラグマを使用すると、コンパイラーによって細部が処理されるため、明示的スレッド化に関わる複雑さを軽減することになります。OpenMP では通常、ソースコードの大きな変更は必要ありません。OpenMP をサポートしないコンパイラーでは、プラグマは無視され、オリジナルの直列コードはそのままの状態で保たれます。
その他すべての最適化と同様に、並列パフォーマンスを向上するための鍵は、アプリケーションに対して適切な粒度を選択することです。ここでは、粒度は並列タスクにおける作業量を意味します。粒度を高く設定すると、通信オーバーヘッドが増加し、パフォーマンスが低下します。逆に、粒度を低く設定すると、負荷のバランスが崩れ、パフォーマンスが低下します。設計する際の課題は、負荷の不均衡と通信オーバーヘッドを回避し、並列タスクに対して適切な粒度を決定することです。
マルチスレッド・アプリケーションにおける各並列タスクの作業量または粒度は、並列パフォーマンスに大きく影響します。アプリケーションをスレッド化する場合、最初に問題をできるだけ多くの並列タスクにパーティショニングします。次に、データおよび同期化に必要な通信を決定します。続いて、アルゴリズムのパフォーマンスについて考えます。通信とパーティショニングにはオーバーヘッドが伴うため、通常、パーティションを結合する必要があります。これにより、オーバーヘッドの問題を解決し、最も効率的な方法で実装することができます。結合ステップは、アプリケーションに最適な粒度を決定します。
粒度は通常、スレッド間におけるワークロードのバランスと関連します。多くの小さなタスクからなるワークロードの方がバランスをとりやすくなります。しかし、タスクの量が多すぎると、並列オーバーヘッドが大きくなります。このため、通常、粗粒度が最適です。粒度を上げすぎると負荷の不均衡が生じます。インテル(R) スレッド・プロファイラーなどのツールは、アプリケーションに対する最適な粒度を特定するのに役立ちます。
ハイパースレッディング・テクノロジーに関する詳細は、『IA-32 インテル(R) アーキテクチャー最適化リファレンス・マニュアル』 (http://www.intel.com/jp/developer/download/) を参照してください。