Intel a eu la gentillesse de répondre à ce problème. Voir leur réponse ci-dessous.
Ce problème est dû à la manière dont les pages physiques sont réellement validées. Dans le cas de pages de 1 Go, la mémoire est contiguë. Ainsi, dès que vous écrivez sur un octet de la page de 1 Go, la page entière de 1 Go est affectée. Cependant, avec des pages de 4 Ko, les pages physiques sont allouées au fur et à mesure que vous touchez pour la première fois dans chacune des pages de 4 Ko.
for (uint64_t i = 0; i < size / MESSINESS_LEVEL / sizeof(*ptr); i++) {
for (uint64_t j = 0; j < MESSINESS_LEVEL; j++) {
index = i + j * size / MESSINESS_LEVEL / sizeof(*ptr);
ptr[index] = index * 5;
}
}
Dans la boucle la plus interne, l'index change à une vitesse de 512 Ko. Ainsi, les références consécutives sont mappées à des décalages de 512 Ko. Généralement, les caches ont 2048 ensembles (soit 2^11). Ainsi, les bits 6:16 sélectionnent les ensembles. Mais si vous progressez à des décalages de 512 Ko, les bits 6:16 seraient les mêmes, ce qui finirait par sélectionner le même ensemble et perdre la localité spatiale.
Nous vous recommandons d'initialiser l'intégralité de la mémoire tampon de 1 Go de manière séquentielle (dans le petit test de page) comme ci-dessous avant de démarrer l'horloge pour la chronométrer
for (uint64_t i = 0; i < size / sizeof(*ptr); i++)
ptr[i] = i * 5;
Fondamentalement, le problème concerne les conflits d'ensemble entraînant des échecs de cache en cas de pages volumineuses par rapport aux petites pages en raison de décalages constants très importants. Lorsque vous utilisez des décalages constants, le test n'est vraiment pas aléatoire .
Pas une réponse, mais pour fournir plus de détails sur ce problème déroutant.
Les compteurs de performances affichent un nombre d'instructions à peu près similaire, mais environ le double du nombre de cycles passés lorsque des pages volumineuses sont utilisées :
- Pages de 4 Kio IPC 0.29
- 1 Gio de pages IPC 0.10.
Ces chiffres IPC indiquent que le code est goulot d'étranglement sur l'accès à la mémoire (l'IPC lié au processeur sur Skylake est de 3 et plus). D'énormes goulots d'étranglement de pages plus difficiles.
J'ai modifié votre benchmark pour utiliser MAP_POPULATE | MAP_LOCKED | MAP_FIXED
avec adresse fixe 0x600000000000
dans les deux cas pour éliminer la variation temporelle associée aux défauts de page et à l'adresse de mappage aléatoire. Sur mon système Skylake, 2 Mio et 1 Gio sont plus de 2 fois plus lents que les pages de 4 Ko.
Compilé avec g++-8.4.0 -std=gnu++14 -pthread -m{arch,tune}=skylake -O3 -DNDEBUG
:
[[email protected]:~/src/test] $ sudo hugeadm --pool-pages-min 2MB:64 --pool-pages-max 2MB:64
[[email protected]:~/src/test] $ sudo hugeadm --pool-pages-min 1GB:1 --pool-pages-max 1GB:1
[[email protected]:~/src/test] $ for s in small huge; do sudo chrt -f 40 taskset -c 7 perf stat -dd ./release/gcc/test $s random; done
Duration: 2156150
Performance counter stats for './release/gcc/test small random':
2291.190394 task-clock (msec) # 1.000 CPUs utilized
1 context-switches # 0.000 K/sec
0 cpu-migrations # 0.000 K/sec
53 page-faults # 0.023 K/sec
11,448,252,551 cycles # 4.997 GHz (30.83%)
3,268,573,978 instructions # 0.29 insn per cycle (38.55%)
430,248,155 branches # 187.784 M/sec (38.55%)
758,917 branch-misses # 0.18% of all branches (38.55%)
224,593,751 L1-dcache-loads # 98.025 M/sec (38.55%)
561,979,341 L1-dcache-load-misses # 250.22% of all L1-dcache hits (38.44%)
271,067,656 LLC-loads # 118.309 M/sec (30.73%)
668,118 LLC-load-misses # 0.25% of all LL-cache hits (30.73%)
<not supported> L1-icache-loads
220,251 L1-icache-load-misses (30.73%)
286,864,314 dTLB-loads # 125.203 M/sec (30.73%)
6,314 dTLB-load-misses # 0.00% of all dTLB cache hits (30.73%)
29 iTLB-loads # 0.013 K/sec (30.73%)
6,366 iTLB-load-misses # 21951.72% of all iTLB cache hits (30.73%)
2.291300162 seconds time elapsed
Duration: 4349681
Performance counter stats for './release/gcc/test huge random':
4385.282466 task-clock (msec) # 1.000 CPUs utilized
1 context-switches # 0.000 K/sec
0 cpu-migrations # 0.000 K/sec
53 page-faults # 0.012 K/sec
21,911,541,450 cycles # 4.997 GHz (30.70%)
2,175,972,910 instructions # 0.10 insn per cycle (38.45%)
274,356,392 branches # 62.563 M/sec (38.54%)
560,941 branch-misses # 0.20% of all branches (38.63%)
7,966,853 L1-dcache-loads # 1.817 M/sec (38.70%)
292,131,592 L1-dcache-load-misses # 3666.84% of all L1-dcache hits (38.65%)
27,531 LLC-loads # 0.006 M/sec (30.81%)
12,413 LLC-load-misses # 45.09% of all LL-cache hits (30.72%)
<not supported> L1-icache-loads
353,438 L1-icache-load-misses (30.65%)
7,252,590 dTLB-loads # 1.654 M/sec (30.65%)
440 dTLB-load-misses # 0.01% of all dTLB cache hits (30.65%)
274 iTLB-loads # 0.062 K/sec (30.65%)
9,577 iTLB-load-misses # 3495.26% of all iTLB cache hits (30.65%)
4.385392278 seconds time elapsed
Tourné sur Ubuntu 18.04.5 LTS avec Intel i9-9900KS (qui n'est pas NUMA), 4x8GiB 4GHz CL17 RAM dans les 4 emplacements, avec performance
gouverneur pour aucune mise à l'échelle de la fréquence du processeur, ventilateurs de refroidissement liquide au maximum pour aucune limitation thermique, priorité FIFO 40 pour aucune préemption, sur un cœur de processeur spécifique pour aucune migration de processeur, plusieurs exécutions. Les résultats sont similaires avec clang++-8.0.0
compilateur.
On dirait que quelque chose ne va pas dans le matériel, comme un tampon de magasin par cadre de page, de sorte que les pages de 4 Ko permettent environ 2 fois plus de magasins par unité de temps.
Il serait intéressant de voir les résultats pour les processeurs AMD Ryzen 3.
Sur AMD Ryzen 3 5950X, la version pages volumineuses n'est que jusqu'à 10 % plus lente :
Duration: 1578723
Performance counter stats for './release/gcc/test small random':
1,726.89 msec task-clock # 1.000 CPUs utilized
0 context-switches # 0.000 K/sec
0 cpu-migrations # 0.000 K/sec
1,947 page-faults # 0.001 M/sec
8,189,576,204 cycles # 4.742 GHz (33.02%)
3,174,036 stalled-cycles-frontend # 0.04% frontend cycles idle (33.14%)
95,950 stalled-cycles-backend # 0.00% backend cycles idle (33.25%)
3,301,760,473 instructions # 0.40 insn per cycle
# 0.00 stalled cycles per insn (33.37%)
480,276,481 branches # 278.116 M/sec (33.49%)
864,075 branch-misses # 0.18% of all branches (33.59%)
709,483,403 L1-dcache-loads # 410.844 M/sec (33.59%)
1,608,181,551 L1-dcache-load-misses # 226.67% of all L1-dcache accesses (33.59%)
<not supported> LLC-loads
<not supported> LLC-load-misses
78,963,441 L1-icache-loads # 45.726 M/sec (33.59%)
46,639 L1-icache-load-misses # 0.06% of all L1-icache accesses (33.51%)
301,463,437 dTLB-loads # 174.570 M/sec (33.39%)
301,698,272 dTLB-load-misses # 100.08% of all dTLB cache accesses (33.28%)
54 iTLB-loads # 0.031 K/sec (33.16%)
2,774 iTLB-load-misses # 5137.04% of all iTLB cache accesses (33.05%)
243,732,886 L1-dcache-prefetches # 141.140 M/sec (33.01%)
<not supported> L1-dcache-prefetch-misses
1.727052901 seconds time elapsed
1.579089000 seconds user
0.147914000 seconds sys
Duration: 1628512
Performance counter stats for './release/gcc/test huge random':
1,680.06 msec task-clock # 1.000 CPUs utilized
1 context-switches # 0.001 K/sec
1 cpu-migrations # 0.001 K/sec
1,947 page-faults # 0.001 M/sec
8,037,708,678 cycles # 4.784 GHz (33.34%)
4,684,831 stalled-cycles-frontend # 0.06% frontend cycles idle (33.34%)
2,445,415 stalled-cycles-backend # 0.03% backend cycles idle (33.34%)
2,217,699,442 instructions # 0.28 insn per cycle
# 0.00 stalled cycles per insn (33.34%)
281,522,918 branches # 167.567 M/sec (33.34%)
549,427 branch-misses # 0.20% of all branches (33.33%)
312,930,677 L1-dcache-loads # 186.261 M/sec (33.33%)
1,614,505,314 L1-dcache-load-misses # 515.93% of all L1-dcache accesses (33.33%)
<not supported> LLC-loads
<not supported> LLC-load-misses
888,872 L1-icache-loads # 0.529 M/sec (33.33%)
13,140 L1-icache-load-misses # 1.48% of all L1-icache accesses (33.33%)
9,168 dTLB-loads # 0.005 M/sec (33.33%)
870 dTLB-load-misses # 9.49% of all dTLB cache accesses (33.33%)
1,173 iTLB-loads # 0.698 K/sec (33.33%)
1,914 iTLB-load-misses # 163.17% of all iTLB cache accesses (33.33%)
253,307,275 L1-dcache-prefetches # 150.772 M/sec (33.33%)
<not supported> L1-dcache-prefetch-misses
1.680230802 seconds time elapsed
1.628170000 seconds user
0.052005000 seconds sys