【方輝專欄】ARM嵌入式編譯器(五) 優(yōu)化循環(huán)的4種方法
2022-08-16
摘要: 本文主要對Arm Compiler 6編譯器的優(yōu)化循環(huán)對編寫優(yōu)化代碼的作用進(jìn)行介紹。關(guān)鍵字:Arm Compiler 6、編譯器、優(yōu)化循環(huán) 、循環(huán)展開、pragma、循環(huán)向量化、循環(huán)終止、無限循環(huán)、1. 循環(huán)展開循環(huán)執(zhí)行的時間取決于循環(huán)的次數(shù),循環(huán)中每次檢查是否進(jìn)行循環(huán)的條件會降低循環(huán)的性能。使用循環(huán)展開可以減少檢查條件的判斷次數(shù),但是展開循環(huán)就意味著增加代碼量。例如:在精確的時鐘周期循環(huán)中,可以使用#pragma unroll (n)來展開循環(huán)。“pragma”(編譯指示)僅在選擇優(yōu)化等級為-O2/-O3/-Ofast和-Omax時有效。 編譯指示的相關(guān)用法:#pragma unroll (n)展開n次循環(huán)#pragma unroll_completely展開所有循環(huán)注:雖然給出了循環(huán)展開的編譯指示,但Arm官方不建議使用,這樣會影響編譯器的展開優(yōu)化和其他循環(huán)優(yōu)化。不使用循環(huán)展開的代碼使用循環(huán)展開的代碼int countSetBits1(unsigned int n){ int bits = 0; while (n != 0) { if (n & 1) bits++; n >>= 1; } return bits;}int countSetBits2(unsigned int n){ int bits = 0; #pragma unroll (4) while (n != 0) { if (n & 1) bits++; n >>= 1; } return bits;}將代碼分別復(fù)制到file.c文件中,然后使用以下命令進(jìn)行編譯和反匯編。armclang --target=arm-arm-none-eabi -march=armv8-a file.c -O2 -S -o file.s不使用循環(huán)展開的匯編代碼使用循環(huán)展開的匯編代碼countSetBits1: mov r1, r0 mov r0, #0 cmp r1, #0 bxeq lr mov r2, #0 mov r0, #0.LBB0_1: and r3, r1, #1 cmp r2, r1, asr #1 add r0, r0, r3 lsr r3, r1, #1 mov r1, r3 bne .LBB0_1 bx lrcountSetBits2: mov r1, r0 mov r0, #0 cmp r1, #0 bxeq lr mov r2, #0 mov r0, #0LBB0_1: and r3, r1, #1 cmp r2, r1, asr #1 add r0, r0, r3 beq .LBB0_4@ BB#2: asr r3, r1, #1 cmp r2, r1, asr #2 and r3, r3, #1 add r0, r0, r3 asrne r3, r1, #2 andne r3, r3, #1 addne r0, r0, r3 cmpne r2, r1, asr #3 beq .LBB0_4@ BB#3: asr r3, r1, #3 cmp r2, r1, asr #4 and r3, r3, #1 add r0, r0, r3 asr r3, r1, #4 mov r1, r3 bne .LBB0_1.LBB0_4: bx lr可以看到展開循環(huán)時,代碼執(zhí)行會更快,但代碼量也更大。2. 循環(huán)向量化如果編譯的目標(biāo)含有SIMD單元,那么編譯器就可以使用向量引擎來優(yōu)化代碼的向量部分。在優(yōu)化等級為-O1,可以使用-fvectorize 來啟動優(yōu)化,而在-O2或更高等級時向量優(yōu)化是自動啟用。要使用向量優(yōu)化,在編寫代碼的時候需要將結(jié)構(gòu)體的成員放到同一個循環(huán)中,而不能使用獨(dú)立的循環(huán)。可以進(jìn)行SIMD優(yōu)化的代碼不能進(jìn)行SIMD優(yōu)化的代碼typedef struct tBuffer { int a; int b; int c;} tBuffer;tBuffer buffer[8];void DoubleBuffer1 (void){ int i; for (i=0; i<8; i++) { buffer[i].a *= 2; buffer[i].b *= 2; buffer[i].c *= 2; }}typedef struct tBuffer { int a; int b; int c;} tBuffer;tBuffer buffer[8];void DoubleBuffer2 (void){ int i; for (i=0; i<8; i++) buffer[i].a *= 2; for (i=0; i<8; i++) buffer[i].b *= 2; for (i=0; i<8; i++) buffer[i].c *= 2;}對于每個例子,將代碼分別復(fù)制到file.c文件中,然后使用以下命令進(jìn)行編譯和反匯編。armclang --target=arm-arm-none-eabi -march=armv8-a file.c -O2 -S -o file.s向量優(yōu)化后匯編代碼未進(jìn)行向量優(yōu)化的代碼DoubleBuffer1:.fnstart@ BB#0: movw r0, :lower16:buffer movt r0, :upper16:buffer vld1.64 {d16, d17}, [r0:128] mov r1, r0 vshl.i32 q8, q8, #1 vst1.32 {d16, d17}, [r1:128]! vld1.64 {d16, d17}, [r1:128] vshl.i32 q8, q8, #1 vst1.64 {d16, d17}, [r1:128] add r1, r0, #32 vld1.64 {d16, d17}, [r1:128] vshl.i32 q8, q8, #1 vst1.64 {d16, d17}, [r1:128] add r1, r0, #48 vld1.64 {d16, d17}, [r1:128] vshl.i32 q8, q8, #1 vst1.64 {d16, d17}, [r1:128] add r1, r0, #64 add r0, r0, #80 vld1.64 {d16, d17}, [r1:128] vshl.i32 q8, q8, #1 vst1.64 {d16, d17}, [r1:128] vld1.64 {d16, d17}, [r0:128] vshl.i32 q8, q8, #1 vst1.64 {d16, d17}, [r0:128] bxlrDoubleBuffer2: .fnstart@ BB#0: movw r0, :lower16:buffer movt r0, :upper16:buffer ldr r1, [r0] lsl r1, r1, #1 str r1, [r0] ldr r1, [r0, #12] lsl r1, r1, #1 str r1, [r0, #12] ldr r1, [r0, #24] lsl r1, r1, #1 str r1, [r0, #24] ldr r1, [r0, #36] lsl r1, r1, #1 str r1, [r0, #36] ldr r1, [r0, #48] lsl r1, r1, #1 str r1, [r0, #48] ldr r1, [r0, #60] lsl r1, r1, #1 str r1, [r0, #60] ldr r1, [r0, #72] lsl r1, r1, #1 str r1, [r0, #72] ldr r1, [r0, #84] lsl r1, r1, #1 str r1, [r0, #84] ldr r1, [r0, #4] lsl r1, r1, #1 str r1, [r0, #4] ldr r1, [r0, #16] lsl r1, r1, #1 ... bx lr在64位運(yùn)行狀態(tài)下要避免編譯器使用SIMD向量優(yōu)化可以在-march或-mcpu后+nosimd;例如:armclang --target=aarch64-arm-none-eabi -march=armv8-a+nosimd -O2 file.c -S -o file.s在32位運(yùn)行狀態(tài)下要避免編譯器使用SIMD向量優(yōu)化,可以通過設(shè)置-mfpu=fp-armv8;例如:armclang --target=aarch32-arm-none-eabi -march=armv8-a -mfpu=fp-armv8 -O2 file.c -S -o file.s3. 循環(huán)終止在寫循環(huán)的時候如果編寫不當(dāng)會使得代碼的運(yùn)行效率降低和代碼量增大。建議使用以下的終止條件:1)使用變量類型為:unsigned int2)使用向下減少的計數(shù)方式,以減到0作為計數(shù)結(jié)束。3)使用簡單的終止條件。單獨(dú)或組合使用以上原則的終止條件,可以獲得更好的代碼大小或效率。例如:這是一個實現(xiàn)n!的計算程序。遞增循環(huán)遞減循環(huán)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);}用以下命令反匯編以下armclang -Os -S --target=arm-arm-none-eabi -march=armv8-a遞增循環(huán)遞減循環(huán)fact1: mov r1, r0 mov r0, #1 cmp r1, #1 bxlt lr mov r2, #0.LBB0_1: add r2, r2, #1 mul r0, r0, r2 cmp r1, r2 bne .LBB0_1 bx lrfact2: mov r1, r0 mov r0, #1 cmp r1, #0 bxeq lr.LBB1_1: mul r0, r0, r1 subs r1, r1, #1 bne .LBB1_1 bx lr對比反匯編代碼可以看出在遞減循環(huán)中用SUBS指令代替了遞增循環(huán)中ADD 和CMP兩條指令。這是因為SUBS指令會自動更新Z標(biāo)志。此外在遞減循環(huán)中變量n不必再循環(huán)的過程實時使用,從而減少了寄存器的數(shù)量。如果終止條件是一個函數(shù),則循環(huán)的每次都調(diào)用該函數(shù),這種情況下遞減的循環(huán)優(yōu)勢就更明顯了。例如:for (...; i < get_limit(); ...);說明:這種遞減循環(huán)計數(shù)的方式也適用于while-do 命令。4. 無限循環(huán)在某些情況下armclang會刪除一些編譯器認(rèn)為沒有影響的無限循環(huán),從而導(dǎo)致最終程序無法正常運(yùn)行。為確保無限循環(huán)的正確編譯執(zhí)行,ARM官方建議在無限循環(huán)中添加__arm volatile的聲明。這個聲明的目的是告訴編譯器刪除這個無限循環(huán)會有影響,不能被優(yōu)化刪除。在無限循環(huán)中,把處理器設(shè)置為低功耗模式是一個不錯的做法,當(dāng)有中斷或事件觸發(fā)時再回到正常模式。下面是一個包含__arm volatile聲明的無限循環(huán)例子:void infinite_loop(void) {while (1)??__asm volatile("wfe");}注:wfe(Wait for Event)是給處理器一個提示,使處理器進(jìn)入低功耗狀態(tài),直到事件或中斷觸發(fā)。來源:《Arm? Compiler for Embedded User Guide Version 6.18》+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++關(guān)于億道電子億道電子技術(shù)有限公司(英文名稱:Emdoor Electronics Technology Co.,Ltd)是國內(nèi)資深的研發(fā)工具軟件提供商,公司成立于 2002 年,面向中國廣大的制造業(yè)客戶提供研發(fā)、設(shè)計、管理過程中使用的各種軟件開發(fā)工具,致力于幫助客戶提高研發(fā)管理效率、縮短產(chǎn)品設(shè)計周期,提升產(chǎn)品可靠性。20 年來,先后與 Altium、ARM、Ansys、QT、Adobe、Visu-IT、Minitab、Testplant、EPLAN、HighTec、GreenHills、PLS、Ashling、MSC Software 、Autodesk、Source Insight、TeamEDA、MicroFocus等多家全球知名公司建立戰(zhàn)略合作伙伴關(guān)系,并作為他們在中國區(qū)的主要分銷合作伙伴服務(wù)了數(shù)千家中國本土客戶,為客戶提供從芯片級開發(fā)工具、EDA 設(shè)計工具、軟件編譯以及測試工具、結(jié)構(gòu)設(shè)計工具、仿真工具、電氣設(shè)計工具、以及嵌入式 GUI 工具等等。億道電子憑借多年的經(jīng)驗積累,真正的幫助客戶實現(xiàn)了讓研發(fā)更簡單、更可靠、更高效的目標(biāo)。歡迎關(guān)注“億道電子”公眾號了解更多研發(fā)工具軟件知識
查看更多→