Vlákno názorů k článku
Ubuntu nebude kompilovat všechny balíčky s -O3 od cc - ětšina zpomalení je způsobena patrně zvětšením binárek a...

  • Článek je starý, nové názory již nelze přidávat.
  • 31. 3. 2025 16:56

    cc

    ětšina zpomalení je způsobena patrně zvětšením binárek a agresivním vkládání funkcí (function inline), což zvětšuje tlak na registry a vyrovnávací paměť.

    A jak si autor představuje, že by "tlak na registry" mohl mít vliv na zpomalení programu? I když bude program menší, tak "tlak na registry" bude pořád stejný. Každá dnešní moderní architektura používá register renaming, takže "tlakem" to asi nebude...

    Vysvětlení je přitom mnohem prostší - co způsobuje tak masivní zvětšení binárky? Je to inlining funkcí, jak je psané v článku, a nebo spíš loop unrolling? Ono totiž když compiler unrolluje všechny loopy v programu, které třeba běžně mají jen pár iterací, tak tam je právě problém - kód bude celkově větší (unrolling znamená extra prolog/epilog - "tlak" právě na instruction cache) a aby ten unrolling dával smysl, tak se musí amortizovat počtem iterací. Takže pokud compiler běžně unrollne loopy, kde je počet iterací minimální, tak to stojí jak strojový čas tak i extra instruction cache.

  • 31. 3. 2025 19:53

    dan8

    Možná tomu moc nerozumím, ale když se unrollne loop, tak pak může být rozpracováno více "iterací" najednou. Tím se také použije více registrů najednou a pokud v nich byly proměnné, které se budou dále využívat, tak se pak musí znovu načíst. Takže možná proto větší tlak na registry?

  • 31. 3. 2025 21:00

    cc

    To ale musí všechno spočítat překladač. Pokud loop potřebuje 8 proměnných a existuje jen 16 registrů, tak moc nemá cenu pokoušet se o unrolling, pokud se nejedná o nějakou autovektorizaci třeba, kde se použijou jiné registry (SIMD).

    Ale překladače v tomto opravdu chybují, a problém je, že loop unrolling se dělá mnohem dřív než alokace registrů, takže se stává, že před transformací je program "perfektní", ale po transformaci už chybí registry. Každý unroll stojí i nějaké GP registry, protože prolog/epilog (popř. lead/tail loop) potřebuje většinou vlastní counter, popř. pokud se dělá alignment tak jsou potřeba nějaké dočasné registry, které nemusí být k dispozici a nebo sice jsou k dispozici, ale funkce bude potřebovat větší prolog/epilog (pro save/restore non-clobbered registrů).

    Já bych osobně doporučil nastudovat si direktivy (#pragma), napsat si makro (#define NO_UNROLL ...) a přidat to k cyklům, které nikdy nechci unrollovat. Clang tomu rozumí, GCC někdy jo a někdo ne... MSVC nevim.

    BTW: Dnešní kompilery umí i "rematerializaci" - to je obejítí znovunačtení tím, že se obsah registru znovu vypočítá (třeba pokud C = A + B, ztratím C, tak si ho můžu znovu vypočítat a vyhnout se uložení a načtení do/z paměti).