2

我刚刚在这里阅读了一篇博文并尝试做类似的事情,这是我检查示例 1 和 2 中的内容的代码:

int doSomething(long numLoop,int cacheSize){
    long k;
    int arr[1000000];
    for(k=0;k<numLoop;k++){
        int i;
        for  (i = 0; i < 1000000; i+=cacheSize) arr[i] = arr[i];
    }
}

如博文所述,doSomething(1000,2) 和 doSomething(1000,1) 的执行时间应该差不多,但我分别得到了 2.1s 和 4.3s。谁能帮我解释一下?谢谢你。

更新 1:我刚刚将数组的大小增加到了 100 倍

int doSomething(long numLoop,int cacheSize){
    long k;
    int * buffer;
    buffer = (int*) malloc (100000000 * sizeof(int));
    for(k=0;k<numLoop;k++){
        int i;
        for  (i = 0; i < 100000000; i+=cacheSize) buffer[i] = buffer[i];
    }
}

不幸的是,doSomething(10,2) 和 doSomething(10,1) 的执行时间仍然相差很大:3.02s 和 5.65s。任何人都可以在你的机器上测试这个吗?

4

2 回答 2

2

使用 doSomething(1000, 2),您正在执行的内部循环是使用 doSomething(1000,1) 时计数的一半。

您的内部循环按 cacheSize 递增,因此值为 2 的迭代次数仅为值为 1 的一半。该数组的大小约为 4MB,这是典型的虚拟内存页面大小。

实际上我有点惊讶的是,一个好的编译器不会优化这个循环,因为没有变量变化。这可能是问题的一部分。

于 2012-09-27T01:06:39.093 回答
2

您的 4M 数组大小不够大。整个数组适合缓存(并且在第一个k循环之后在缓存中),因此时序由指令执行决定。如果您制作arr的缓存大小远大于缓存大小,您将开始看到预期的效果。

(当你arr比缓存更大时,你会看到一个额外的效果:运行时间应该随着arr大小线性增加,直到你超过缓存,当你会看到性能拐点并且它会突然变得更糟并且运行时间将以新的线性比例增加)

编辑:我尝试了您的第二个版本,并进行了以下更改:

  1. 更改以volatile int *buffer确保buffer[i] = buffer[i]不会被优化掉。
  2. 编译-O2以确保循环得到充分优化,以防止循环开销占主导地位。

当我尝试得到几乎相同的时间时:

kronos /tmp $ time ./dos 2
./dos 2  1.65s user 0.29s system 99% cpu 1.947 total
kronos /tmp $ time ./dos 1
./dos 1  1.68s user 0.25s system 99% cpu 1.926 total

在这里,您可以看到将步幅设为两个完整缓存行的效果:

kronos /tmp $ time ./dos 16
./dos 16  1.65s user 0.28s system 99% cpu 1.926 total
kronos /tmp $ time ./dos 32
./dos 32  1.06s user 0.30s system 99% cpu 1.356 total
于 2012-09-27T01:16:33.923 回答