只需对a、b 和c 使用普通的ints 即可将代码更改为更简单的代码
if (a >= b - c && a <= b + c) ...
另外,作为替代方案,256*256*256 仅为 16M,而 16M 位的映射为 2 MBytes。这意味着使用类似的查找表是可行的
int index = (a<<16) + (b<<8) + c;
if (lookup_table[index>>3] & (1<<(index&7))) ...
但我认为即使现代处理器讨厌条件,缓存垃圾也会使这个速度变慢......
另一种选择是使用一点代数
b - c <= a <= b + c
iff
- c <= a - b <= c (subtracted b from all terms)
iff
0 <= a - b + c <= 2*c (added c to all terms)
这允许只使用一个测试
if ((unsigned)(a - b + c) < 2*c) ...
假设a、b 和c 是普通的ints。原因是如果a - b + c 是负数,那么无符号算术会使它比2*c 大得多(如果c 是0..255)。
如果处理器具有专用的有符号/无符号比较指令,如 x86 (ja/jg),这应该会生成具有单个分支的高效机器代码。
#include <stdio.h>
int main()
{
int err = 0;
for (int ia=0; ia<256; ia++)
for (int ib=0; ib<256; ib++)
for (int ic=0; ic<256; ic++)
{
unsigned char a = ia;
unsigned char b = ib;
unsigned char c = ic;
int res1 = (a >= ( b - c > 0 ? b - c : 0 ) &&
a <= ( b + c < 255 ? b + c : 255 ));
int res2 = (unsigned(a - b + c) <= 2*c);
err += (res1 != res2);
}
printf("Errors = %i\n", err);
return 0;
}
在使用 g++ 的 x86 上,为 res2 测试生成的汇编代码仅包含一条条件指令。
下面循环的汇编代码是
void process(unsigned char *src, unsigned char *dst, int sz)
{
for (int i=0; i<sz; i+=3)
{
unsigned char a = src[i];
unsigned char b = src[i+1];
unsigned char c = src[i+2];
dst[i] = (unsigned(a - b + c) <= 2*c);
}
}
.L3:
movzbl 2(%ecx,%eax), %ebx ; This loads c
movzbl (%ecx,%eax), %edx ; This loads a
movzbl 1(%ecx,%eax), %esi ; This loads b
leal (%ebx,%edx), %edx ; This computes a + c
addl %ebx, %ebx ; This is c * 2
subl %esi, %edx ; This is a - b + c
cmpl %ebx, %edx ; Comparison
setbe (%edi,%eax) ; Set 0/1 depending on result
addl $3, %eax ; next group
cmpl %eax, 16(%ebp) ; did we finish ?
jg .L3 ; if not loop back for next
使用 dst[i] = (a<b ? b-a : a-b); 代码变得更长
.L9:
movzbl %dl, %edx
andl $255, %esi
subl %esi, %edx
.L4:
andl $255, %edi
cmpl %edi, %edx
movl 12(%ebp), %edx
setle (%edx,%eax)
addl $3, %eax
cmpl %eax, 16(%ebp)
jle .L6
.L5:
movzbl (%ecx,%eax), %edx
movb %dl, -13(%ebp)
movzbl 1(%ecx,%eax), %esi
movzbl 2(%ecx,%eax), %edi
movl %esi, %ebx
cmpb %bl, %dl
ja .L9
movl %esi, %ebx
movzbl %bl, %edx
movzbl -13(%ebp), %ebx
subl %ebx, %edx
jmp .L4
.p2align 4,,7
.p2align 3
.L6:
我现在太累了,无法尝试破译它(这里是凌晨 2:28)
无论如何,更长并不意味着一定要更慢(乍一看,g++ 决定在这种情况下一次写入几个元素来展开循环)。
正如我之前所说,您应该对您的真实计算和真实数据进行一些实际分析。请注意,如果需要真正的性能,最佳策略可能会因处理器而异。
例如,Linux 在引导期间会进行 ae 测试,以确定执行内核所需的特定计算的更快方法。变量太多(缓存大小/级别、内存速度、cpu 时钟、芯片组、cpu 类型...)。