和我的一样。
有人在那篇文章下的讨论中得到了相同的结果。他说也许这只是 arr[i] *= 3 的真正 CPU 时间。
当 K = 1 时,它需要运行 SIZE 次。但是当 K = 2 时,只需要运行 SIZE/2 次。
因此,无论 K 是什么,您都可以重写代码以运行相同的时间。然后检查是否相同。我只是在打字的时候有这个想法。我稍后会试试这个。如果您尝试过,请添加评论。
这是我的代码:
#include <stdio.h>
#include <time.h>
#include <stdint.h>
#define SIZE 64*1024*1024
int32_t arr [SIZE];
struct timespec ts;
int main(int argc, char *argv[])
{
long i,j= 0;
long start;
long start_sec;
int count = 1;
int k = 0;
// init the arr;
for (i = 0; i< 64*1024*1024;++i){
arr[i] = 0;
}
for (j = 1; j< 1025;){
clock_gettime(CLOCK_REALTIME, &ts);
start = ts.tv_nsec;
start_sec = ts.tv_sec;
for (i = 0, k = 0; i< 64*1024*1024; i++, k+=j){
k = k & (SIZE -1);
arr[k] *=3;
arr[k] =1;
}
clock_gettime(CLOCK_REALTIME, &ts);
printf ("%d, %ld, %ld\n", count,(ts.tv_sec-start_sec)*1000000000+(ts.tv_nsec -start), j);
count++;
j *= 2;
}
return 0;
}
输出如下:
1, 352236657, 1
2, 356920027, 2
3, 375986006, 4
4, 494875602, 8
5, 957796009, 16
6, 1397285233, 32
7, 1784398514, 64
8, 1070586859, 128
9, 1130548756, 256
10, 1169113810, 512
11, 1312605482, 1024
如果我注释掉 arr-init 循环,当 K=1 时,它比 K=2 花费的时间更多。
我们可以看到,时间只是随着期望的增加而增加(在 K = 128 之前)。因为我们总是有一个 64*1024*1024 次的循环,尽管 K 越大。K 越大,缓存行刷新的时间就越多。
好吧,但我无法解释从 K = 64 到 K = 128 的减少。
并且@Mysticial讲到了lazy-malloc,所以我也对文章中的原代码做了一个实验,但是添加了arr-init循环来避免lazy-malloc的问题。K = 1 的成本确实降低了,但仍然大于 K=2 的成本,并且比原始版本更接近 K=2 的成本 * 2。数据如下:
1, 212882204, 1
2, 111660951, 2
3, 67843457, 4
4, 62980310, 8
5, 62092973, 16
6, 42531407, 32
7, 27686909, 64
8, 9142755, 128
9, 4064936, 256
10, 2342842, 512
11, 1130305, 1024
所以我认为从 K = 1 减少到 K = 2 和 K=2 到 K=4 的原因是迭代次数从 SIZE 减少到 SIZE/2。
这是我的想法,但我不确定。
==================================================== ====
我用 -Ox 编译了代码,减少消失了(但必须添加 arr-init 循环)。感谢@Basile。稍后我将检查 asm 代码中的差异。
这是asm代码之间的区别:
没有 O1,
movq $0, -32(%rbp)
jmp .L5
.L6:
movq -32(%rbp), %rax
movl arr(,%rax,4), %edx
movl %edx, %eax
addl %eax, %eax
addl %eax, %edx
movq -32(%rbp), %rax
movl %edx, arr(,%rax,4)
movq -24(%rbp), %rax
addq %rax, -32(%rbp)
.L5:
cmpq $67108863, -32(%rbp)
jle .L6
和 O1,
movl $0, %eax
.L3:
movl arr(,%rax,4), %ecx # ecx = a[i]
leal (%rcx,%rcx,2), %edx # edx = 3* rcx
movl %edx, arr(,%rax,4) # a[i] = edx
addq %rbx, %rax # rax += rbx
cmpq $67108863, %rax
jle .L3
我将没有 O1 的 asm 代码更改为此,
movq $0, -32(%rbp)
movl $0, %eax
movq -24(%rbp), %rbx
jmp .L5
.L6:
movl arr(,%rax,4), %edx
movl %edx, %ecx
addl %ecx, %ecx
addl %ecx, %edx
movl %edx, arr(,%rax,4)
addq %rbx, %rax
.L5:
cmpq $67108863, %rax
jle .L6
然后我得到结果:
1, 64119476, 1
2, 63417463, 2
3, 63732534, 4
4, 66703562, 8
5, 65740635, 16
6, 47743618, 32
7, 28402013, 64
8, 9444894, 128
9, 4544371, 256
10, 2991025, 512
11, 1242882, 1024
它几乎和O1一样。似乎 movq -32(%rbp), %rax 的东西成本太高了。但我不知道为什么。
也许我最好问一个关于它的新问题。