Je travaille sur un projet où nous devons implémenter un algorithme dont la compatibilité avec le cache a été démontrée. En termes simples, si N
est l'entrée et B
est le nombre d'éléments qui sont transférés entre le cache et la RAM chaque fois que nous avons manqué un cache, l'algorithme nécessitera O(N/B)
accès à la RAM.Pourquoi Perf et Papi attribuent-ils des valeurs différentes aux références et échecs du cache L3?
Je voudrais montrer que c'est effectivement le comportement en pratique. Pour mieux comprendre comment on peut mesurer différents compteurs matériels liés au cache, j'ai décidé d'utiliser différents outils. L'un est Perf et l'autre est la bibliothèque PAPI. Malheureusement, plus je travaille avec ces outils, moins je comprends ce qu'ils font exactement. J'utilise un processeur Intel (R) Core (TM) i5-3470 @ 3,20 GHz avec 8 Go de RAM, cache L1 256 Ko, cache L2 1 Mo, cache L3 6 Mo. La taille de la ligne de cache est de 64 octets. Je suppose que cela doit être la taille du bloc B
.
Regardons l'exemple suivant:
#include <iostream>
using namespace std;
struct node{
int l, r;
};
int main(int argc, char* argv[]){
int n = 1000000;
node* A = new node[n];
int i;
for(i=0;i<n;i++){
A[i].l = 1;
A[i].r = 4;
}
return 0;
}
Chaque nœud nécessite 8 octets, ce qui signifie qu'une ligne de cache peut s'adapter à 8 nœuds, donc je devrais être attendre environ 1000000/8 = 125000
cache L3 misses.
Sans optimisation (pas -O3
), c'est la sortie de perf:
perf stat -B -e cache-references,cache-misses ./cachetests
Performance counter stats for './cachetests':
162,813 cache-references
142,247 cache-misses # 87.368 % of all cache refs
0.007163021 seconds time elapsed
Il est assez proche de ce que nous attendons. Supposons maintenant que nous utilisions la bibliothèque PAPI.
#include <iostream>
#include <papi.h>
using namespace std;
struct node{
int l, r;
};
void handle_error(int err){
std::cerr << "PAPI error: " << err << std::endl;
}
int main(int argc, char* argv[]){
int numEvents = 2;
long long values[2];
int events[2] = {PAPI_L3_TCA,PAPI_L3_TCM};
if (PAPI_start_counters(events, numEvents) != PAPI_OK)
handle_error(1);
int n = 1000000;
node* A = new node[n];
int i;
for(i=0;i<n;i++){
A[i].l = 1;
A[i].r = 4;
}
if (PAPI_stop_counters(values, numEvents) != PAPI_OK)
handle_error(1);
cout<<"L3 accesses: "<<values[0]<<endl;
cout<<"L3 misses: "<<values[1]<<endl;
cout<<"L3 miss/access ratio: "<<(double)values[1]/values[0]<<endl;
return 0;
}
Ceci est la sortie que je reçois:
L3 accesses: 3335
L3 misses: 848
L3 miss/access ratio: 0.254273
Pourquoi une telle différence entre les deux outils?
Avez glissement annuel essayé de compter les misses de données en utilisant PAPI_L3_DCA et PAPI_L3_DCM? – HazemGomaa
seulement PAPI_L3_DCA est disponible et il semble donner autour des mêmes numéros – jsguy