Kako ubrzati kôd pomoću CPU predmemorije

Predmemorija CPU smanjuje kašnjenje memorije kada se podacima pristupa iz glavne sistemske memorije. Programeri mogu i trebaju iskoristiti predmemoriju CPU-a za poboljšanje performansi aplikacije.

Kako rade CPU predmemorije

Moderni CPU obično imaju tri razine predmemorije, označene L1, L2 i L3, što odražava redoslijed kojim ih CPU provjerava. CPU često imaju predmemoriju podataka, predmemoriju uputa (za kod) i objedinjenu predmemoriju (za bilo što). Pristup tim predmemorijama mnogo je brži od pristupa RAM-u: Tipično je L1 predmemorija oko 100 puta brža od RAM-a za pristup podacima, a L2 predmemorija je 25 puta brža od RAM-a za pristup podacima.

Kada se softver pokrene i treba uvući podatke ili upute, najprije se provjeravaju CPU predmemorije, zatim sporiji RAM sustava i na kraju puno sporiji pogoni diskova. Zbog toga želite optimizirati svoj kôd da biste prvo tražili ono što će vjerojatno trebati iz predmemorije CPU-a.

Vaš kôd ne može odrediti gdje se nalaze podatkovne upute i podaci - računalni hardver to radi - pa neke elemente ne možete prisiliti u predmemoriju CPU-a. Ali svoj kod možete optimizirati da biste dohvatili veličinu predmemorije L1, L2 ili L3 u vašem sustavu pomoću Windows Management Instrumentation (WMI) kako biste optimizirali kada vaša aplikacija pristupa predmemoriji, a time i njezine performanse.

CPU nikad ne pristupaju bajtu po bajtu. Umjesto toga, memoriju čitaju u predmemorijskim linijama, što su dijelovi memorije, općenito veličine 32, 64 ili 128 bajtova.

Sljedeći popis kodova ilustrira kako možete dohvatiti veličinu predmemorije CPU L2 ili L3 u svom sustavu:

javna statička uint GetCPUCacheSize (string cacheType) {try {using (ManagementObject managementObject = new ManagementObject ("Win32_Processor.DeviceID = 'CPU0'")) {return (uint) (managementObject [cacheType]); }} catch {povratak 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 ima dodatnu dokumentaciju o klasi WMI Win32_Processor.

Programiranje za izvedbu: Primjer koda

Kada imate predmete u hrpi, ne dolazi se do sakupljanja smeća. Ako koristite objekte koji se temelje na hrpi, uvijek postoji trošak generacijskog sakupljanja smeća za prikupljanje ili premještanje predmeta u hrpi ili zbijanje memorije hrpe. Dobar način za izbjegavanje skupljanja smeća je upotreba struktura umjesto klasa.

Predmemorije najbolje funkcioniraju ako koristite sekvencijalnu strukturu podataka, poput niza. Sekvencijalno naručivanje omogućuje CPU-u da čita unaprijed, a također i špekulativno čitajući u očekivanju onoga što će vjerojatno biti zatraženo sljedeće. Dakle, algoritam koji uzastopno pristupa memoriji uvijek je brz.

Ako memoriji pristupate slučajnim redoslijedom, CPU treba nove predmemorije svaki put kad pristupite memoriji. To smanjuje performanse.

Sljedeći isječak koda implementira jednostavan program koji ilustrira prednosti korištenja strukture nad klasom:

 struct RectangleStruct {širina javnog int; visina javnog int; } class RectangleClass {public int width; visina javnog int; }

Sljedeći kod profilira izvedbu korištenja niza struktura protiv niza klasa. U svrhu ilustracije upotrijebio sam milijun objekata za oba, ali obično vam nije potrebno toliko objekata u vašoj aplikaciji.

statička praznina Main (string [] args) {const int size = 1000000; var structs = novi RectangleStruct [veličina]; var klase = novi RectangleClass [veličina]; var sw = nova Štoperica (); sw.Start (); for (var i = 0; i <size; ++ i) {structs [i] = new RectangleStruct (); structs [i] .breadth = 0 structs [i] .height = 0; } var structTime = sw.ElapsedMilliseconds; sw.Reset (); sw.Start (); for (var i = 0; i <size; ++ i) {classes [i] = new RectangleClass (); klase [i] .širina = 0; klase [i] .height = 0; } var classTime = sw.ElapsedMilliseconds; sw.Stop (); Console.WriteLine ("Vrijeme potrebno nizom klasa:" + classTime.ToString () + "milisekunde."); Console.WriteLine ("Vrijeme potrebno nizom struktura:" + structTime.ToString () + "milisekunde."); Console.Read (); }

Program je jednostavan: stvara milijun objekata objekata i pohranjuje ih u niz. Također stvara milijun objekata klase i pohranjuje ih u drugi niz. Širina i visina svojstava dodjeljuju se vrijednosti nula na svakoj instanci.

Kao što vidite, upotreba struktura prilagođenih predmemoriji pruža ogroman dobitak u performansama.

Pravila za bolje korištenje predmemorije CPU-a

Pa, kako napisati kod koji najbolje koristi CPU predmemoriju? Nažalost, nema čarobne formule. Ali postoje neka osnovna pravila:

  • Izbjegavajte upotrebu algoritama i struktura podataka koji pokazuju nepravilne obrasce pristupa memoriji; umjesto toga koristite linearne strukture podataka.
  • Koristite manje vrste podataka i organizirajte podatke tako da nema rupa za poravnanje.
  • Razmotrite obrasce pristupa i iskoristite linearne strukture podataka.
  • Poboljšajte prostorni lokalitet koji koristi svaku liniju predmemorije u maksimalnoj mjeri nakon što je preslikana u predmemoriju.