C コードのループ終了の最適化

ループは、大半のプログラムに含まれている一般的な構文の 1 つです。ループの実行には多大な時間が費やされることが多いので、時間制限の厳しいループに注意を払うことを推奨します。

ループ終了条件は、多大なオーバーヘッドの原因となりかねないので、その作成にあたっては注意が必要です。可能な限り以下を実践して下さい。

任意のガイドラインまたはすべてのガイドラインに関して、個別にまたはそれらを組み合わせて従うことで、より優れたコードを作成できます。

Table 8 に、n! を計算するルーチンの実装例を 2 つ示します。これらの例は共に、ループ終了のオーバーヘッドを示しています。最初の実装では、インクリメントループを使用して n! が計算されるのに対し、2 番目の実装では、デクリメントループを使用して n! が計算されます。

Table 8. インクリメントループとデクリメントループを表す C コード

インクリメントループデクリメントループ
int fact1(int n)
{
    int i, fact = 1;
    for (i = 1; i <= n; i++)
        fact *= i;
    return (fact);
}
int fact2(int n)
{
    unsigned int i, fact = 1;
    for (i = n; i != 0; i--)
        fact *= i;
    return (fact);
}

Table 9 は、コンパイラによって生成されたマシンコードがTable 8 の各実装例でどのように逆アセンブルされるかを示したものです。いずれの実装の C コードも、オプション -O2 -Otime を使用してコンパイルされています。

Table 9. インクリメントループとデクリメントループを表す C 逆アセンブリコード

インクリメントループデクリメントループ
fact1 PROC
    MOV      r2, r0
    MOV      r0, #1
    CMP      r2, #1
    MOV      r1, r0
    BXLT     lr
|L1.20|
    MUL      r0, r1, r0
    ADD      r1, r1, #1
    CMP      r1, r2
    BLE      |L1.20|
    BX       lr
    ENDP
fact2 PROC
    MOVS     r1, r0
    MOV      r0, #1
    BXEQ     lr
|L1.12|
    MUL      r0, r1, r0
    SUBS     r1, r1, #1
    BNE      |L1.12|
    BX       lr
ENDP

Table 9 の逆アセンブリを比較すると、インクリメントループ逆アセンブリの命令ペア ADD および CMP が、デクリメントループ逆アセンブリの単一の SUBS 命令で置換されていることがわかります。これは、代わりにゼロとの比較を使用できるためです。

ループ内で命令を節約したことに加えて、変数 n をループをまたがって保存する必要はありません。そのため、デクリメントループ逆アセンブリではレジスタの使用も節約され、レジスタの割り当てが容易になります。これは、元の終了条件に関数呼び出しが含まれる場合には、より重要になります。以下に例を示します。

for (...; i < get_limit(); ...);

ループカウンタを必要な繰り返し数に初期化して、ゼロにデクリメントするテクニックは、while ステートメントと do ステートメントにも適用されます。

Show/hide関連項目

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