我想写一个程序来获取我的缓存大小(L1,L2,L3)。我知道它的一般想法。
所以我写了一个小程序。 这是我的代码:
#include <cstdio>
#include <time.h>
#include <sys/mman.h>
const int KB = 1024;
const int MB = 1024 * KB;
const int data_size = 32 * MB;
const int repeats = 64 * MB;
const int steps = 8 * MB;
const int times = 8;
long long clock_time() {
struct timespec tp;
clock_gettime(CLOCK_REALTIME, &tp);
return (long long)(tp.tv_nsec + (long long)tp.tv_sec * 1000000000ll);
}
int main() {
// allocate memory and lock
void* map = mmap(NULL, (size_t)data_size, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
if (map == MAP_FAILED) {
return 0;
}
int* data = (int*)map;
// write all to avoid paging on demand
for (int i = 0;i< data_size / sizeof(int);i++) {
data[i]++;
}
int steps[] = { 1*KB, 4*KB, 8*KB, 16*KB, 24 * KB, 32*KB, 64*KB, 128*KB,
128*KB*2, 128*KB*3, 512*KB, 1 * MB, 2 * MB, 3 * MB, 4 * MB,
5 * MB, 6 * MB, 7 * MB, 8 * MB, 9 * MB};
for (int i = 0; i <= sizeof(steps) / sizeof(int) - 1; i++) {
double totalTime = 0;
for (int k = 0; k < times; k++) {
int size_mask = steps[i] / sizeof(int) - 1;
long long start = clock_time();
for (int j = 0; j < repeats; j++) {
++data[ (j * 16) & size_mask ];
}
long long end = clock_time();
totalTime += (end - start) / 1000000000.0;
}
printf("%d time: %lf\n", steps[i] / KB, totalTime);
}
munmap(map, (size_t)data_size);
return 0;
}
然而,结果很奇怪:
1 time: 1.989998
4 time: 1.992945
8 time: 1.997071
16 time: 1.993442
24 time: 1.994212
32 time: 2.002103
64 time: 1.959601
128 time: 1.957994
256 time: 1.975517
384 time: 1.975143
512 time: 2.209696
1024 time: 2.437783
2048 time: 7.006168
3072 time: 5.306975
4096 time: 5.943510
5120 time: 2.396078
6144 time: 4.404022
7168 time: 4.900366
8192 time: 8.998624
9216 time: 6.574195
我的CPU是Intel(R)Core(TM)i3-2350M。 L1缓存:32K(用于数据),L2缓存256K,L3缓存3072K。 好像它没有遵循任何规则。我无法从中获取缓存大小或缓存级别的信息。 有人可以帮忙吗?提前谢谢。
更新
关注@Leeor建议,我使用j*64
代替j*16
。新结果:
1 time: 1.996282
4 time: 2.002579
8 time: 2.002240
16 time: 1.993198
24 time: 1.995733
32 time: 2.000463
64 time: 1.968637
128 time: 1.956138
256 time: 1.978266
384 time: 1.991912
512 time: 2.192371
1024 time: 2.262387
2048 time: 3.019435
3072 time: 2.359423
4096 time: 5.874426
5120 time: 2.324901
6144 time: 4.135550
7168 time: 3.851972
8192 time: 7.417762
9216 time: 2.272929
10240 time: 3.441985
11264 time: 3.094753
两个峰,4096K和8192K。仍然很奇怪。
答案 0 :(得分:6)
我不确定这是否是唯一的问题,但它肯定是最大的问题 - 您的代码会很快触发HW流预取程序,使您几乎总是遇到L1或L2延迟。
对于您的基准测试您应该禁用它们(通过BIOS或任何其他方式),或者至少通过替换j*16
(*每个int 4个字节= 64B,一个缓存行 - 一个经典单元)来延长步数使用j*64
(4个缓存行)来寻找流检测器。原因是 - 预取器可以为每个流请求发出2个预取,因此当你执行单元步长时,它会在你的代码之前运行,当你的代码跳过2行时可能仍然领先于你,但是在更长时间内变得无用跳跃(3因为你的modulu不好,你需要一个step_size的分隔符)
使用新结果更新问题,我们可以确定这里是否有其他内容。
EDIT1 : 好的,我运行了固定代码并得到了 -
1 time: 1.321001
4 time: 1.321998
8 time: 1.336288
16 time: 1.324994
24 time: 1.319742
32 time: 1.330685
64 time: 1.536644
128 time: 1.536933
256 time: 1.669329
384 time: 1.592145
512 time: 2.036315
1024 time: 2.214269
2048 time: 2.407584
3072 time: 2.259108
4096 time: 2.584872
5120 time: 2.203696
6144 time: 2.335194
7168 time: 2.322517
8192 time: 5.554941
9216 time: 2.230817
如果你忽略几列你会更有意义 - 你跳过32k(L1大小),但不是在256k(L2大小)之后跳跃,我们得到384的结果太好了,只跳到512K。最后一次跳跃是8M(我的LLC大小),但9k再次被打破。
这允许我们发现下一个错误 - 只有大小掩码的ANDing才有意义,当它是2的幂时,否则你不会回绕,而是重复一些最后的地址(最终会产生乐观的结果)因为它在缓存中很新鲜。)
尝试将... & size_mask
替换为% steps[i]/sizeof(int)
,modulu更贵,但如果你想拥有这些尺寸,你需要它(或者,只要它超过当前尺寸就会变为零的运行索引)
答案 1 :(得分:4)
我认为你最好看一下CPUID指令。这不是微不足道的,但网上应该有信息。
此外,如果您使用的是Windows,则可以使用GetLogicalProcessorInformation功能。请注意,它仅出现在Windows XP SP3及更高版本中。我对Linux / Unix一无所知。
答案 2 :(得分:2)
如果您正在使用GNU / Linux,您只需阅读 / proc / cpuinfo 文件的内容,并了解更多详情 / sys / devices / system / cpu / { {1}} 的。在UNIX下常见的不是定义API,普通文件无论如何都可以完成这项工作。
我还要看一下 util-linux 的来源,它包含一个名为 lscpu 的程序。这应该是一个如何检索所需信息的例子。
//更新
http://git.kernel.org/cgit/utils/util-linux/util-linux.git/tree/sys-utils/lscpu.c
如果只是看看他们的来源。它基本上是从上面提到的文件中读取的,就是这样。因此,从文件中读取它们绝对有效,它们由内核提供。