我写了一个小测试,用gcc -O4优化编译。
这是来自此测试的 add_index_mod 和 add_index_if 实现:
void add_index_mod(int *p) {
*p = (*p + 1) % 10;
}
void add_index_if(int *p) {
if (*p == 9)
*p = 0;
else
(*p)++;
}
这就是我得到的add_index_mod:
mov eax, dword [rdi]
mov edx, 0x66666667
lea ecx, dword [rax + 1]
mov eax, ecx
imul edx
mov eax, ecx
sar eax, 0x1f
sar edx, 2
sub edx, eax
lea eax, dword [rdx + rdx*4]
add eax, eax
sub ecx, eax
mov dword [rdi], ecx
ret
在这里我们可以看到编译器将 div 替换为 mul、shifts 和 subs 的序列。这个技巧很好描述here。
这就是我得到的add_index_if:
mov edx, dword [rdi]
lea eax, dword [rdx + 1]
cmp edx, 9
mov edx, 0
cmove eax, edx
mov dword [rdi], eax
ret
这里没有什么特别的,只是 cmp 和条件 mov。
所以现在你可以尝试计算这两者的汇编代码的效率
使用此table 的函数。但这不是最好的方法,因为乱序执行、分支预测等。
所以正如我上面提到的,我只是写了一个小测试:
#include <stdio.h>
#include <stdint.h>
#define REPEATS (1 << 30)
static inline uint64_t rdtsc() {
unsigned int hi, lo;
__asm__ volatile("rdtsc" : "=a" (lo), "=d" (hi));
return ((uint64_t)hi << 32) | lo;
}
void add_index_mod(int *p) {
*p = (*p + 1) % 10;
}
void add_index_if(int *p) {
if (*p == 9)
*p = 0;
else
(*p)++;
}
int main() {
int p = 0;
uint32_t i;
uint64_t start, stop;
double delta, ticks_per_call;
// mod ================================
start = rdtsc();
for (i = 0; i < REPEATS; ++i) {
add_index_mod(&p);
}
stop = rdtsc();
// gcc with -O4 can remove above loop
// if we don't use its result so print it
printf("%d\n", p);
delta = (double)(stop - start);
ticks_per_call = delta / REPEATS;
printf("add_index_mod: %f\n", ticks_per_call);
// if ================================
start = rdtsc();
for (i = 0; i < REPEATS; ++i) {
add_index_if(&p);
}
stop = rdtsc();
printf("%d\n", p);
delta = (double)(stop - start);
ticks_per_call = delta / REPEATS;
printf("add_index_if: %f\n", ticks_per_call);
return 0;
}
这是我的英特尔酷睿 i5-6500 的输出:
add_index_mod: 9.643092
add_index_if: 2.063125
因此,在我的 CPU 上,add_index_if 的大量调用比 add_index_mod 快 5 倍。