我正在通过以下示例了解缓存的工作原理:
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
typedef uint32_t data_t;
const int U = 10000000; // size of the array. 10 million vals ~= 40MB
const int N = 100000000; // number of searches to perform
int main() {
data_t* data = (data_t*) malloc(U * sizeof(data_t));
if (data == NULL) {
free(data);
printf("Error: not enough memory\n");
exit(-1);
}
// fill up the array with sequential (sorted) values.
int i;
for (i = 0; i < U; i++) {
data[i] = i;
}
printf("Allocated array of size %d\n", U);
printf("Summing %d random values...\n", N);
data_t val = 0;
data_t seed = 42;
for (i = 0; i < N; i++) {
int l = rand_r(&seed) % U;
val = (val + data[l]);
}
free(data);
printf("Done. Value = %d\n", val);
return 0;
}
使用perf record./sum和perf report找到的慢速随机访问循环的相关注释如下
0.05 │ mov -0x18(%rbp),%eax ▒
0.07 │ mov -0x10(%rbp),%rcx ▒
│ movslq -0x20(%rbp),%rdx ▒
0.03 │ add (%rcx,%rdx,4),%eax ▒
95.39 │ mov %eax,-0x18(%rbp) ▒
1.34 │ mov -0x14(%rbp),%eax ▒
│ add $0x1,%eax ◆
│ mov %eax,-0x14(%rbp)
此时,-0x18
持有val
,-0x10
持有data
,-0x14
持有i
,-0x20
持有l
.左栏中的数字显示花在该指令上的时间百分比.我本以为这条线
add (%rcx, %rdx, 4), %eax
将占用最多的时间,因为它必须为data[l]
(仅为(%rcx, %rdx, 4)
)执行随机访问加载.这应该只在大约16k/U=0.16%的情况下在L1缓存中,因为我的L1缓存的大小是64k字节,或16k整数.因此,这一操作应该是缓慢的.相反,看起来很慢的操作只是从寄存器%eax
移到val
,该寄存器被如此频繁地使用,以至于它肯定在高速缓存中.有人能解释一下这是怎么回事吗?