La Mesure De Cache Latences

Donc je suis en train d'essayer de mesurer les temps de latence de L1, L2, L3 cache à l'aide de C. je sais que la taille d'eux et j'ai l'impression de comprendre conceptuellement comment faire, mais je suis en cours d'exécution dans des problèmes avec ma mise en œuvre. Je me demande si certains des autres matériels subtilités comme la pré-extraction sont à l'origine de problèmes.

#include <time.h>
#include <stdio.h>
#include <string.h>
int main(){
srand(time(NULL));  //Seed ONCE
const int L1_CACHE_SIZE =  32768/sizeof(int);
const int L2_CACHE_SIZE =  262144/sizeof(int);
const int L3_CACHE_SIZE =  6587392/sizeof(int);
const int NUM_ACCESSES = 1000000;
const int SECONDS_PER_NS = 1000000000;
int arrayAccess[L1_CACHE_SIZE];
int arrayInvalidateL1[L1_CACHE_SIZE];
int arrayInvalidateL2[L2_CACHE_SIZE];
int arrayInvalidateL3[L3_CACHE_SIZE];
int count=0;
int index=0;
int i=0;
struct timespec startAccess, endAccess;
double mainMemAccess, L1Access, L2Access, L3Access;
int readValue=0;
memset(arrayAccess, 0, L1_CACHE_SIZE*sizeof(int));
memset(arrayInvalidateL1, 0, L1_CACHE_SIZE*sizeof(int));
memset(arrayInvalidateL2, 0, L2_CACHE_SIZE*sizeof(int));
memset(arrayInvalidateL3, 0, L3_CACHE_SIZE*sizeof(int));
index = 0;
clock_gettime(CLOCK_REALTIME, &startAccess); //start clock
while (index < L1_CACHE_SIZE) {
int tmp = arrayAccess[index];               //Access Value from L2
index = (index + tmp + ((index & 4) ? 28 : 36));   //on average this should give 32 element skips, with changing strides
count++;                                           //divide overall time by this 
}
clock_gettime(CLOCK_REALTIME, &endAccess); //end clock
mainMemAccess = ((endAccess.tv_sec - startAccess.tv_sec) * SECONDS_PER_NS) + (endAccess.tv_nsec - startAccess.tv_nsec);
mainMemAccess /= count;
printf("Main Memory Access %lf\n", mainMemAccess);
index = 0;
count=0;
clock_gettime(CLOCK_REALTIME, &startAccess); //start clock
while (index < L1_CACHE_SIZE) {
int tmp = arrayAccess[index];               //Access Value from L2
index = (index + tmp + ((index & 4) ? 28 : 36));   //on average this should give 32 element skips, with changing strides
count++;                                           //divide overall time by this 
}
clock_gettime(CLOCK_REALTIME, &endAccess); //end clock              
L1Access = ((endAccess.tv_sec - startAccess.tv_sec) * SECONDS_PER_NS) + (endAccess.tv_nsec - startAccess.tv_nsec);
L1Access /= count;
printf("L1 Cache Access %lf\n", L1Access);
//invalidate L1 by accessing all elements of array which is larger than cache
for(count=0; count < L1_CACHE_SIZE; count++){
int read = arrayInvalidateL1[count]; 
read++;
readValue+=read;               
}
index = 0;
count = 0;
clock_gettime(CLOCK_REALTIME, &startAccess); //start clock
while (index < L1_CACHE_SIZE) {
int tmp = arrayAccess[index];               //Access Value from L2
index = (index + tmp + ((index & 4) ? 28 : 36));   //on average this should give 32 element skips, with changing strides
count++;                                           //divide overall time by this 
}
clock_gettime(CLOCK_REALTIME, &endAccess); //end clock
L2Access = ((endAccess.tv_sec - startAccess.tv_sec) * SECONDS_PER_NS) + (endAccess.tv_nsec - startAccess.tv_nsec);
L2Access /= count;
printf("L2 Cache Acces %lf\n", L2Access);
//invalidate L2 by accessing all elements of array which is larger than cache
for(count=0; count < L2_CACHE_SIZE; count++){
int read = arrayInvalidateL2[count];  
read++;
readValue+=read;                        
}
index = 0;
count=0;
clock_gettime(CLOCK_REALTIME, &startAccess); //sreadValue+=read;tart clock
while (index < L1_CACHE_SIZE) {
int tmp = arrayAccess[index];               //Access Value from L2
index = (index + tmp + ((index & 4) ? 28 : 36));   //on average this should give 32 element skips, with changing strides
count++;                                           //divide overall time by this 
}
clock_gettime(CLOCK_REALTIME, &endAccess); //end clock
L3Access = ((endAccess.tv_sec - startAccess.tv_sec) * SECONDS_PER_NS) + (endAccess.tv_nsec - startAccess.tv_nsec);
L3Access /= count;
printf("L3 Cache Access %lf\n", L3Access);
printf("Read Value: %d", readValue);
}

Je commence par l'accès à une valeur dans le tableau, je veux données. Cela devrait évidemment venir de la mémoire principale, car il en est le premier accès. Le tableau est de petite taille (moins de la taille de la page), donc il doit être copié en L1, L2, L3. - Je accéder à la valeur à partir de la même matrice qui doit maintenant être en L1. J'ai ensuite accéder à toutes les valeurs d'un tableau de la même taille que le cache L1 d'invalider les données que je veux d'accès (donc, maintenant, il faut juste être dans L2/3). Puis-je répéter ce processus pour les L2 et L3. Les temps d'accès sont clairement si, ce qui signifie que je suis en train de faire quelque chose de mal...

Je pense qu'il pourrait y avoir des problèmes avec le temps qu'il faut pour l'horloge (le démarrage et l'arrêt va prendre un certain temps en ns et il va changer quand ils sont mis en cache/unchached)

Quelqu'un peut-il me donner quelques conseils sur ce que je fais de mal?

UPDATE1: Donc je l'ai amorti le coût de la minuterie en faisant beaucoup de accède, je fixe la taille de mes caches et j'ai aussi suivi les conseils à la rendre de plus en plus complexes schéma d'indexation pour éviter fixe progrès. Malheureusement, les temps sont toujours éteint. Ils semblent tous être à venir pour la L1. Je pense que le problème pourrait être avec l'invalider, au lieu d'y accéder. Serait aléatoire vs LRU régime d'affecter les données étant invalidé?

UPDATE2: Fixe le memset (Ajouté L3 memset pour invalider les données L3 ainsi donc d'abord d'accès commence à la mémoire principale) et schéma d'indexation, toujours pas de chance.

UPDATE3: je ne pouvais pas obtenir que cette méthode fonctionne, mais il y avait quelques bonnes suggestions de réponses, et j'ai posté un couple de solutions de mon propre.

J'ai aussi couru Cachegrind pour afficher hit/miss

 ==6710== I   refs:      1,735,104
==6710== I1  misses:        1,092
==6710== LLi misses:        1,084
==6710== I1  miss rate:      0.06%
==6710== LLi miss rate:      0.06%
==6710== 
==6710== D   refs:      1,250,696  (721,162 rd   + 529,534 wr)
==6710== D1  misses:      116,492  (  7,627 rd   + 108,865 wr)
==6710== LLd misses:      115,102  (  6,414 rd   + 108,688 wr)
==6710== D1  miss rate:       9.3% (    1.0%     +    20.5%  )
==6710== LLd miss rate:       9.2% (    0.8%     +    20.5%  )
==6710== 
==6710== LL refs:         117,584  (  8,719 rd   + 108,865 wr)
==6710== LL misses:       116,186  (  7,498 rd   + 108,688 wr)
==6710== LL miss rate:        3.8% (    0.3%     +    20.5%  )
Ir I1mr ILmr      Dr  D1mr  DLmr     Dw D1mw DLmw 
.    .    .       .     .     .      .    .    .  #include <time.h>
.    .    .       .     .     .      .    .    .  #include <stdio.h>
.    .    .       .     .     .      .    .    .  #include <string.h>
.    .    .       .     .     .      .    .    .  
6    0    0       0     0     0      2    0    0  int main(){
5    1    1       0     0     0      2    0    0      srand(time(NULL));  //Seed ONCE
1    0    0       0     0     0      1    0    0      const int L1_CACHE_SIZE =  32768/sizeof(int);
1    0    0       0     0     0      1    0    0      const int L2_CACHE_SIZE =  262144/sizeof(int);
1    0    0       0     0     0      1    0    0      const int L3_CACHE_SIZE =  6587392/sizeof(int);
1    0    0       0     0     0      1    0    0      const int NUM_ACCESSES = 1000000;
1    0    0       0     0     0      1    0    0      const int SECONDS_PER_NS = 1000000000;
21    2    2       3     0     0      3    0    0      int arrayAccess[L1_CACHE_SIZE];
21    1    1       3     0     0      3    0    0      int arrayInvalidateL1[L1_CACHE_SIZE];
21    2    2       3     0     0      3    0    0      int arrayInvalidateL2[L2_CACHE_SIZE];
21    1    1       3     0     0      3    0    0      int arrayInvalidateL3[L3_CACHE_SIZE];
1    0    0       0     0     0      1    0    0      int count=0;
1    1    1       0     0     0      1    0    0      int index=0;
1    0    0       0     0     0      1    0    0      int i=0;
.    .    .       .     .     .      .    .    .      struct timespec startAccess, endAccess;
.    .    .       .     .     .      .    .    .      double mainMemAccess, L1Access, L2Access, L3Access;
1    0    0       0     0     0      1    0    0      int readValue=0;
.    .    .       .     .     .      .    .    .  
7    0    0       2     0     0      1    1    1      memset(arrayAccess, 0, L1_CACHE_SIZE*sizeof(int));
7    1    1       2     2     0      1    0    0      memset(arrayInvalidateL1, 0, L1_CACHE_SIZE*sizeof(int));
7    0    0       2     2     0      1    0    0      memset(arrayInvalidateL2, 0, L2_CACHE_SIZE*sizeof(int));
7    1    1       2     2     0      1    0    0      memset(arrayInvalidateL3, 0, L3_CACHE_SIZE*sizeof(int));
.    .    .       .     .     .      .    .    .  
1    0    0       0     0     0      1    1    1      index = 0;
4    0    0       0     0     0      1    0    0      clock_gettime(CLOCK_REALTIME, &startAccess); //start clock
772    1    1     514     0     0      0    0    0      while (index < L1_CACHE_SIZE) {
1,280    1    1     768   257   257    256    0    0          int tmp = arrayAccess[index];               //Access Value from L2
2,688    0    0     768     0     0    256    0    0          index = (index + tmp + ((index & 4) ? 28 : 36));   //on average this should give 32 element skips, with changing strides
256    0    0     256     0     0      0    0    0          count++;                                           //divide overall time by this 
.    .    .       .     .     .      .    .    .      }
4    0    0       0     0     0      1    0    0      clock_gettime(CLOCK_REALTIME, &endAccess); //end clock
14    1    1       5     1     1      1    1    1      mainMemAccess = ((endAccess.tv_sec - startAccess.tv_sec) * SECONDS_PER_NS) + (endAccess.tv_nsec - startAccess.tv_nsec);
6    0    0       2     0     0      1    0    0      mainMemAccess /= count;
.    .    .       .     .     .      .    .    .  
6    1    1       2     0     0      2    0    0      printf("Main Memory Access %lf\n", mainMemAccess);
.    .    .       .     .     .      .    .    .  
1    0    0       0     0     0      1    0    0      index = 0;
1    0    0       0     0     0      1    0    0      count=0;
4    1    1       0     0     0      1    0    0      clock_gettime(CLOCK_REALTIME, &startAccess); //start clock
772    1    1     514     0     0      0    0    0      while (index < L1_CACHE_SIZE) {
1,280    0    0     768   240     0    256    0    0          int tmp = arrayAccess[index];               //Access Value from L2
2,688    0    0     768     0     0    256    0    0          index = (index + tmp + ((index & 4) ? 28 : 36));   //on average this should give 32 element skips, with changing strides
256    0    0     256     0     0      0    0    0          count++;                                           //divide overall time by this 
.    .    .       .     .     .      .    .    .      }
4    0    0       0     0     0      1    0    0      clock_gettime(CLOCK_REALTIME, &endAccess); //end clock              
14    1    1       5     0     0      1    1    0      L1Access = ((endAccess.tv_sec - startAccess.tv_sec) * SECONDS_PER_NS) + (endAccess.tv_nsec - startAccess.tv_nsec);
6    1    1       2     0     0      1    0    0      L1Access /= count;
.    .    .       .     .     .      .    .    .  
6    0    0       2     0     0      2    0    0      printf("L1 Cache Access %lf\n", L1Access);
.    .    .       .     .     .      .    .    .  
.    .    .       .     .     .      .    .    .      //invalidate L1 by accessing all elements of array which is larger than cache
32,773    1    1  24,578     0     0      1    0    0      for(count=0; count < L1_CACHE_SIZE; count++){
40,960    0    0  24,576   513   513  8,192    0    0          int read = arrayInvalidateL1[count]; 
8,192    0    0   8,192     0     0      0    0    0          read++;
16,384    0    0  16,384     0     0      0    0    0          readValue+=read;               
.    .    .       .     .     .      .    .    .      }
.    .    .       .     .     .      .    .    .  
1    0    0       0     0     0      1    0    0      index = 0;
1    1    1       0     0     0      1    0    0      count = 0;
4    0    0       0     0     0      1    1    0      clock_gettime(CLOCK_REALTIME, &startAccess); //start clock
772    1    1     514     0     0      0    0    0      while (index < L1_CACHE_SIZE) {
1,280    0    0     768   256     0    256    0    0          int tmp = arrayAccess[index];               //Access Value from L2
2,688    0    0     768     0     0    256    0    0          index = (index + tmp + ((index & 4) ? 28 : 36));   //on average this should give 32 element skips, with changing strides
256    0    0     256     0     0      0    0    0          count++;                                           //divide overall time by this 
.    .    .       .     .     .      .    .    .      }
4    1    1       0     0     0      1    0    0      clock_gettime(CLOCK_REALTIME, &endAccess); //end clock
14    0    0       5     1     0      1    1    0      L2Access = ((endAccess.tv_sec - startAccess.tv_sec) * SECONDS_PER_NS) + (endAccess.tv_nsec - startAccess.tv_nsec);
6    1    1       2     0     0      1    0    0      L2Access /= count;
.    .    .       .     .     .      .    .    .  
6    0    0       2     0     0      2    0    0      printf("L2 Cache Acces %lf\n", L2Access);
.    .    .       .     .     .      .    .    .  
.    .    .       .     .     .      .    .    .      //invalidate L2 by accessing all elements of array which is larger than cache
262,149    2    2 196,610     0     0      1    0    0      for(count=0; count < L2_CACHE_SIZE; count++){
327,680    0    0 196,608 4,097 4,095 65,536    0    0          int read = arrayInvalidateL2[count];  
65,536    0    0  65,536     0     0      0    0    0          read++;
131,072    0    0 131,072     0     0      0    0    0          readValue+=read;                        
.    .    .       .     .     .      .    .    .      }
.    .    .       .     .     .      .    .    .  
1    0    0       0     0     0      1    0    0      index = 0;
1    0    0       0     0     0      1    0    0      count=0;
4    0    0       0     0     0      1    1    0      clock_gettime(CLOCK_REALTIME, &startAccess); //sreadValue+=read;tart clock
772    1    1     514     0     0      0    0    0      while (index < L1_CACHE_SIZE) {
1,280    0    0     768   256     0    256    0    0          int tmp = arrayAccess[index];               //Access Value from L2
2,688    0    0     768     0     0    256    0    0          index = (index + tmp + ((index & 4) ? 28 : 36));   //on average this should give 32 element skips, with changing strides
256    0    0     256     0     0      0    0    0          count++;                                           //divide overall time by this 
.    .    .       .     .     .      .    .    .      }
4    0    0       0     0     0      1    0    0      clock_gettime(CLOCK_REALTIME, &endAccess); //end clock
14    1    1       5     1     0      1    1    0      L3Access = ((endAccess.tv_sec - startAccess.tv_sec) * SECONDS_PER_NS) + (endAccess.tv_nsec - startAccess.tv_nsec);
6    0    0       2     0     0      1    0    0      L3Access /= count;
.    .    .       .     .     .      .    .    .  
6    1    1       2     0     0      2    0    0      printf("L3 Cache Access %lf\n", L3Access);
.    .    .       .     .     .      .    .    .  
6    0    0       1     0     0      1    0    0      printf("Read Value: %d", readValue);
.    .    .       .     .     .      .    .    .  
3    0    0       3     0     0      0    0    0  }
  • Utilisation rdtsc au lieu de clock_gettime voir: [Est clock_gettime() adéquat pour submicrosecond calendrier?][1] [1]: stackoverflow.com/questions/7935518/...
  • ne devriez pas faire une grande différence dans le grand schéma des choses depuis que je suis à la propagation de la surcharge par de gros d'accès.
  • L1 peut être une réponse à partir de l'Intel des développeurs manuel. Je suis assez sûr qu'il dit là que la performance de la L1 accès est exactement le même que l'accès de registre. Ce que le hardware prefetcher obtient vs ce qu'il parvient à désespérément muck up ne cesse jamais de me surprendre.
  • Quelle architecture de processeur que vous utilisez?
  • architecture x86 🙂
  • PandaRaid, le Cachegrind n'est pas vrai, c'est le seul simulateur de caches, et ses caches ne sont pas exactement correspondre à la réelle caches du CPU et de leurs moyens/miss régimes). Utilisation perf stat pour obtenir le total réel des comtes de hits/accidents et perf record pour obtenir des informations sur les consignes de faire manque.