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

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 組み込み関数は、自分で繰り返し回数を指定するという条件で、ループの開始時点でループの繰り返し回数がわからない場合にベクトル化を有効にするために必要です。

この場合、生成されるコードのサイズは小さくなり、パフォーマンスが向上します。

Example 11 の逆アセンブルした出力結果に、__promise を使用した場合の相違を示します。追加のループの繰り返しに必要なベクトル化されていないコードが削除され、逆アセンブリは単純なベクトル化されたループに縮小されています。つまり、NEON レジスタ内の適切なデータ型に使用できるレーンの倍数を超えるループの繰り返し回数に該当します。追加のベクトル化されていないコードは、スカラフィックスアップループと呼ばれます。__promise(expr) 組み込み関数を使用することで、スカラフィックスアップループが削除されます。

Example 11. __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 をサポートするプロセッサ用にコンパイルした場合、逆アセンブルした出力結果は次のようになります。

armcc -S promise.c

        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


Show/hide関連項目

Copyright © 2010 ARM. All rights reserved.ARM DUI 0472BJ
Non-ConfidentialID011811