3.19 __promise(expr) によるコンパイラへのループ繰り返し回数の指定

__promise 組み込み関数を使用すると、ループの繰り返し回数が常に、例えば 8 の倍数であることをコンパイラに通知できます。これにより、コンパイラはランタイム繰り返し回数テストのオーバーヘッドを減らすことでサイズが小さくより高速なコードを生成できます。

NEON ユニットは、2、4、8、16 のグループの要素に対して演算を実行できます。ループの開始時に繰り返し回数がわかっていない場合、コンパイラはランタイムテストを追加して、繰り返し回数が NEON レジスタ内の適切なデータ型に使用できるレーンの倍数でないことを確認します。この場合、余ったループの繰り返しを実行するためにベクトル化されないコードが生成されて追加されるので、コードサイズが増加します。
めったに発生しない厄介なケースもありますが、通常、ランタイムテストによって発生するオーバーヘッドは、ベクトル化されたコードによるパフォーマンスの向上と比較してわずかなものです。例えば、繰り返し回数が 17 の場合、16 個の要素からなるグループが並列に実行され、1 回の繰り返しがベクトル化できないコードとして残されます。これに対し、繰り返し回数が 3 の場合は、2 個の要素からなるグループだけが並列に実行されます。後者の場合、ランタイムテストのオーバーヘッドは、ベクトル化されたコードとの比較において比例して大きくなります。
繰り返し回数が NEON ユニットで並列に操作できる要素の数で割り切れることがわかっている場合は、そのことを以下のように __promise 組み込み関数を使用してコンパイラに指示できます。
/* ループの繰り返し回数が 16 で割り切れることをコンパイラに保証する */
__promise((k % 16) == 0);
for (i = 0; i < k; i++)
{
    ...
}
__promise 組み込み関数は、自分で繰り返し回数を指定するという条件で、ループの開始時点でループの繰り返し回数がわからない場合にベクトル化を有効にするために必要です。
この場合、生成されるコードのサイズは小さくなり、パフォーマンスが向上します。
以下のサンプルコードを逆アセンブルした出力結果に、__promise を使用した場合の相違を示します。追加のループの繰り返しに必要なベクトル化されていないコードが削除され、逆アセンブリは単純なベクトル化されたループに縮小されています。つまり、NEON レジスタ内の適切なデータ型に使用できるレーンの倍数を超えるループの繰り返し回数に該当します。追加のベクトル化されていないコードは、スカラフィックスアップループと呼ばれます。__promise(expr) 組み込み関数を使用することで、スカラフィックスアップループが削除されます。
/* promise.c */
void f(int *x, int n)
{
    int i;
    __promise((n > 0) && ((n & 7) == 0));
    for (i=0; i < n; i++) x[i]++;
}
NEON をサポートするプロセッサ用にコンパイルした場合、逆アセンブルした出力結果は次のようになります。
        ARM
        REQUIRE8
        PRESERVE8
        AREA ||.text||, CODE, READONLY, ALIGN=2
f PROC
        VMOV.I32 q0,#0x1
        ASR      r1,r1,#2
|L0.8|
        VLD1.32  {d2,d3},[r0]
        SUBS     r1,r1,#1
        VADD.I32 q1,q1,q0
        VST1.32  {d2,d3},[r0]!
        BNE      |L0.8|
        BX       lr
        ENDP
関連する概念
3.18 ベクトル化可能なループ繰り返し回数
関連する参考文書
8.192 --vectorize、--no_vectorize
8.164 --restrict、--no_restrict
9.13 restrict
10.131 __promise コンパイラ組み込み関数
非機密扱いPDF file icon PDF 版ARM DUI0472LJ
Copyright © 2010-2015 ARM.All rights reserved.