Programovanie

Ako zrýchliť kód pomocou kešiek CPU

Vyrovnávacia pamäť procesora znižuje latenciu pamäte, keď sa k údajom pristupuje z hlavnej systémovej pamäte. Vývojári môžu a mali by využívať výhody medzipamäte CPU na zlepšenie výkonu aplikácií.

Ako fungujú medzipamäte procesora

Moderné CPU majú zvyčajne tri úrovne vyrovnávacej pamäte označené L1, L2 a L3, čo odráža poradie, v akom ich CPU kontroluje. CPU často majú dátovú cache, inštrukčnú cache (pre kód) a zjednotenú cache (pre čokoľvek). Prístup k týmto cache je oveľa rýchlejší ako prístup do RAM: Zvyčajne je vyrovnávacia pamäť L1 asi 100-krát rýchlejšia ako RAM pre prístup k údajom a vyrovnávacia pamäť L2 je 25-krát rýchlejšia ako RAM pre prístup k údajom.

Keď je váš softvér spustený a vyžaduje načítanie údajov alebo pokynov, najskôr sa skontrolujú pamäte cache procesora, potom pomalšia systémová pamäť RAM a nakoniec oveľa pomalšie diskové jednotky. Preto chcete optimalizovať svoj kód tak, aby ste najskôr hľadali, čo bude pravdepodobne potrebné z medzipamäte CPU.

Váš kód nemôže určiť, kde sa nachádzajú dátové pokyny a údaje - robí to hardvér počítača - takže nemôžete nútiť určité prvky do medzipamäte CPU. Môžete však optimalizovať svoj kód tak, aby načítal veľkosť medzipamäte L1, L2 alebo L3 vo vašom systéme, pomocou nástroja Windows Management Instrumentation (WMI) na optimalizáciu, keď vaša aplikácia pristupuje do medzipamäte, a teda k jej výkonu.

CPU nikdy nepristupujú k bajtu cache po bajtech. Namiesto toho čítajú pamäť v riadkoch vyrovnávacej pamäte, čo sú kúsky pamäte, ktoré majú zvyčajne veľkosť 32, 64 alebo 128 bajtov.

Nasledujúci zoznam kódov ilustruje, ako môžete načítať veľkosť medzipamäte procesora L2 alebo L3 vo vašom systéme:

public static uint GetCPUCacheSize (string cacheType) {try {using (ManagementObject managementObject = new ManagementObject ("Win32_Processor.DeviceID = 'CPU0'")) {return (uint) (managementObject [cacheType]); }} chytit {vratit 0; }} static void Main (string [] args) {uint L2CacheSize = GetCPUCacheSize ("L2CacheSize"); uint L3CacheSize = GetCPUCacheSize ("L3CacheSize"); Console.WriteLine ("L2CacheSize:" + L2CacheSize.ToString ()); Console.WriteLine ("L3CacheSize:" + L3CacheSize.ToString ()); Console.Read (); }

Spoločnosť Microsoft má k triede WMI Win32_Processor ďalšiu dokumentáciu.

Programovanie výkonu: Príklad kódu

Keď máte objekty v zásobníku, nad hlavou nie je žiadny odpad. Ak používate haldy založené objekty, s generačným zberom odpadkov za zhromažďovanie alebo premiestňovanie objektov v halde alebo zhutňovanie pamäte haldy sú spojené vždy náklady. Dobrým spôsobom, ako sa vyhnúť režijným nákladom, je použitie štruktúr namiesto tried.

Cache najlepšie fungujú, ak používate postupnú dátovú štruktúru, napríklad pole. Postupné radenie umožňuje CPU čítať ďalej a špekulatívne čítať dopredu v očakávaní toho, čo bude pravdepodobne požadované ďalej. Algoritmus, ktorý pristupuje k pamäti postupne, je teda vždy rýchly.

Ak pristupujete k pamäti v náhodnom poradí, procesor potrebuje pri každom prístupe do pamäte nové riadky vyrovnávacej pamäte. To znižuje výkon.

Nasledujúci úryvok kódu implementuje jednoduchý program, ktorý ilustruje výhody použitia štruktúry nad triedou:

 struct RectangleStruct {public int šírka; verejná int výška; } trieda RectangleClass {public int šírka; verejná int výška; }

Nasledujúci kód profiluje výkonnosť používania poľa štruktúr proti množine tried. Na ilustráciu som pre oba použil milión objektov, ale vo svojej aplikácii zvyčajne toľko objektov nepotrebujete.

static void Main (reťazec [] args) {const int veľkosť = 10 000 000; var structs = new RectangleStruct [veľkosť]; var classes = new RectangleClass [veľkosť]; var sw = nové stopky (); sw.Start (); for (var i = 0; i <size; ++ i) {structs [i] = new RectangleStruct (); štruktúry [i]. šírka = 0 štruktúr [i]. výška = 0; } var structTime = sw.ElapsedMilliseconds; sw.Reset (); sw.Start (); for (var i = 0; i <size; ++ i) {classes [i] = new RectangleClass (); triedy [i]. šírka = 0; triedy [i] .výška = 0; } var classTime = sw.ElapsedMilliseconds; sw.Stop (); Console.WriteLine ("Čas vyťažený z radu tried:" + classTime.ToString () + "milisekundy."); Console.WriteLine ("Čas potrebný pre pole štruktúr:" + structTime.ToString () + "milisekundy."); Console.Read (); }

Program je jednoduchý: Vytvára 1 milión objektov štruktúr a ukladá ich do poľa. Vytvára tiež 1 milión objektov triedy a ukladá ich do iného poľa. Šírke a výške vlastností je v každej inštancii priradená hodnota nula.

Ako vidíte, použitie štruktúr priateľských k medzipamäti poskytuje obrovský nárast výkonu.

Pravidlá pre lepšie využitie medzipamäte procesora

Ako teda napíšete kód, ktorý najlepšie využíva medzipamäť procesora? Bohužiaľ, neexistuje žiadny magický vzorec. Existujú však určité základné pravidlá:

  • Nepoužívajte algoritmy a dátové štruktúry, ktoré vykazujú nepravidelné vzory prístupu do pamäte; namiesto toho použite lineárne dátové štruktúry.
  • Používajte menšie typy údajov a usporiadajte ich tak, aby v nich neboli otvory na zarovnanie.
  • Zvážte prístupové vzorce a využite výhody lineárnych dátových štruktúr.
  • Vylepšite priestorovú lokalitu, ktorá využíva každý riadok medzipamäte v maximálnej miere po namapovaní na medzipamäť.
$config[zx-auto] not found$config[zx-overlay] not found