結論#
結論は前に書かれています
for ループ文内でローカルループ変数を定義しても、AC6 コンパイラまたは GCC コンパイラを使用しても、複数回のスタック操作は発生しません。代わりに、同じ 2 つのスタックオフセットが使用されます。最適化を行った場合、両者の論理機能に実際の差異がない場合、アセンブリは完全に同じになります。
実際に for ループの同時にループ変数を定義することは優れた操作です。すべてのローカル変数の定義を関数の先頭に移動すると、事実上の負の最適化または最適化なし(最適化レベルとコンパイラによって異なる)が発生します。
したがって、極限のパフォーマンスを追求する場合は、ローカル変数を使用する分岐内でのみそのローカル変数を宣言すべきです。
以下のテストはすべて stm32H7 をターゲットにコンパイルされています
以下の書き方でローカル変数のスタック操作について議論します#
for(int i = 0; i < 50; i++)
{
for(int j = 0; j < 50; j++)
{
HAL_Delay(1);
}
}
感覚的には、最初のループが 1 回実行されるごとに、ローカル変数 j が宣言されますが、これによって複数のスタック要求操作が発生するのでしょうか?
この部分の逆アセンブルは以下の通りです#
0x0000001e: LDR r0,[sp,#0]
0x00000020: STR r0,[sp,#8]
0x00000022: B {pc}+0x2 ; 0x24
0x00000024: LDR r0,[sp,#8]
0x00000026: CMP r0,#0x31
0x00000028: BGT {pc}+0x2c ; 0x54
0x0000002a: B {pc}+0x2 ; 0x2c
0x0000002c: MOVS r0,#0
0x0000002e: STR r0,[sp,#4]
0x00000030: B {pc}+0x2 ; 0x32
0x00000032: LDR r0,[sp,#4]
0x00000034: CMP r0,#0x31
0x00000036: BGT {pc}+0x14 ; 0x4a
0x00000038: B {pc}+0x2 ; 0x3a
0x0000003a: MOVS r0,#1
0x0000003c: BL HAL_Delay
0x00000040: B {pc}+0x2 ; 0x42
0x00000042: LDR r0,[sp,#4]
0x00000044: ADDS r0,#1
0x00000046: STR r0,[sp,#4]
0x00000048: B {pc}-0x16 ; 0x32
0x0000004a: B {pc}+0x2 ; 0x4c
0x0000004c: LDR r0,[sp,#8]
0x0000004e: ADDS r0,#1
0x00000050: STR r0,[sp,#8]
0x00000052: B {pc}-0x2e ; 0x24
外側のループ#
これは私たちの主要な議論の対象ではありませんが、内側のループを 50 回実行するためにジャンプを使用します
0x0000001e: LDR r0,[sp,#0]
0x00000020: STR r0,[sp,#8]
0x00000022: B {pc}+0x2 ; 0x24
0x00000024: LDR r0,[sp,#8]
0x00000026: CMP r0,#0x31
0x00000028: BGT {pc}+0x2c ; 0x54
0x0000002a: B {pc}+0x2 ; 0x2c
; .....内側のループ
0x0000004a: B {pc}+0x2 ; 0x4c
0x0000004c: LDR r0,[sp,#8]
0x0000004e: ADDS r0,#1
0x00000050: STR r0,[sp,#8]
0x00000052: B {pc}-0x2e ; 0x24
内側のループ#
0x0000002c: MOVS r0,#0
0x0000002e: STR r0,[sp,#4]
0x00000030: B {pc}+0x2 ; 0x32
0x00000032: LDR r0,[sp,#4]
0x00000034: CMP r0,#0x31
0x00000036: BGT {pc}+0x14 ; 0x4a
0x00000038: B {pc}+0x2 ; 0x3a
0x0000003a: MOVS r0,#1
0x0000003c: BL HAL_Delay
0x00000040: B {pc}+0x2 ; 0x42
0x00000042: LDR r0,[sp,#4]
0x00000044: ADDS r0,#1
0x00000046: STR r0,[sp,#4]
0x00000048: B {pc}-0x16 ; 0x32
2c、2e の命令は sp+4 のスタックの値を 0 に設定します
その後、1 を加算し、ジャンプを利用して 50 回ループを実行します
つまり、外側のループが実行されるたびに、sp+4 のスタックに対するこの操作ロジックが存在し、外側のループは常に sp+8 のスタックに対する操作ロジックがあります
もしローカル変数を事前に定義した場合は?#
次のように書き換えます
int i = 0;
int j = 0;
for(i = 0; i < 50; i++)
{
for(j = 0; j < 50; j++)
{
HAL_Delay(1);
}
}
この部分の逆アセンブルは以下の通りです#
0x0000001e: LDR r0,[sp,#0]
0x00000020: STR r0,[sp,#8]
0x00000022: STR r0,[sp,#4]
0x00000024: STR r0,[sp,#8]
0x00000026: B {pc}+0x2 ; 0x28
0x00000028: LDR r0,[sp,#8]
0x0000002a: CMP r0,#0x31
0x0000002c: BGT {pc}+0x2c ; 0x58
0x0000002e: B {pc}+0x2 ; 0x2c
0x00000030: MOVS r0,#0
0x00000032: STR r0,[sp,#4]
0x00000034: B {pc}+0x2 ; 0x36
0x00000036: LDR r0,[sp,#4]
0x00000038: CMP r0,#0x31
0x0000003a: BGT {pc}+0x14 ; 0x4e
0x0000003c: B {pc}+0x2 ; 0x3e
0x0000003e: MOVS r0,#1
0x00000040: BL HAL_Delay
0x00000044: B {pc}+0x2 ; 0x46
0x00000046: LDR r0,[sp,#4]
0x00000048: ADDS r0,#1
0x0000004a: STR r0,[sp,#4]
0x0000004c: B {pc}-0x16 ; 0x36
0x0000004e: B {pc}+0x2 ; 0x50
0x00000050: LDR r0,[sp,#8]
0x00000052: ADDS r0,#1
0x00000054: STR r0,[sp,#8]
0x00000056: B {pc}-0x2e ; 0x28
ループ部分(26-56)を見てみると、以前の書き方(22-52)と差異はなく、逆に (sp+4) と (sp+8) を 0 にする 2 つの命令が追加され、負の最適化が発生しています。
ループを複雑にするとどうなるか#
次のコードは、逆アセンブルで (sp+8) と (sp+12) を使用しても、過剰なスタック操作は発生しません
int test = 0;
for(int i = 0; i < 50; i++)
{
for(int j = 0; j < 50; j++)
{
if((test & 0x01) == 0)
HAL_Delay(1);
else
HAL_Delay(2);
}
test++;
}
0x0000001e: 9801 .. LDR r0,[sp,#4]
0x00000020: 9004 .. STR r0,[sp,#0x10]
0x00000022: 9003 .. STR r0,[sp,#0xc]
0x00000024: e7ff .. B {pc}+0x2 ; 0x26
0x00000026: 9803 .. LDR r0,[sp,#0xc]
0x00000028: 2831 1( CMP r0,#0x31
0x0000002a: dc21 !. BGT {pc}+0x46 ; 0x70
0x0000002c: e7ff .. B {pc}+0x2 ; 0x2e
0x0000002e: 2000 . MOVS r0,#0
0x00000030: 9002 .. STR r0,[sp,#8]
0x00000032: e7ff .. B {pc}+0x2 ; 0x34
0x00000034: 9802 .. LDR r0,[sp,#8]
0x00000036: 2831 1( CMP r0,#0x31
0x00000038: dc12 .. BGT {pc}+0x28 ; 0x60
0x0000003a: e7ff .. B {pc}+0x2 ; 0x3c
0x0000003c: f89d0010 .... LDRB r0,[sp,#0x10]
0x00000040: 07c0 .. LSLS r0,r0,#31
0x00000042: b920 . CBNZ r0,{pc}+0xc ; 0x4e
0x00000044: e7ff .. B {pc}+0x2 ; 0x46
0x00000046: 2001 . MOVS r0,#1
0x00000048: f7fffffe .... BL HAL_Delay
0x0000004c: e003 .. B {pc}+0xa ; 0x56
0x0000004e: 2002 . MOVS r0,#2
0x00000050: f7fffffe .... BL HAL_Delay
0x00000054: e7ff .. B {pc}+0x2 ; 0x56
0x00000056: e7ff .. B {pc}+0x2 ; 0x58
0x00000058: 9802 .. LDR r0,[sp,#8]
0x0000005a: 3001 .0 ADDS r0,#1
0x0000005c: 9002 .. STR r0,[sp,#8]
0x0000005e: e7e9 .. B {pc}-0x2a ; 0x34
0x00000060: 9804 .. LDR r0,[sp,#0x10]
0x00000062: 3001 .0 ADDS r0,#1
0x00000064: 9004 .. STR r0,[sp,#0x10]
0x00000066: e7ff .. B {pc}+0x2 ; 0x68
0x00000068: 9803 .. LDR r0,[sp,#0xc]
0x0000006a: 3001 .0 ADDS r0,#1
0x0000006c: 9003 .. STR r0,[sp,#0xc]
0x0000006e: e7da .. B {pc}-0x48 ; 0x26
次のコードでは、宣言を前に移動しても、依然として負の最適化が発生します
int test = 0;
int i = 0;
int j = 0;
for(i = 0; i < 50; i++)
{
for(j = 0; j < 50; j++)
{
if((test & 0x01) == 0)
HAL_Delay(1);
else
HAL_Delay(2);
}
test++;
}
0x0000001e: 9801 .. LDR r0,[sp,#4]
0x00000020: 9004 .. STR r0,[sp,#0x10]
0x00000022: 9003 .. STR r0,[sp,#0xc]
0x00000024: 9002 .. STR r0,[sp,#8]
0x00000026: 9003 .. STR r0,[sp,#0xc]
0x00000028: e7ff .. B {pc}+0x2 ; 0x2a
0x0000002a: 9803 .. LDR r0,[sp,#0xc]
0x0000002c: 2831 1( CMP r0,#0x31
0x0000002e: dc21 !. BGT {pc}+0x46 ; 0x74
0x00000030: e7ff .. B {pc}+0x2 ; 0x32
0x00000032: 2000 . MOVS r0,#0
0x00000034: 9002 .. STR r0,[sp,#8]
0x00000036: e7ff .. B {pc}+0x2 ; 0x38
0x00000038: 9802 .. LDR r0,[sp,#8]
0x0000003a: 2831 1( CMP r0,#0x31
0x0000003c: dc12 .. BGT {pc}+0x28 ; 0x64
0x0000003e: e7ff .. B {pc}+0x2 ; 0x40
0x00000040: f89d0010 .... LDRB r0,[sp,#0x10]
0x00000044: 07c0 .. LSLS r0,r0,#31
0x00000046: b920 . CBNZ r0,{pc}+0xc ; 0x52
0x00000048: e7ff .. B {pc}+0x2 ; 0x4a
0x0000004a: 2001 . MOVS r0,#1
0x0000004c: f7fffffe .... BL HAL_Delay
0x00000050: e003 .. B {pc}+0xa ; 0x5a
0x00000052: 2002 . MOVS r0,#2
0x00000054: f7fffffe .... BL HAL_Delay
0x00000058: e7ff .. B {pc}+0x2 ; 0x5a
0x0000005a: e7ff .. B {pc}+0x2 ; 0x5c
0x0000005c: 9802 .. LDR r0,[sp,#8]
0x0000005e: 3001 .0 ADDS r0,#1
0x00000060: 9002 .. STR r0,[sp,#8]
0x00000062: e7e9 .. B {pc}-0x2a ; 0x38
0x00000064: 9804 .. LDR r0,[sp,#0x10]
0x00000066: 3001 .0 ADDS r0,#1
0x00000068: 9004 .. STR r0,[sp,#0x10]
0x0000006a: e7ff .. B {pc}+0x2 ; 0x6c
0x0000006c: 9803 .. LDR r0,[sp,#0xc]
0x0000006e: 3001 .0 ADDS r0,#1
0x00000070: 9003 .. STR r0,[sp,#0xc]
0x00000072: e7da .. B {pc}-0x48 ; 0x2a
最適化を使用する#
O1#
引き続き、上記のループを複雑にします
for 内で宣言
0x00000014: 2400 .$ MOVS r4,#0
0x00000016: bf00 .. NOP
0x00000018: f0040501 .... AND r5,r4,#1
0x0000001c: 2632 2& MOVS r6,#0x32
0x0000001e: bf00 .. NOP
0x00000020: 2002 . MOVS r0,#2
0x00000022: 2d00 .- CMP r5,#0
0x00000024: bf08 .. IT EQ
0x00000026: 2001 . MOVEQ r0,#1
0x00000028: f7fffffe .... BL HAL_Delay
0x0000002c: 3e01 .> SUBS r6,#1
0x0000002e: d1f7 .. BNE {pc}-0xe ; 0x20
0x00000030: 3401 .4 ADDS r4,#1
0x00000032: 2c32 2, CMP r4,#0x32
0x00000034: d1f0 .. BNE {pc}-0x1c ; 0x18
宣言を前に移動すると、両者は完全に一致します
0x00000014: 2400 .$ MOVS r4,#0
0x00000016: bf00 .. NOP
0x00000018: f0040501 .... AND r5,r4,#1
0x0000001c: 2632 2& MOVS r6,#0x32
0x0000001e: bf00 .. NOP
0x00000020: 2002 . MOVS r0,#2
0x00000022: 2d00 .- CMP r5,#0
0x00000024: bf08 .. IT EQ
0x00000026: 2001 . MOVEQ r0,#1
0x00000028: f7fffffe .... BL HAL_Delay
0x0000002c: 3e01 .> SUBS r6,#1
0x0000002e: d1f7 .. BNE {pc}-0xe ; 0x20
0x00000030: 3401 .4 ADDS r4,#1
0x00000032: 2c32 2, CMP r4,#0x32
0x00000034: d1f0 .. BNE {pc}-0x1c ; 0x18
O2#
引き続き、上記のループを複雑にします
for 内で宣言
0x00000014: 2500 .% MOVS r5,#0
0x00000016: bf00 .. NOP
0x00000018: 2402 .$ MOVS r4,#2
0x0000001a: 2632 2& MOVS r6,#0x32
0x0000001c: 07e8 .. LSLS r0,r5,#31
0x0000001e: bf08 .. IT EQ
0x00000020: 2401 .$ MOVEQ r4,#1
0x00000022: bf00 .. NOP
0x00000024: 4620 F MOV r0,r4
0x00000026: f7fffffe .... BL HAL_Delay
0x0000002a: 3e01 .> SUBS r6,#1
0x0000002c: d1fa .. BNE {pc}-0x8 ; 0x24
0x0000002e: 3501 .5 ADDS r5,#1
0x00000030: 2d32 2- CMP r5,#0x32
0x00000032: d1f1 .. BNE {pc}-0x1a ; 0x18
宣言を前に移動すると、両者は完全に一致します
0x00000014: 2500 .% MOVS r5,#0
0x00000016: bf00 .. NOP
0x00000018: 2402 .$ MOVS r4,#2
0x0000001a: 2632 2& MOVS r6,#0x32
0x0000001c: 07e8 .. LSLS r0,r5,#31
0x0000001e: bf08 .. IT EQ
0x00000020: 2401 .$ MOVEQ r4,#1
0x00000022: bf00 .. NOP
0x00000024: 4620 F MOV r0,r4
0x00000026: f7fffffe .... BL HAL_Delay
0x0000002a: 3e01 .> SUBS r6,#1
0x0000002c: d1fa .. BNE {pc}-0x8 ; 0x24
0x0000002e: 3501 .5 ADDS r5,#1
0x00000030: 2d32 2- CMP r5,#0x32
0x00000032: d1f1 .. BNE {pc}-0x1a ; 0x18
O3#
O3 は議論の価値がなく、ループを完全に展開します。
GCC 環境下の状況#
ローカル変数を事前に定義すると、同様に負の最適化が発生します
ローカル変数が for 内に定義されている場合、20 命令
ローカル変数を先に定義すると、24 命令
この記事は Mix Space によって xLog に同期更新されました
元のリンクは https://www.yono233.cn/posts/shoot/24_8_6_%E5%85%B3%E4%BA%8E%E5%B1%80%E9%83%A8%E5%8F%98%E9%87%8F%E7%9A%84%E6%A0%88%E8%A1%8C%E4%B8%BA%E2%80%94%E2%80%94%E7%94%B1%E5%BE%AA%E7%8E%AF%E8%AF%AD%E5%8F%A5%E5%86%85%E5%AE%9A%E4%B9%89%E5%BE%BE%E7%8E%AF%E5%8F%98%E9%87%8F%E5%BC%95%E7%94%B3