要求如下:
/* length must be >= 18 */
int calcActualLength(int length) {
int remainder = (length - 18) % 8;
if (remainder == 0)
return length;
return length + 8 - remainder;
}
使用逐位运算符,我可以重构第1行
int remainder = (length - 2) & 7;
可以进一步优化吗?
答案 0 :(得分:2)
((length+5)&~7)+2
int calcActualLength(int length) {
int remainder = (length - 18) % 8;
if (remainder == 0)
return length;
return length + 8 - remainder;
}
==>
int HELPER_calcActualLength(int length) {
int remainder = length % 8;
if (remainder == 0)
return length;
return length + 8 - remainder;
}
int calcActualLength(int length) {
return 18 + HELPER_calcActualLength(length - 18);
}
当参数> = 0时,HELPER_calcActualLength()
等于ROUNDUP_8()
语义
更简单的ROUNDUP_8()可以是:
#define ROUNDUP_8(x) (((x)+7)&~7)
int calcActualLength(int length) {
return 18 + ROUNDUP_8(length - 18);
}
==> 2 + ROUNDUP_8(length - 18 + 16);
==> 2 + ROUNDUP_8(length - 2);
==> 2 + (((length - 2)+7)&~7)
==> ((length+5)&~7)+2
答案 1 :(得分:2)
使用gcc -O3
进行编译时,原始代码会生成以下64位程序集:
movl %edi, %eax
leal -18(%rax), %ecx
movl %ecx, %edx
sarl $31, %edx
shrl $29, %edx
addl %edx, %ecx
andl $7, %ecx
subl %edx, %ecx
je .L2
addl $8, %eax
subl %ecx, %eax
.L2:
rep
正如您对问题的评论中所建议的那样,将参数更改为unsigned int
可以更好地进行优化并导致以下汇编:
leal -18(%rdi), %edx
movl %edi, %eax
andl $7, %edx
je .L3
leal 8(%rdi), %eax
subl %edx, %eax
.L3:
rep
可以通过添加8
并使用7
屏蔽来累计到~7
的倍数。它的工作方式如下:如果最后三位不是全为零,则将7
加载到第4位,否则不会发生进位。所以你的功能可以简化为:
return (((length - 18) + 7) & ~7) + 18;
或更简单:
return ((length - 11) & ~7) + 18;
GCC简单编译最后一行:
leal -11(%rdi), %eax
andl $-8, %eax
addl $18, %eax
请注意,lea
(加载有效地址)说明因其计算reg1 + size*reg2 + offset
等简单线性组合的能力而经常被“滥用”