我正在使用 PAPI 库来读取硬件计数器。我注意到调用 PAPI_library_init(PAPI_VER_CURRENT) 初始化的顺序对我得到的结果有影响。我对数组的初始化和读取是这样的:
int retval;
/*
retval = PAPI_library_init(PAPI_VER_CURRENT);
if (retval != PAPI_VER_CURRENT) {
fprintf(stderr, "PAPI library init error!\n");
exit(1);
}
*/
for(int i=0; i < arr_size; i++){
array[i].value = 1;
//_mm_clflush(&array[i]); flushing does not make difference.
}
_mm_mfence();
for(int i=0; i < arr_size; i++){
temp = array[i].value ;
}
_mm_mfence();
retval = PAPI_library_init(PAPI_VER_CURRENT);
if (retval != PAPI_VER_CURRENT) {
fprintf(stderr, "PAPI library init error!\n");
exit(1);
}
我相信第二个循环读取数组的必要性是为了一致性协议,但在这里应该没什么大不了的。在此之后,我将 MEM_LOAD_RETIRED 的本机事件添加到要读取的事件集中,并在第三个循环周围使用 PAPI_read(我在循环之前和之后读取它,最后打印差异):
for(int i=0; i < arr_size; i++){
temp = array[i].value ;
}
其中 arr_size 为 1000,数组的每个元素为 64 字节大小(等于缓存行)。我已禁用所有预取器。我使用 gcc -O3 标志进行编译以进行优化和 -lpapi 库。使用此代码,对于第三个循环,我得到:
L1_HIT:64,L1_MISS:1011,L2_HIT:15,L2_MISS:996。
但是,如果我在数组初始化之前取消注释 PAPI_library_init 并在之后注释它,我得到的结果是:
L1_HIT:73,L1_MISS:1004,L2_HIT:990,L2_MISS:14。
我正在 Skylake 服务器中对此进行测试,缓存大小为:
L1d cache: 32K
L1i cache: 32K
L2 cache: 1024K
L3 cache: 22528K
现在我有点困惑为什么 papi 初始化会影响这个结果。这是 L2 的命中和错过的变化。我只需要第三个循环,我相信前两个循环对计数器的影响没有被考虑在内。
因此,任何提示都会有所帮助,因为所有文档都说:“PAPI_library_init() 初始化 PAPI 库。必须在使用任何低级 PAPI 函数之前调用它。如果您的应用程序正在使用线程 PAPI_thread_init (3 ) 也必须在调用除 PAPI_library_init() 之外的库之前调用。"