Intel byl tak laskav a odpověděl na tento problém. Viz jejich odpověď níže.
Tento problém je způsoben tím, jak jsou fyzické stránky skutečně potvrzeny. V případě 1GB stránek je paměť souvislá. Jakmile tedy zapíšete na kterýkoli bajt v rámci stránky o velikosti 1 GB, bude přiřazena celá stránka o velikosti 1 GB. U stránek o velikosti 4 kB se však fyzické stránky přidělují, jakmile se na každé ze stránek o velikosti 4 kB poprvé dotknete.
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;
}
}
V nejvnitřnější smyčce se index mění s krokem 512 kB. Po sobě jdoucí reference se tedy mapují s offsety 512 kB. Mezipaměti mají obvykle 2048 sad (což je 2^11). Takže bity 6:16 vyberou sady. Ale pokud budete postupovat s offsety 512 kB, bity 6:16 by byly stejné, což by skončilo výběrem stejné sady a ztrátou prostorové lokality.
Doporučujeme inicializovat celou 1GB vyrovnávací paměť sekvenčně (v testu malých stránek), jak je uvedeno níže, před spuštěním hodin k měření času
for (uint64_t i = 0; i < size / sizeof(*ptr); i++)
ptr[i] = i * 5;
V zásadě jde o konflikty nastavení, které mají za následek vynechání mezipaměti v případě velkých stránek ve srovnání s malými stránkami kvůli velmi velkým konstantním posunům. Když používáte konstantní posuny, test opravdu není náhodný .
Není to odpověď, ale poskytnout více podrobností k tomuto matoucímu problému.
Čítače výkonu ukazují zhruba podobný počet instrukcí, ale zhruba dvojnásobný počet cyklů strávených při použití velkých stránek:
- 4KiB stránky IPC 0,29,
- 1GiB stránky IPC 0.10.
Tato čísla IPC říkají, že kód je omezený v přístupu k paměti (CPU vázaný IPC na Skylake je 3 a vyšší). Obrovské stránky jsou obtížnější.
Upravil jsem váš benchmark tak, aby používal MAP_POPULATE | MAP_LOCKED | MAP_FIXED
s pevnou adresou 0x600000000000
pro oba případy, aby se eliminovaly časové odchylky spojené s chybami stránek a náhodnou mapovací adresou. Na mém systému Skylake jsou 2MiB a 1GiB více než 2x pomalejší než 4kB stránky.
Zkompilováno s 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
Běžel na Ubuntu 18.04.5 LTS s Intel i9-9900KS (což není NUMA), 4x8GiB 4GHz CL17 RAM ve všech 4 slotech, s performance
Governor pro žádné škálování frekvence CPU, ventilátory kapalinového chlazení na maximum bez tepelného škrcení, priorita FIFO 40 bez preempce, na jednom konkrétním jádru CPU bez migrace CPU, více běhů. Výsledky jsou podobné s clang++-8.0.0
kompilátor.
Zdá se, že je něco v hardwaru podlomené, jako je vyrovnávací paměť úložiště na rámec stránky, takže stránky o velikosti 4 kB umožňují ~2x více obchodů za jednotku času.
Bylo by zajímavé vidět výsledky pro CPU AMD Ryzen 3.
Na AMD Ryzen 3 5950X je verze pro velké stránky pouze o 10 % pomalejší:
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