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 etperf record
pour obtenir des informations sur les consignes de faire manque.
Vous devez vous connecter pour publier un commentaire.
Je serais plutôt d'essayer d'utiliser le matériel de l'horloge comme une mesure. Le
rdtsc
instruction va vous dire que le cycle actuel de compter depuis la CPU a été mis sous tension. Aussi il est préférable d'utiliserasm
à assurez-vous toujours les mêmes instructions sont utilisés à la fois mesurée et au sec. En utilisant que quelques ingénieuses statistiques que j'ai fait il y a longtemps:De sortie sur mon Core2Duo:
error: 'asm' operand has impossible constraints
clang 5.0
,gcc 4.8
eticc 14.0.1
pour x86_64 générique cible sans erreurs. Essayez de mettre à jour votre compilateur.error: 'asm' operand has impossible constraints
. icc 13.1.3 (n'ont pas 14) donne:catastrophic error: can't allocate registers for asm instruction
asm(
ligne danstest_cache
. Je n'ai pas utilisé tous les indicateurs.gcc
qui va provoquer une erreur si vous essayez de compiler pouria32
(-m32
), il suffit de compiler pour 64-bit.Ok, plusieurs problèmes avec votre code:
Comme vous l'avez mentionné, les mesures prennent beaucoup de temps. En fait, ils sont très susceptibles de prendre beaucoup plus de temps que le simple accès lui-même, de sorte qu'ils ne sont pas mesurer quelque chose d'utile. Pour atténuer la situation, l'accès à plusieurs éléments, et de les amortir (diviser le temps total par le nombre d'accès. Notez que pour mesurer la latence, vous voulez que ces accède à être sérialisés, sinon, ils peuvent être réalisées en parallèle et vous aurez seulement à mesurer le débit d'indépendants accède. À atteindre que vous pouvez simplement ajouter une fausse dépendance entre les accès.
Pour, par exemple, d'initialiser la matrice de zéros, et à faire:
.. et bien sûr n'oubliez pas de diviser le temps par
NUM_ACCESSES
.Maintenant, j'ai fait l'indice intentionnellement compliqué pour vous éviter un fixe foulée qui pourrait déclencher une prefetcher (un peu de trop, vous n'êtes pas susceptibles de remarquer un impact, mais pour les besoins de la démonstration...). Vous pourriez probablement se contenter d'un simple
index += 32
, ce qui vous permettrait des progrès de 128k (deux lignes de cache), et éviter les "avantages" de plus simple adjacentes ligne/simple flux prefetchers. J'ai aussi remplacé le% 1000
avec& 1023
depuis&
est plus rapide, mais elle doit être une puissance de 2 à fonctionner de la même manière qu'augmenterACCESS_SIZE
à 1024 et cela devrait fonctionner.Invalider la L1 par le chargement de quelque chose d'autre est bonne, mais les tailles de l'air bizarre. Vous n'avez pas de spécifier votre système, mais
256000
semble assez grand pour la L1. Une L2 est généralement 256k sur de nombreuses communes moderne les Processeurs x86 pour, par exemple, notez Également que 256k est pas256000
, mais plutôt256*1024=262144
. En va de même pour la deuxième taille: 1M n'est pas1024000
, c'est1024*1024=1048576
. En supposant que c'est bien votre L2 taille (plus probablement d'une L3, mais sans doute trop petit pour ça).Votre invalider les tableaux sont de type
int
, de sorte que chaque élément est plus long qu'un seul octet (le plus probable est de 4 octets, selon le système). Vous êtes en fait invaliderL1_CACHE_SIZE*sizeof(int)
vaut la peine d'octets (et il en va de même pour la L2 invalidation de la boucle)Mise à jour:
memset
reçoit la taille en octets, vos tailles sont divisés parsizeof(int)
Invalidation de votre lit ne sont jamais utilisés, et peut être optimisé à. Essayer d'accumuler le lit de la valeur et de l'imprimer à la fin, pour éviter cette possibilité.
Le memset au début est de l'accès aux données ainsi, à cet effet, votre première boucle de l'accès aux données à partir de la L3 (depuis les 2 autres memsets étaient toujours en vigueur à expulser de L1+L2, bien que partiellement en raison de la taille de l'erreur.
Le progrès est peut-être trop petit, de sorte que vous obtenez deux accès à la même cacheline (L1 hit). Assurez-vous qu'ils sont assez répandus par l'ajout de 32 éléments (x4 octets) - c'est 2 cacheline, donc vous n'aurez pas adjacent cacheline prefetch avantages.
Depuis NUM_ACCESSES est plus grande que ACCESS_SIZE, vous êtes essentiellement de répéter les mêmes éléments et serait probablement obtenir L1 hits pour eux (donc le avg temps changements en faveur de la L1 à la latence d'accès). Au lieu d'essayer à l'aide de la L1 de taille de sorte que vous avez accès à l'ensemble de la L1 (sauf pour les sauts) exactement une fois. Par exemple, ce
n'oubliez pas d'augmenter
arrayAccess
de L1 taille.Maintenant, avec les modifications ci-dessus (plus ou moins), j'obtiens quelque chose comme ceci:
Qui semble encore un peu long, mais peut-être parce qu'il inclut un supplément de dépendance sur des opérations arithmétiques
/proc/cpuinfo
dire?/sys/devices/system/cpu/cpu0/cache/index1/size
dire?i
, et depuisi
changements il n'y a pas fixé la foulée, pas besoin de la multiplication.Largement utilisés test classique pour le cache de latence est de parcourir la liste chaînée. Il fonctionne sur moderne superscalar/superpipelined CPU et même en Dehors de l'ordre des cœurs comme le BRAS Cortex-A9+ et Intel Core 2/ix. Cette méthode est utilisée par open-source lmbench - dans le test
lat_mem_rd
(page de man) et dans CPU-Z latence outil de mesure: http://cpuid.com/medias/files/softwares/misc/latency.zip (natif de Windows binaire)Il existe des sources de lat_mem_rd test de lmbench: https://github.com/foss-for-synopsys-dwc-arc-processors/lmbench/blob/master/src/lat_mem_rd.c
Et le principal test est
Donc, après le déchiffrage de la macro, nous faisons beaucoup de linéaire des opérations comme:
sur la mémoire, rempli avec des pointeurs, chaque pointage
stride
éléments de l'avant.Comme le dit la page de man http://www.bitmover.com/lmbench/lat_mem_rd.8.html
Description plus détaillée avec des exemples sur les Pouvoirs est disponible à partir d'IBM wiki: Démêler l'accès à la mémoire de mesures - lat_mem_rd par Jenifer Trémie 2013
PS: Il y a du papier à partir d'Intel (grâce à Eldar Abusalimov) avec des exemples de l'exécution de lat_mem_rd: ftp://download.intel.com/design/intarch/PAPERS/321074.pdf - désolé droite de l'url est http://www.intel.com/content/dam/www/public/us/en/documents/white-papers/ia-cache-latency-bandwidth-paper.pdf
La "mesure de la Cache et la Latence de la Mémoire et du PROCESSEUR à la Mémoire de la bande Passante Pour une utilisation avec une Architecture Intel" par Joshua Ruggiero à partir de décembre 2008:
Pas vraiment une réponse, mais lire de toute façon quelque chose qui a déjà été mentionné dans d'autres réponses et commentaires ici
bien juste l'autre jour, je réponds à cette question:
c'est au sujet de la mesure de
L1/L2/.../L?/MEMORY
taux de transfert de prendre un coup d'oeil pour le meilleur point de départ de votre problème[Notes]
Je recommande fortement d'utiliser l'instruction RDTSC pour la mesure du temps de
surtout pour L1 que tout le reste est trop lent. Ne pas oublier de mettre le processus affinité simple CPU parce que tous les cœurs ont leur propre compteur et leur nombre diffère beaucoup, même sur la même entrée d'Horloge !!!
Ajuster la CPU horloge Maximale pour la variable de l'horloge des ordinateurs et n'oubliez pas de compte pour
RDTSC
dépassement de capacité si vous utilisez seulement 32 bits de la partie moderne (PROCESSEUR 32 bits de dépassement de compteur dans une seconde). Pour le calcul du temps d'utilisation du PROCESSEUR de l'horloge (le mesurer ou de l'utilisation de la valeur de registre)ensemble du processus d'affinité CPU seul
tous CPU cœurs ont généralement leur propre L1,L2 caches donc sur multi-tâche OS vous pouvez mesurer des choses confuses si vous ne le faites pas
faire de la sortie graphique (diagramme)
puis vous verrez bien ce qui se passe réellement dans le lien ci-dessus, j'ai posté tout à fait une quelques parcelles
utilisation la plus haute priorité du processus disponible en OS
Bien pour ceux qui sont intéressés, je ne pouvais pas obtenir mon premier jeu de codes de travail donc j'ai essayé quelques solutions de rechange qui produit des résultats décents.
Le premier listes liées avec des nœuds affectés foulée octets à part, dans une mémoire contiguë de l'espace. Le déréférencement des nœuds atténue l'efficacité de la pré-récupérateur et dans le cas de plusieurs lignes de cache sont tirés dans j'ai fait le progrès important pour éviter les accès au cache. Comme la taille de la liste alloué augmente, il accède à la mémoire cache ou de la structure de la mémoire qui va contenir montrant clairement les divisions de latence.
Ce produit le plus de résultats cohérents, et en utilisant une variété de la taille des matrices et de tracer le respectifs des latences a donné une distinction très claire des différentes tailles de cache présents.
La méthode suivante comme le précédent alloué augmentation de la taille des tableaux. Mais au lieu d'utiliser une liste chaînée pour l'accès à la mémoire, je remplis chaque indice avec son numéro et mélangées au hasard le tableau. J'ai ensuite utilisé ces indices de sauter autour de aléatoirement dans la matrice des accès, de l'atténuation des effets de la pré-récupérateur. Cependant, il avait un très fort écart de temps d'accès lors de plusieurs lignes de cache adjacentes sont tiré dans et arriver à être frappé.
Moyenne à travers de nombreux essais, cette méthode produit relativement à l'exactitude des résultats aussi bien. Le premier choix est certainement le meilleur des deux, mais c'est une autre approche qui fonctionne très bien.