实验最快
#include <stdio.h>
#include <stdint.h>
inline uint64_t rdtsc(void)
{
uint32_t hi, lo;
asm volatile ("rdtsc" : "=a"(lo), "=d"(hi));
return ((uint64_t)lo) | (((uint64_t)hi)<<32);
}
uint64_t calc(uint64_t outer, uint64_t inner)
{
uint64_t s = 0;
for(uint64_t i = 1; i <= outer; ++i){
for(uint64_t j = 1; j <= inner; ++j){
s += i*j;
}
}
return s;
}
int main()
{
uint64_t t0 = rdtsc();
uint64_t sum1 = calc(100ul, 1000000ul);
uint64_t t1 = rdtsc();
uint64_t sum2 = calc(1000000ul, 100ul);
uint64_t t2 = rdtsc();
printf("sum1 = %lu, used %lu cycles\n", sum1, t1-t0);
printf("sum2 = %lu, used %lu cycles\n", sum2, t2-t1);
return 0;
}
使用 gcc 6.3 用 -O2 编译
执行环境 Linux 4.9.18 with Intel Core i5-7260U
结果:
sum1 = 2525002525000000, used 356 cycles
sum2 = 2525002525000000, used 2633286 cycles
造成这样差异的原因,是 gcc 把 calc() 函式直接 inline
然后展开内层循环的运算结果
第一个 function call 转为 asm 后长这样:
movabsq $500000499999, %rdx
movq %rdx, %rdi
.p2align 4,,10
.p2align 3
.L15:
leaq (%rdx,%rax), %rcx
addq $1, %rax
addq %rdi, %rdx
addq %rcx, %rsi
cmpq $101, %rax
jne .L15
看起来有点复杂,翻回 C 就是这样:
rax = 1;
rdx = 500000499999;
do{
rcx = rdx + rax;
rdx += 500000499999;
sum += rcx;
rax++;
}while(rax != 101);
我不知道为什么不直接加 500000500000 就好,不过这不是重点
重点是这样循环只跑了 100 次
相较于第二个 function call,同样的展开方式循环会跑 1000000 次
当然是第一种方式比较快,而且也很刚好差了一万倍左右
不过这一切都取决于 compiler 最佳化
未来会不会不管怎么呼叫都直接在 compile time 算出答案给你,其实很有可能
到时候两种作法就一样快了