Pro vysvětlení: Linus se snaží říct, že jádro má dávno mnohem podrobnější detekci schopností CPU a interně může využívat i nuancí, které by úrovněmi architektur byly ztraceny. Tj. že různé CPU podporují různé specializované strojové instrukce a rozřazením do těchto 4 úrovní by se občas stalo, že by se zbytečně nějaká fičůra nepoužila.
A teď se na to podívej z pohledu distribuce.
Podle mě chcou udělat to, že když máš CPU co podporuje všechno z v3 tak se automaticky použije jádro zkompilované pro v3. Toto je jasný bonus, protože některé instrukce jsou šikovné a nejedná se pouze o SIMD, na druhou stranu nemá cenu dělat runtime dispatch kvůli kódu kde se použije trochu BMI2 instrukcí. Pokud máš v1 až v4 tak máš jen 4 úrovně, pro které budeš optimalizovat, a tyto úrovně reflektujou celkový pokrok v mikroarchitektuře.
v1 je v podstatě baseline (SSE2), v2 je SSE4.2 (CPU co nemá AVX2), v3 je AVX2/FMA a v4 je AVX-512. Toto je podle mě celkem dobrá granularita, kde trochu ostrouhají jen ti, co mají CPU s AVX ale bez AVX2, ale to je právě ta daň za ty úrovně.
Jádro si stejně detekuje všechny CPUID rozšíření a podle toho udělá runtime dispatch kde je to vhodné (třeba když máš AESNI a tak se použije toto pro AES, atd...). Prostě ty úrovně jsou vhodné v případě, že chci optimalizované celé jádro a ne jen části, které mají runtime dispatch.
5. 12. 2024, 23:52 editováno autorem komentáře
Já jsem napsal v x86 asm desetitisíce a možná i statisíce řádků a řeknu ti, že přehodit nějaké 2 instrukce dnes nemá vůbec žádný efekt. Dnešní CPU jsou tak OoO, že to je úplně jedno.
Naopak co se vyplatí optimalizovat je aby CPU viděl adresy dopředu, ze kterých bude číst. Takže pokud udělám třeba XOR nějakého registru a vypadne mi z toho adresa, ze které hned další instrukce čte - tady je stall (perf: frontend stall), a nepomůže NIC kromě přepsání toho kódu tak aby tam ten XOR nebyl (toto může být třeba problém pokud mám red/black tree a ten bit pro barvu nacpu do jednoho pointeru, na druhou stranu pokud chci výkon tak nebudu používat red/black tree).
Takže, vědět jak velký je ROB a podle toho dělat nějaké optimalizace je úplný nonsense. Vědět ale třeba latence instrukcí, tady se dají dělat kouzla (třeba AVX2 kód kde použiju VPCMPEQB může mít menší latenci než AVX-512 kód, který použije tu stejnou instrukci jen kvůli tomu, že ten AVX2 kód použije SIMD registr jako masku a ne K registr).
Takže za mě optimalizovat pro konkrétní mikroarchitekturu je skoro vždycky o tom, jaké mají instrukce latenci a kolik L1/L2/L3 cache je k dispozici. Další věci jako třeba penalizace za branch misprediction jsou sice užitečné, ale neviděl jsem nikdy kód, který by z toho dokázal něco měřitelného vytěžit.
Ale tak můžeš překvapit a linknout něco, kde se opravdu optimalizuje podle velikosti ROB, atd... Takový kód bych rád viděl (pokud dělá něco užitečného) i benchmarky k tomu, a naučil se něco nového.
6. 12. 2024, 00:41 editováno autorem komentáře
Tak se treba podivej na GCC ze co dela -mtune (meni charakter generace - prave podle mikroarchitektury), zatimco -march voli subset instrukcnich sad, ktere muzou byt obsazeny ve vystupu (podle typu/modelu/submodelu procesoru).
Nebavime se o ASM, tam to neni optimalizovatelny (resp. kdo pise asm, nechce aby se do toho jakkoliv sahalo).
GCC dělá jen to, že má tabulky latencí některých instrukcí, takže je dokáže optimálně seřadit. Další z věcí je třeba znalost optimální velikosti load/store. Si pamatuju jak starší procesory měli penalizaci za 256-bit load/store, takže compiler emitoval kód, který to rozdělil na 128-bit operace, jenže toto je pořád kategorie "latence instrukcí".
Podle me si to je vedome i poctu tech internich rename registers, takze to muze pouzivat jakesi virtualni registry, byt generovany kod bude mit porad dokola pouzivat ty same nazvy zakladnich registru.
Za me je prave pocet internich (neviditelnych) registru stezejni optimalizace (u mtune) pro zvolenou mikroarchitekturu, aby byl konkretni procesor (a jeho tranzistory) vyuzity naplno.
Takze (nakonec) souhlasim, ze prerovnani kvuli latencim uz neni uplne cilovka.