Co je NUMA?
NUMA je zkratka pro Non-uniform memory access a jde o architekturu, kdy každý procesor obsluhuje jen svoji paměť. Na rozdíl od toho UMA (Uniform memory access) mají všechny procesory paměť jen jednu – sdílenou. Nejrozšířenější SMP (Symmetric multiprocessing) je podmnožinou UMA. Dodejme, že NUMA najdeme kupříkladu na víceprocesorových deskách (myšleno více fyzických patic, ne více jader v jednom procesoru).
NUMA může mít obecně rychlejší přístup do paměti, jelikož paměti jednotlivých uzlů (nodes) jsou nezávislé. Přitom samotný systém vidí všechna jádra všech procesorů a také celou paměť jako jeden celek. Tím se dostáváme k nevýhodě NUMA: Když data pro procesor 0 budou v paměti obsluhované procesorem 1, musí data z paměti nejprve přečíst procesor 1 a poté je interconnectem poslat do procesoru 0. To je samozřejmě pomalejší, než kdyby se četlo z lokální paměti.
Proto systém o rozložení uzlů a paměti ví a snaží se data držet v lokální paměti. K obsluze NUMA se využívá nástroje numactl. Například dvouprocesorový počítač se 40 jádry vypadá takto:
$ numactl --hardware available: 2 nodes (0-1) node 0 cpus: 0 1 2 3 4 5 6 7 8 9 20 21 22 23 24 25 26 27 28 29 node 0 size: 63818 MB node 0 free: 57015 MB node 1 cpus: 10 11 12 13 14 15 16 17 18 19 30 31 32 33 34 35 36 37 38 39 node 1 size: 64462 MB node 1 free: 33607 MB node distances: node 0 1 0: 10 21 1: 21 10
$ numactl --show policy: default preferred node: current physcpubind: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 cpubind: 0 1 nodebind: 0 1 membind: 0 1 preferred:
Všimneme si policy: default
a také node distances
, kdy pro komunikaci 0 a 1 je nastaveno 21, kdežto třeba pro 0 a 0 je to 10.
Proč NUMA na Raspberry Pi?
Raspberry Pi 4 a 5 však mají jen jeden čtyřjádrový procesor a paměť připojenou k procesoru jen jedním řadičem, proč tedy zde používáme NUMA? Letos v červnu si Tvrtko Ursulin z Igalia všiml, že možná trochu nelogicky po zapnutí emulace NUMA (fake NUMA) je výkon Raspberry Pi 5 v testu Geekbench lepší o 6 % pro jednovláknové úlohy a o 18 % pro vícevláknové.
Jen bylo potřeba použít NUMA interleave=all (pomocí parametru jádra numa_policy=interleave
), což v praxi znamená, že se zároveň zapisuje prokládaně do paměti všech dostupných uzlů. Tady jen poznamenám, že interleave=all u normální NUMA architektury, jako byl třeba příklad dvouprocesorové desky v předchozí kapitole, povede téměř vždy ke zhoršení výkonu, protože místo použití lokální paměti se bude prokládaně využívat paměť všech uzlů, což vede k horší latenci a navíc se může přetížit interconnect.
Nápad se zalíbil vývojářům z Raspberry a ti se začali fake NUMA zabývat pro Raspberry Pi 4 a 5. Proč ne pro Raspberry Pi 3? Zřejmě proto, že na něm není možné kvůli nedostatku paměti (maximálně 1 GB) pustit Geekbench. Ten vyžaduje více než 2 GB RAM. Anebo to bude kvůli detailům organizace paměti (single rank / dual rank). Ve výchozím stavu nebude NUMA zapnutá ani na Raspberry Pi 4 s 1 a 2 GB RAM.
Vysvětlení, proč fake NUMA pomáhá s výkonem obzvláště vícevláknových aplikací, je patrně v řadiči paměti, který Broadcom v SoC pro Raspberry Pi 4 a 5 používá, nebo v paměti samotné. Rozdělení paměti na několik oblastí společně s použitím interleave pak lépe využije paměťový čip, kterému lépe svědčí zápisy a čtení daleko od sebe než blízko u sebe.
Raspberry Pi 5 s 4 GB rychlejší než s 8 GB?
Zde uděláme malou, ale zajímavou odbočku. Uživatel Brunnis před rokem zjistil, že Raspberry Pi 5 s 4 GB RAM je překvapivě rychlejší než Raspberry Pi 5 s 8 GB RAM, a to asi o 8 % ve vícejádrovém Geekbench 5. Ten mají mimochodem vývojáři Raspberry Pi celkem v oblibě, i když Linus Torvalds moc ne.
Nesporná výhoda Geekbench je v tom, že existuje pro Linux (x86, Arm64 i RISC-V), Windows, macOS, Android i iOS. V případě Raspberry Pi si tedy můžete lehce zkusit, jestli má procesor rychlejší než ten, co máte v mobilním telefonu s Androidem.
Problém byl patrně s nutným obnovováním DRAM paměti podle JEDEC. Větší paměť vyžaduje delší obnovování. Experimentálně bylo možné nastavit i na 8GB verzi obnovovací parametry 4GB verze. Dále se zjistilo, že je možné ještě zkrátit obnovovací parametry, pokud je teplota paměťového čipu dostatečně nízká.
Nakonec se po diskusi s dodavatelem RAM (Micron) zjistilo, že je možné použít ještě rychlejší obnovovací parametry pro oba paměťové čipy 4 GB a 8 GB, než dovoloval standard JEDEC. Podobné zlepšení bylo také použito pro Raspberry Pi 4. Pokud aktualizujete, měli byste tedy mít rychlejší jak Raspberry Pi 5, tak i Raspberry Pi 4. Zároveň je Raspberry Pi 5 se 4 GB a 8 GB nyní stejně rychlé. V dlouhé diskusi na GitHubu se také řešilo NUMA a zjistilo se, že významně pomáhá výkonu.
Můžu si NUMA zkusit?
Ano, vývojáři to patrně myslí s NUMA vážně a je možné si to na Raspberry Pi 4 a 5 vyzkoušet, vyladit pro svoji aplikaci a nahlásit případné problémy. Postup je jednoduchý. Aktualizujete jádro pomocí sudo rpi-update
. Navíc je potřeba jen přidat řádek do nastavení boot loaderu sudo rpi-eeprom-config -e
. Pro Raspberry Pi 5 tam dáte SDRAM_BANKLOW=1
a pro Raspberry Pi 4 tam dáte SDRAM_BANKLOW=3
.
Počet NUMA uzlů (vlastně jen oblastí v paměti, jádra budete mít pořád čtyři) pak závisí na modelu a na paměti. Raspberry Pi 5 s 8 GB má ve výchozím nastavení uzlů osm a Raspberry Pi 4 s 8 GB jen dva uzly. Toto nastavení se zatím jeví vývojářům jako optimální. Je ale možné, že pro vaši zátěž bude výhodnější počet uzlů změnit. Slouží k tomu parametr jádra například numa=fake=4
, který napíšete do /boot/firmware/cmdline.txt
.
Na Raspberry Pi 5 8 GB pak NUMA uzly vypadají následovně:
$ numactl --hardware available: 8 nodes (0-7) node 0 cpus: 0 1 2 3 node 0 size: 993 MB node 0 free: 948 MB node 1 cpus: node 1 size: 1019 MB node 1 free: 992 MB node 2 cpus: node 2 size: 1019 MB node 2 free: 993 MB node 3 cpus: node 3 size: 955 MB node 3 free: 926 MB node 4 cpus: node 4 size: 1019 MB node 4 free: 991 MB node 5 cpus: node 5 size: 1019 MB node 5 free: 992 MB node 6 cpus: node 6 size: 1019 MB node 6 free: 993 MB node 7 cpus: node 7 size: 1014 MB node 7 free: 986 MB node distances: node 0 1 2 3 4 5 6 7 0: 10 20 20 20 20 20 20 20 1: 20 10 20 20 20 20 20 20 2: 20 20 10 20 20 20 20 20 3: 20 20 20 10 20 20 20 20 4: 20 20 20 20 10 20 20 20 5: 20 20 20 20 20 10 20 20 6: 20 20 20 20 20 20 10 20 7: 20 20 20 20 20 20 20 10
$numactl --show policy: interleave preferred node: 0 (interleave next) interleavemask: 0 1 2 3 4 5 6 7 interleavenode: 0 physcpubind: 0 1 2 3 cpubind: 0 nodebind: 0 membind: 0 1 2 3 4 5 6 7 preferred: 0 1 2 3 4 5 6 7
Všimneme si, že jednotlivé uzly nemají přesně stejně velkou paměť a také již zmiňované policy: interleave
.
Jak je to rychlé?
Sám jsem udělal několik testů na Raspberry Pi 5 8 GB (výchozí numa=fake=8
) a Raspberry Pi 4 8 GB (výchozí numa=fake=2
) a výsledky jsou potěšující.
benchmark | 1 vlákno | 4 vlákna |
Geekbench | +14 % | +46 % |
Stream add | –2 % | +27 % |
kompilace jádra | +11 % | |
HPL | +3 % | +25 % |
bzip3 | +29 % | +96 % |
pigz | +0 % | +28 % |
zstd | +71 % | +164 % |
xz | +10 % | +21 % |
geometrický průměr | +16 % | +46 % |
V jednovláknových úlohách jsou vybrané testy rychlejší průměrně o 16 % rychlejší, ve vícevláknových o 46 %. Všimněte si, že nárůst výkonu v Geekbench je o dost vyšší, než původně hlásil Tvrtko. Jednak máme ve výchozím nastavení dvakrát více uzlů (osm místo čtyř) a možná také pomohlo vylepšené časování pamětí, jak bylo výše zmíněno.
benchmark | 1 vlákno | 4 vlákna |
Geekbench | +1 % | +8 % |
Stream add | –1 % | +7 % |
kompilace jádra | +4 % | |
HPL | +2 % | +4% |
bzip3 | +9 % | +12 % |
pigz | –0 % | –6 % |
zstd | +1 % | +36 % |
xz | +2 % | +7 % |
geometrický průměr | +2 % | +8 % |
V případě Raspberry Pi 4 8 GB nejsou výsledky tak dobré, nicméně průměrně jsou jednojádrové testy o 2 % rychlejší a vícejádrové o 8 %. Možná je potřeba zvolit větší počet NUMA uzlů pomocí parametru jádra, například numa=fake=4
.
Výraznější přínos je u vícevláknových testů, tam je patrně paměť a řadič úzkým hrdlem. Největší nárůst výkonu pozorujeme u komprese pomocí zstd a bzip3 na výchozí nastavení komprese a ve více vláknech. V případě Raspberry Pi 5 je vícevláknové zstd rychlejší dokonce o 164 %.
Rychlejší Raspberry Pi 4 a 5 pro všechny
Vývojáři nechají uživatelé NUMA nějakou chvíli testovat. Problémy by mohly způsobovat například různé periférie, jako jsou kamery nebo dekodér videa h264/hevc v Raspberry Pi 4. Pokud testování dopadne dobře, změna se časem dostane do stabilního jádra v distribuci Raspberry Pi OS (dříve Raspbian) a ze zrychlení budou moci profitovat všichni uživatelé.
Uživatelé Raspberry Pi 4 s 1 a 2 GB RAM budou mít sice NUMA ve výchozím stavu patrně vypnuté, jako je tomu nyní, mohou si však NUMA ručně zapnout.
Zvýšení výkonu je patrnější u Raspberry Pi 5. Pokud máte Raspberry Pi 4, můžete experimentovat se zvýšením počtu NUMA uzlů.