取决于循环的迭代次数,完成循环可能需要花费大量时间,此外,每次迭代时,需要判断循环条件是否成立,这一操作也会降低循环的性能。
1 循环展开-Loop unrolling
为了减少每次循环都需要判断迭代条件带来的性能影响,用户可以将循环展开,以减少判断循环条件的次数。使用 #pragma unroll (<n>) 来展开用户代码中对时间、性能比较敏感的循环。然而,将循环展开也有一个缺点:增加了代码量。下表中的操作仅对 -O2
, -O3
, -Ofast
, 以及-Omax优化时有效果:
Pragma | Description |
---|---|
#pragma unroll (<n>) | 展开循环的 n 次迭代 |
#pragma unroll_completely | 展开循环中的所有迭代 |
具体的使用方法见:
#pragma unroll[(n)], #pragma unroll_completelyhttps://developer.arm.com/documentation/101754/0620/armclang-Reference/Compiler-specific-Pragmas/-pragma-unroll--n-----pragma-unroll-completely 需要注意的是:手动在源代码中将循环展开和使用 #pragma unroll (<n>) 的效果是不一样的,手动在源代码中展开循环可能会阻碍编译器对循环的优化操作,ARM建议用户使用 #pragma unroll (<n>) 。如果没有指定 展开的迭代次数 n, 将默认展开循环中的所有迭代。此外,如果编译器无法计算出迭代的次数,使用#pragma unroll_completely,在编译时将不会进行循环展开。
比如有以下示例代码:
int countSetBits1(unsigned int n)
{
int bits = 0;
while (n != 0)
{
if (n & 1) bits++;
n >>= 1;
}
return bits;
}
使用如下命令进行编译:
armclang --target=arm-arm-none-eabi -march=armv8-a file.c -O2 -S -o file.s
默认情况下,将得到如下汇编代码:
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 lr
如果使用循环展开,