Programování

Jak zrychlit kód pomocí mezipaměti CPU

Mezipaměť CPU snižuje latenci paměti při přístupu k datům z hlavní systémové paměti. Vývojáři mohou a měli by využívat mezipaměť CPU ke zlepšení výkonu aplikace.

Jak fungují mezipaměti CPU

Moderní CPU mají obvykle tři úrovně mezipaměti, označené L1, L2 a L3, což odráží pořadí, ve kterém je CPU kontroluje. CPU často mají datovou mezipaměť, mezipaměť instrukcí (pro kód) a jednotnou mezipaměť (pro cokoli). Přístup k těmto mezipaměti je mnohem rychlejší než přístup k RAM: Typicky je mezipaměť L1 přibližně 100krát rychlejší než RAM pro přístup k datům a mezipaměť L2 je 25krát rychlejší než RAM pro přístup k datům.

Když váš software běží a potřebuje načíst data nebo pokyny, nejdříve se zkontrolují mezipaměti CPU, poté pomalejší systémová RAM a nakonec mnohem pomalejší diskové jednotky. Proto chcete svůj kód optimalizovat, abyste nejprve vyhledali, co bude pravděpodobně potřeba z mezipaměti CPU.

Váš kód nemůže určit, kde jsou umístěny datové pokyny a data - to dělá hardware počítače - takže nemůžete vynutit určité prvky do mezipaměti CPU. Můžete však optimalizovat svůj kód tak, aby načetl velikost mezipaměti L1, L2 nebo L3 ve vašem systému pomocí Windows Management Instrumentation (WMI) k optimalizaci, když vaše aplikace přistupuje k mezipaměti a tím k jejímu výkonu.

CPU nikdy nepřistupují k bajtům mezipaměti po bajtech. Místo toho čte paměť v řádcích mezipaměti, což jsou bloky velikosti obvykle 32, 64 nebo 128 bajtů.

Následující výpis kódu ukazuje, jak můžete načíst velikost mezipaměti CPU L2 nebo L3 ve vašem systému:

public static uint GetCPUCacheSize (string cacheType) {try {using (ManagementObject managementObject = new ManagementObject ("Win32_Processor.DeviceID = 'CPU0'")) {return (uint) (managementObject [cacheType]); }} chytit {návrat 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 (); }

Microsoft má další dokumentaci k třídě Win32_Processor WMI.

Programování výkonu: Příklad kódu

Když máte v zásobníku objekty, není nad hlavou žádný úklid. Pokud používáte haldy založené objekty, vždy existuje cena spojená s generačním sběrem odpadu pro shromažďování nebo přesunutí objektů v haldě nebo zhutnění paměti haldy. Dobrým způsobem, jak se vyhnout režii sběru odpadků, je použití struktur místo tříd.

Mezipaměti fungují nejlépe, pokud používáte sekvenční datovou strukturu, například pole. Sekvenční řazení umožňuje CPU číst dopředu a také číst dopředu spekulativně v očekávání toho, co bude pravděpodobně požadováno dále. Algoritmus, který přistupuje k paměti postupně, je tedy vždy rychlý.

Pokud přistupujete k paměti v náhodném pořadí, procesor potřebuje nové řádky mezipaměti pokaždé, když přistupujete k paměti. To snižuje výkon.

Následující fragment kódu implementuje jednoduchý program, který ilustruje výhody použití struktury nad třídou:

 struct RectangleStruct {public int šíře; veřejná int výška; } třída RectangleClass {public int šíře; veřejná int výška; }

Následující kód profiluje výkon použití pole struktur proti poli tříd. Pro ilustraci jsem pro oba použil milion objektů, ale obvykle ve své aplikaci tolik objektů nepotřebujete.

static void Main (řetězec [] args) {const int size = 10 000 000; var structs = new RectangleStruct [velikost]; var classes = new RectangleClass [velikost]; var sw = nové stopky (); sw.Start (); for (var i = 0; i <size; ++ i) {structs [i] = new RectangleStruct (); struktury [i]. šířka = 0 struktury [i]. výška = 0; } var structTime = sw.ElapsedMilliseconds; sw.Reset (); sw.Start (); for (var i = 0; i <size; ++ i) {classes [i] = new RectangleClass (); třídy [i]. šířka = 0; třídy [i] .výška = 0; } var classTime = sw.ElapsedMilliseconds; sw.Stop (); Console.WriteLine ("Čas zabraný řadou tříd:" + classTime.ToString () + "milisekundy."); Console.WriteLine ("Čas potřebný pro pole struktur:" + structTime.ToString () + "milisekundy."); Console.Read (); }

Program je jednoduchý: Vytváří 1 milion objektů struktur a ukládá je do pole. Vytvoří také 1 milion objektů třídy a uloží je do jiného pole. Šířce a výšce vlastností je v každé instanci přiřazena hodnota nula.

Jak vidíte, použití struktur vhodných pro mezipaměť poskytuje obrovský nárůst výkonu.

Pravidla pro lepší využití mezipaměti CPU

Jak tedy napíšete kód, který nejlépe využívá mezipaměť CPU? Bohužel neexistuje žádný magický vzorec. Existují však některá základní pravidla:

  • Nepoužívejte algoritmy a datové struktury, které vykazují nepravidelné vzory přístupu k paměti; místo toho použijte lineární datové struktury.
  • Použijte menší datové typy a uspořádejte data tak, aby v nich nebyly žádné díry pro zarovnání.
  • Zvažte přístupové vzory a využijte výhod lineárních datových struktur.
  • Vylepšete prostorovou lokalitu, která využívá každý řádek mezipaměti v maximálním rozsahu, jakmile je mapován do mezipaměti.
$config[zx-auto] not found$config[zx-overlay] not found