Renesas Synergy™

FAQ 1008236 : 高速化のための式の記述で、注意することはありますか?

回答

次のような記述で高速化することができます。
場合によって、利用してみてください。



ソースを変更した結果、演算やメモリ参照が減少し、コード・サイズも減少する場合があります。



ソース変更を行う際には、次のような点に注意してください。

  • ソース変更によりレジスタの使用状況が変わるため、意図しない箇所において、 それまで最適化されずに残っていたレジスタ転送が削除されたり、 逆に、最適化が効かなくなって冗長なレジスタ転送が残ったりする可能性があります。
  • テンポラリ変数を追加することにより、新たなレジスタ変数用レジスタが使用されるようになり、 それに伴い、関数の入口/出口でそのレジスタの退避/復帰が追加されることがあります。
    この場合、退避/復帰のコード分(8バイト)だけコード・サイズが増加します。


式の整理

式の記述の仕方によって演算の数を減らすことができます。


※以下の例では、x、yは外部変数であるものとします。

変更前
------------------------------
    y = 7*x*x*x+5*x*x+x;
------------------------------
    ↓
変更後
------------------------------
    y = x*(7*x*x+5*x+1);
------------------------------

この例では、出力命令数が3つ減少します。


シフト演算の利用

計算式で2のべき乗(2、4、8、16、32...)での剰余算は、 シフト命令に置き換えた方が高速化できます。
明らかに2のべき乗になる計算は、 コーディング時にシフト命令にしましょう。
ただし、負の値を扱う場合は不具合が生じます。
正の値(unsingned int型)を扱う場合に利用してください。
なお、Cコンパイラの最適化ではシフト命令に置き換えられます。


※以下の例では、s、t、uは外部変数(unsigned int)であるものとします。

変更前
---------------
    s = s/2;
    t = t*8;
    u = u*64;
---------------
    ↓
変更後
---------------
    s = s>>1;
    t = t<<3;
    u = t<<6;
---------------


ループ内での配列の連続アクセス

ループ内での配列の連続アクセスは、ポインタで行いましょう。
ポインタを使用しない場合、配列の添え字から実アドレスを求める処理が、 毎回出力される可能性があります。


※以下の例では、sum、array[]は外部変数であるものとします。

通常の記述
------------------------------
    int  i;

    sum = 0;
    for(i = 0; i < 10; i++){
        sum += array[i];

    }
------------------------------
    ↓
高速化の記述
------------------------------
    int  i;
    int  *ptr;

    sum = 0;
    ptr = &array[0];
    for(i = 0; i < 10; i++){
        sum += *ptr++;

    }
------------------------------

この例では、出力命令で、シフト命令が1つなくなります。


ループ内の外部変数

ループ内では、できるだけ外部変数を使用しないようにしましょう。
アドレス計算やメモリ・アクセス(ld/st命令)が毎回出力される可能性があります。


※以下の例では、sum、array[]は外部変数であるものとします。

通常の記述
------------------------------
    int i;
    int *ptr;

    sum = 0;
    ptr = &array[0];
    for(i = 0; i < 10; i++){
        sum += *ptr++;
    }
------------------------------
    ↓
高速化の記述
------------------------------
    int i;
    int *ptr;
    int tmp;

    smp = 0;
    ptr = &array[0];
    for(i = 0; i < 10; i++){
        tmp += *ptr++;
    }
    sum = tmp;
------------------------------

この例では、出力命令で、ストア命令が1つなくなります。


ループの終了条件(1)

ループの終了条件には、できるだけ変数式を使用しないようにしましょう。
終了式の比較のために演算が毎回出力される可能性があります。


※以下の例では、array[][]は外部変数であるものとします。

通常の記述
------------------------------
    int  i;
    int  nSize;
    int  mSize;
    int  *ptr;

    ptr = &(array[0][0]);
    for(i = 0; i < nSize * mSize; i++){
        *ptr++ = 0;

    }
------------------------------
    ↓
高速化の記述
------------------------------
    int  i;
    int  nSize;
    int  mSize;
    int  *ptr;
    int  size;

    ptr = &(array[0][0]);
    size = nSize * mSize;
    for(i = 0; i < size; i++){
        *ptr++ = 0;

    }
------------------------------

なお、この例では、出力命令は、Cコンパイラの最適化で同じになります。


ループの終了条件(2)

ループの終了条件に0との比較式を使用すると、 ループ1回ごとの終了条件の演算が速くなる可能性があります。
また、使用するレジスタ数が減る可能性もあります。


※以下の例では、array[][]は外部変数であるものとします。

通常の記述
------------------------------
    int  i;
    int  nSize;
    int  mSize;
    int  *ptr;
    int  size;

    ptr = &(array[0][0]);
    size = nSize * mSize;
    for(i = 0; i < size; i++){
        *ptr++ = 0;

    }
------------------------------
    ↓
高速化の記述
------------------------------
    int  i;
    int  nSize;
    int  mSize;
    int  *ptr;

    ptr = &(array[0][0]);
    for(i = nSize * mSize; i > 0; i--){
        *ptr++ = 0;

    }
------------------------------

なお、この例では、出力命令は同じになります。


ループの展開

ループする回数を削減すると、ループによる分岐命令のためのオーバヘッドが減少します。


※以下の例では、array[]は外部変数であるものとします。

通常の記述
------------------------------
    int  i;
    int  *ptr;

    ptr = &array[0];
    for(i = N; i > 0; i--){
        *ptr++ = 0;

    }
------------------------------
    ↓
高速化の記述
------------------------------
    int  i;
    int  *ptr;

    ptr = &array[0];
    for(i = N >> 2; i > 0; i--){  /* N/4 */
        *ptr++ = 0;
        *ptr++ = 0;
        *ptr++ = 0;
        *ptr++ = 0;
    }
    for(i = N & 3; i > 0; i--){  /* N mod 4 */
        *ptr++ = 0;

    }
------------------------------

この例では、出力命令数は増加しますが、分岐する回数は減少します。


ループ内でのポインタの最適化

ループ内で連続処理をするポインタの使い方で、アドレス計算によるオーバヘッドが減少します。


※以下の例では、array[]は外部変数で、Nは10であるものとします。

通常の記述
------------------------------
    int  i;
    int  *ptr;

    ptr = &array[0];
    for(i = N >> 2; i > 0; i--){  /* N/4 */
        *ptr++ = 0;
        *ptr++ = 0;
        *ptr++ = 0;
        *ptr++ = 0;
    }
    for(i = N & 3; i > 0; i--){  /* N mod 4 */
        *ptr++ = 0;

    }
------------------------------
    ↓
高速化の記述
------------------------------
    int  i;
    int  *ptr;

    ptr = &array[0];
    for(i = N >> 2; i > 0; i--){  /* N/4 */
        *(ptr + 0) = 0;
        *(ptr + 1) = 0;
        *(ptr + 2) = 0;
        *(ptr + 3) = 0;
        ptr += 4;
    }
    for(i = N & 3; i > 0; i--){  /* N mod 4 */
        *ptr++ = 0;

    }
------------------------------

この例では、出力命令で、アドレス計算のためのaddi命令がなくなります。

適用製品

V850用コンパイラパッケージ [CA850]
他にご質問がございましたら、リクエストを送信してください