我正在使用 PAPI 来计算 L1 缓存访问结果。大多数原生事件都会给出预期的结果,但是有一种情况是 L1_MISS 不精确。我有一个大小为 64 的对象和 100,000 个元素的易失性数组,如代码所示:
typedef struct _object{
int value;
char pad[60];
} object;
#define arr_size 100000
volatile object array [arr_size];
void * loop (int arg){
/* Threads are set in NUMA2 */
int temp;
for(int i=0; i < arr_size; i++){
temp = array[i].value;
}
}
我正在使用两个 NUMA 节点在 Skylake 处理器中进行测试。我禁用了预取器。使用 gcc -O3 编译。场景如下:从 NUMA1 中设置的主进程,我初始化一个数组并刷新缓存行。然后我创建 5 个线程,通过调用循环函数从 NUMA2 读取相同的数组。在所有这些都终止后,我从主进程循环遍历一个数组,读取每个元素并监控 L1 缓存访问结果:
int main(int argc, char* argv[]){
/* Main thread is set in NUMA1 */
/* Array is initialized and flushed from the cache*/
/* 5 threads are created with pthread_create, that call loop function,
and waited to finish by calling pthread_join*/
int tmp;
/*Hardware counters are counted for this loop*/
for(int i=0; i < arr_size; i++){
tmp = array[i].value;
}
}
我正在阅读这 5 个本机事件计数器:
MEM_INST_RETIRED.ALL_LOADS: 100095
L1D.REPLACEMENT: 100246
MEM_LOAD_RETIRED.L1_HIT: 113
MEM_LOAD_RETIRED.L1_MISS: 56
MEM_LOAD_RETIRED.FB_HIT: 55
期望看到 L1_MISS 大约为 100,000,因为未在缓存中获取元素,而 main 中的此读取会导致未命中。ALL_LOADS 也不等于三个计数器的总和:L1_HIT + L1_MISS + FB_HIT。尽管 L1D.REPLACEMENT 在这种情况下通过计算 L1D 数据线替换似乎是有意义的,但我不相信它,因为它在启用时也计算预取。
我不明白 MEM_LOAD_RETIRED.L1_MISS 计数器没有看到由 main 中的读取操作引起的事件的原因是什么,仅在这种特定情况下。例如,如果来自 NUMA2 的线程不是读取,而是修改一个数组元素,那么对于同一个循环,我会得到 L1_MISS:99818。所以任何建议都会有所帮助。我试图提供代码的主要框架。如果评论点的任何部分很重要,我也可以添加它们。