非機密扱い | ![]() | ARM DUI0472LJ | ||
| ||||
ホーム > コンパイラのコーディング慣行 > C コードのループ終了の最適化 |
ループは、大半のプログラムに含まれている一般的な構文の 1 つです。ループの実行には多大な時間が費やされることが多いので、時間制限の厳しいループに注意を払うことを推奨します。
unsigned int
型のカウンタを使用する。n!
を計算するルーチンの実装例を 2 つ示します。これらの例は共に、ループ終了のオーバーヘッドを示しています。最初の実装では、インクリメントループを使用して n!
が計算されるのに対し、2 番目の実装では、デクリメントループを使用して n!
が計算されます。表 5-1 インクリメントループとデクリメントループを表す 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); } |
O2
-Otime
を使用してコンパイルされています。表 5-2 インクリメントループとデクリメントループを表す 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 |
ADD
および CMP
が、デクリメントループ逆アセンブリの単一の SUBS
命令で置換されていることがわかります。これは、代わりにゼロとの比較を使用できるためです。n
をループをまたがって保存する必要はありません。そのため、デクリメントループ逆アセンブリではレジスタの使用も節約され、レジスタの割り当てが容易になります。これは、元の終了条件に関数呼び出しが含まれる場合には、より重要になります。例えば、for (...; i < get_limit(); ...);
while
ステートメントと
do
ステートメントにも適用されます。