レジスター割り当てについて

IA-32 およびインテル(R) 64 対応システムのインテル(R) コンパイラーには、領域ベースの高度なレジスター・アロケーターが含まれています。レジスター割り当ては、-opt-ra-region-strategy (Linux* および Mac OS* X) および /Qopt-ra-region-strategy (Windows*) オプションを使用して、操作することができます。

ルーチンをコンパイルする際のレジスター割り当ての高レベルな手法は、ルーチンを領域へ分割し、各領域内のレジスターまたはメモリーに変数を割り当てて、領域境界における不一致を解決します。割り当ての全体的な性能は、領域のパーティショニングに大いに依存します。

インテル・コンパイラーは、デフォルトで、最適な領域パーティショニング手法を選択しますが、-opt-ra-region-strategy (Linux および Mac OS X) または /Qopt-ra-region-strategy (Windows) オプションを使用して、ほかに利用可能な割り当て手法を選択することも可能です。場合によっては、他の手法を選択したほうがパフォーマンスが向上することがあります。割り当て手法を指定する引数は次のとおりです。

詳細は、-opt-ra-region-strategy コンパイラー・オプションを参照してください。

このオプションは、コンパイル時間に影響を与えます。レジスター割り当ては比較的負担のかかる操作で、レジスター割り当てに時間がかかると領域の数も増える傾向があります。相対的なコンパイル時間は、次の順に増加すると考えられます。

  1. ルーチンベースの領域 (最短)

  2. ループベースの領域

  3. トレースベースの領域

  4. ブロックベースの領域 (最長)

トレースベースの領域は、プロファイルに基づく最適化が有効な場合に非常に適しています。アロケーターは、ルーチンのホットなパスを正確に反映するトレースを構築することができます。

プロファイル情報がない場合、プログラムの実行プロファイルはループ構造と一致することが多いため、ループベースの領域が適しています。実行プロファイルがループ構造、ルーチン、またはブロックベースの領域と一致しないプログラムでは、より良い割り当てが行われます。

ブロックベースの領域は、アロケーターに最大限の柔軟性を提供し、多くの場合、最良の割り当てが行われます。しかし、アロケーターは、レジスターへの変数割り当てでブロックベースの領域を積極的に利用しすぎる傾向があるため、レジスターが希少なリソースである IA-32 およびインテル 64 対応システムでは割り当てが適切に行われないことがあります。

レジスター割り当ての例

ループ内の単純な if-then-else 文で制御の流れを示す、次の例について考えてみます。このループには、isn の 3 つの変数があります。この例では、これらの 3 つの変数を保持できるレジスターは 2 つのみで、1 つ以上の変数が少なくともループの一部でメモリーを格納する必要があると仮定します。

最適な選択は、ループでどのパスが頻繁に実行されるかに依存します。例えば、B1、B2、B4 がホットパスである場合、そのパスに沿ってレジスターで変数 in を保持し、B3 でその 1 つを保存して復元し、s にレジスターを解放するのが最良の方法です。この方法では、ホットパスでメモリーアクセスがすべて回避されます。B1、B3、B4 がホットパスの場合、パスに沿って n を割り当てられないため、レジスターで is を保持してメモリーに n を格納するのが最良の方法です。この方法では、ホットパスで 1 回のメモリー読み取りが行われます。両方のパスが同じ頻度で実行される場合、B1、B2、B4 と同じように B3 で i または n のいずれかを保存して復元するのが最良の方法です。この方法では、1 つのパスでメモリーアクセスがすべて回避され、他のパスで 1 回のメモリー書き込みと 1 回のメモリー読み取りが行われます。

コンパイラーは、領域の手法に依存するこの例で 2 つの大きく異なる割り当てを行います。どちらの割り当てが適しているかは、プログラムのランタイムの動作に依存します。コンパイル時にはわかりません。

ルーチンまたはループベースの手法では、4 つのブロックはすべて一緒に割り当てられます。コンパイラーは、予想される負担に基づいてメモリーに格納する変数を選択します。この例では、通常、アロケーターは変数 n を選択し、B2 でメモリー書き込み、B3 でメモリー読み取りを行います。

トレースベースの手法では、コンパイラーは実行頻度の推定によって、ループで最も頻繁に実行されるパスを選択します。プロファイルに基づく最適化が有効な場合、その推定はインストルメント済みプログラムのランタイムの動作に関する具体的な情報に基づいています。PGO 情報がアプリケーションの動作を正確に反映する場合、コンパイラーは非常に正確なトレースを行うことができます。情報が正確ではない場合、トレースはコードのホットパスを正確に反映しません。

この例で、コンパイラーは B1、B2、B4 パスをホットパスとして選択すると仮定します。コンパイラーは、1 つの領域にこれらの 3 つのブロックを割り当て、B3 を別の領域に割り当てます。より大きな領域にある変数は 2 つのみです。つまり、両方の領域がレジスターで保持されます。B3 のみを含む領域では、i または n のいずれかはメモリーに格納されます。コンパイラーは 2 つの変数を任意に選択します。ブロックベースの手法では、各ブロックが領域です。B1、B2、B4 パスでは、2 つの変数 in はレジスターに保持されます。B3 を含む領域は、トレースベースの場合と同じように、i または n のいずれかをメモリーに格納します。