2012-07-27 29 views
1

的要求是像下面:效率使用位运算符

/* length must be >= 18 */ 

int calcActualLength(int length) { 
    int remainder = (length - 18) % 8; 
    if (remainder == 0) 
     return length; 
    return length + 8 - remainder; 
} 

使用位运算符,我可以重构一号线

int remainder = (length - 2) & 7; 

是否可以进一步优化?

+5

除非你的编译器死脑筋,否则几乎肯定会比单纯的凡人优化得更好:-) – paxdiablo 2012-07-27 08:49:35

+2

为什么你认为你甚至需要“优化”这个特定的函数?你有没有对其进行分析并确定性能问题? – 2012-07-27 08:55:27

+0

“优化”是什么意思?如果你的意思是提高运行时间,我怀疑你正在试图解决错误的问题,并且你会比编译器开关更好地运行,而不是试图微调函数。也就是说,如果你真的想优化这个功能,我怀疑你最好的选择是潜入汇编代码并在那里优化;你可能会修剪掉一两个多余的操作。您可能还会发现重构对生成的汇编代码没有任何影响。 – 2012-07-27 09:05:01

回答

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); 
} 

而且HELPER_calcActualLength()等于在语义ROUNDUP_8()当参数> = 0

而更简单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 
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(加载有效地址)instruciton经常被“滥用”,因为它能够计算简单的线性组合,如reg1 + size*reg2 + offset