Top Banner
Hatékony CPU kód írásához hasznos architekturális háttér Valasek Gábor
74

Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Jun 24, 2020

Download

Documents

dariahiddleston
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Hatékony CPU kódírásához hasznos architekturális háttér

Valasek Gábor

Page 2: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Hatékony CPU kódírásához hasznos architekturális háttér,

Valasek Gábor

Mentegetőzések

hogy miért is lassú a kódom

Page 3: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Műveletvégzés

+4

9

13

Page 4: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Műveletvégzés

+4

9

13

Page 5: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Műveletvégzés

+4

9

13

Page 6: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Műveletvégzés

+4

9

13

Intel szintaxis: mnemonic src, dst

Page 7: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Regiszterek

http://www.cs.virginia.edu/~evans/cs216/guides/x86.html

Page 8: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

UtasításokHárom fő kategória:

● adatmozgatás: mov, push, pop stb.● aritmetikai/logikai műveletek: add, sub, imul, mul, div, and, xor stb.● control flow: jmp, je, jne, call, ret stb.

Page 9: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Műveletvégzés

+4

9

13

Page 10: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Műveletvégzés

+4

9

13

Page 11: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Műveletvégzés

+4

9

13

Page 12: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Tartalom1. Pipeline-ing, szuperskalár architektúra2. A programunk a memóriában3. Cache-ek: I cache és D cache4. Néhány probléma (stalling, branch prediction, load-hit-store)

Page 13: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Tartalom1. Pipeline-ing, szuperskalár architektúra2. A programunk a memóriában3. Cache-ek: I cache és D cache4. Néhány probléma (stalling, branch prediction, load-hit-store)

Page 15: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Pipeline architektúrákKét fontos statisztikájuk van:

● Latency: egy utasítás végrehajtásához szükséges teljes idő. ● Bandwidth vagy throughput: egységnyi idő alatt hány utasítást tud feldolgozni

a rendszer. Ez a leglassabb komponenstől függ.

Page 16: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Szuperskalár architektúrák

Page 17: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Szuperskalár architektúrák

Page 18: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Szuperskalár architektúrák● A pipeline egyes fázisait megvalósító egységeknek több, redundáns példánya

is van● Ezáltal egyszerre több utasítás is végrehajtható párhuzamosan● Például az Intel processzorok pipeline-os és szuperskalár architektúrák● Érdeklődőknek további anyagok:

http://www.lighterra.com/papers/modernmicroprocessors/

Page 20: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Hatékony kód régen és most● Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a

számítások számát● Ez ma már megfordult: a mantra az, hogy inkább végezz több munkát a

ALU-kkal és csak akkor nyúlj a memóriához, ha feltétlenül szükséges● Ennek oka az elérési idők módosulása (forrás:

https://www.theregister.co.uk/2016/04/21/storage_approaches_memory_speed_with_xpoint_and_storageclass_memory/ )

Page 21: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Elérési idők

Page 22: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Utasítások költsége

Page 23: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

IrodalomEgy nagyon hasznos bejegyzés erről a témáról: http://ithare.com/infographics-operation-costs-in-cpu-clock-cycles/

Page 24: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Kitérő● Miért olyan drága az egésszel számokkal való osztás? Mindig ugyanolyan

drága? Mi a helyzet a lebegőpontos számokkal?● Milyen algoritmusokat használnak a szokásos művetek megvalósítására

CPU-ban?● Miket használnak a kevésbé szokásos műveletekre (sin, cos stb.)? ● Sok válasz megtalálható az ilyen jellegű kérdésekre Jean Michel Muller

könyveiben:○ https://www.amazon.com/Handbook-Floating-Point-Arithmetic-Jean-Michel-Muller/dp/0817647

04X○ https://www.amazon.com/Elementary-Functions-Implementation-Jean-Michel-Muller/dp/14899

79816/ref=sr_1_2?s=books&ie=UTF8&qid=1537995137&sr=1-2

Page 25: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Tartalom1. Pipeline-ing, szuperskalár architektúra2. A programunk a memóriában3. Cache-ek: I cache és D cache4. Néhány probléma (stalling, branch prediction, load-hit-store)

Page 26: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Program a memóriában● A végrehajtható fájl lehet egy .exe (Windows) vagy .elf (executable and

linking format - Unix)● A lefordított és összelinkelt végrehajtható fájl tartalmazza a program

executable image-ét● Ez egy részleges képe annak, ahogyan a program memóriában ki fog nézni -

a dinamikus memóriafoglalásokat értelemszerűen nem tartalmazza● Az image 4 szegmensből áll

Page 27: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Execution image

Code/text segment

Data segment

BSS segment

Read-only data segment

Page 28: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Execution image

Végrehajtható gépikód

A kódban inicializált globális illetve statikus

változók

Inicializálatlan globális és statikus változók

Csak olvasható adatok Egyes esetekben (ún. manifest constant-oknál) a kódba fordul

bele a konstans értéke. Ilyen pl. az int konstans.

Az értékük a specifikáció szerint adott (=0), de csak a program belépési pontjának meghívása

előtt nullázódik ki.

Page 29: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Execution image● A BSS megnevezés történelmi hagyaték: a “block started by symbol”

rövidítése● A manifest konstansok trükkösek: mivel a compiler az értéküket

belehelyettesíti a kódba, ezért az ő tárolásuk valójában a kódszegmensben történik

Page 30: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Endian● Az egy bájtnál nagyobb foglalású változók esetén jön képbe● Kétféle verziója van:

○ Little endian: a kisebb helyiértéket reprezentáló bájtok a memória elejéhez vannak közelebb○ Big endian: a legnagyobb helyiérték bájtja van a kisebb memóriacímen

● Fontos, hogy min fejlesztünk és mire fejlesztünk:○ Intel processzorok: little endian-ok○ Wii, Xbox 360, PlayStation 3 (vagyis PowerPC hajtotta konzolok): big endian-ok

Page 31: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Program végrehajtása● A belépési pont elindításával kezdődik (pl. main() )● A futtatás megkezdésekor az OS lefoglal egy memóriaterületet az

alkalmazásnak, amit program stack-nek hívnak● Minden egyes függvényhíváskor erre a stack-re push-olnak egy összefüggő

(=folytonos) memóriafoglalást, amit stack frame-nek hívnak

Page 32: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Stack frameHáromféle adatot tárolunk itt:

● A függvényünket hívó függvény memóriacímét, hogy a visszatérésünk után folytatódhasson a program futtatása

● A CPU regisztereinek értékeit a hívás pillanatában. Visszatéréskor ezeket az értékeket visszaírjuk a regiszterekbe.

○ A függvény visszatérési értéke viszont általában egy speciális regiszterbe kerül, amit értelemszerűen nem állítunk vissza

● A függvény lokális változói is itt kerülnek foglalásra. (Meg néha regiszterekben)

Page 33: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Példavoid c() { U32 localC1; ... }F32 b() {

F32 localB1; I32 localB2;...c(); return localB1;

}void a() {

U32 aLocalsA1[5];...F32 localA2 = b();...

}

Page 34: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások
Page 35: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások
Page 36: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások
Page 37: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Változók helye a memóriában● A globális és a statikus változók a futtatható fájlban vannak● A lokális változók a stack-re kerülnek● A dinamikus változók azonban a heap-re

○ A probléma ezzel az, hogy az OS-től függ a foglalás○ Ezért váratlanul sokat állhat a programunk, amíg a new vissza nem tér (...legalábbis 4 new a

6-ból (<C++17)/8-ból(>=C++17)..)

Page 38: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Objektumok elhelyezése a memóriábanstruct Foo

{

U32 mUnsignedValue;

F32 mFloatValue;

I32 mSignedValue;

};

Page 39: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Objektumok elhelyezése a memóriábanstruct InefficientPacking

{

U32 mU1; // 32 bits

F32 mF2; // 32 bits

U8 mB3; // 8 bits

I32 mI4; // 32 bits

bool mB5; // 8 bits

char* mP6; // 32 bits

};

Page 40: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Objektumok elhelyezése a memóriábanstruct InefficientPacking

{

U32 mU1; // 32 bits

F32 mF2; // 32 bits

U8 mB3; // 8 bits

I32 mI4; // 32 bits

bool mB5; // 8 bits

char* mP6; // 32 bits

};

Page 41: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Objektumok elhelyezése a memóriában● Alignment: az objektum címe a memóriában az alignment méretének egy

egész számú többszöröse kell, hogy legyen.

Page 42: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Objektumok elhelyezése a memóriában● Alignment: az objektum címe a memóriában az alignment méretének egy

egész számú többszöröse kell, hogy legyen.

Page 43: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Alignment● Az alignment mérete adattípustól függ

○ például 32 bites típusok általában 4 bájtos rácsra illesztendők○ feltehetjük rule of thumb-ként, hogy a típus mérete határozza meg, hogy milyen rácsra kell

illeszteni az objektum kezdetét

● A objektumok/struktúrák adattagjaira ezek külön-külön is érvényesek ○ +array context padding: azaz ha a struktúra példányaiból van egy tömbünk, akkor az i-edik

elem utolsó adattagja után a memóriában annyival jön az i+1-edik elem első adattagja, hogy a méretének megfelelő alignment-en legyen

Page 44: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Tartalom1. Pipeline-ing, szuperskalár architektúra2. A programunk a memóriában3. Cache-ek: I cache és D cache4. Néhány probléma (stalling, branch prediction, load-hit-store)

Page 45: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Cache● A CPU által írható és olvasható memóriadarab, aminek kialakításánál a cél a

minél kisebb késleltetés● Ezt kétféleképpen érik el:

○ A lehető leggyorsabb memóriatechnológiát használják hozzájuk○ A cache-ek fizikailag is közelebb vannak a CPU-hoz

● A cache lényegében a globális memóriában lévő változóknak egy (CPU szempontjából) lokális másolata

● Méghozzá azoké, amiket gyakran lekérdez a program● Így ha épp cache-ben van, akkor nem kell elzarándokolni a RAM-ig (ez a

cache hit)● Ha nem volt a cache-ben (cache miss), elmegyünk a RAM-ig, visszahozzuk

az adatot de egyúttal beírjuk a cache-be is (hátha legközelebb is kell)

Page 46: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Cache line● Amikor egy új memóriádarabot érünk el cache miss miatt, nem csak az

általunk kért adatok jönnek vissza, hanem egy cache line-nyi darab○ i7-es archiektúrákon a L1, L2, L3 cache line-ok 64 byte-osak

● Így ha a programunk következő utasítása az előzőleg lekért memória utáni memóriát olvasná, akkor garantált (*) cache hit-ünk van

● A cache-ek asszociatív memóriák: tudják, hogy melyik RAM-beli valódi memóriaterület van bennük (ehhez használják a translation lookaside buffer-t, azaz a TLB-t)

Page 47: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Cache

Page 48: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Cache - írási policy● Amikor a programunk egy változó értékét módosítja, akkor azt vissza kell

juttatni a memóriába is● Ez a CPU architektúra write policy-jétől függ, hogy miképp történik● Az írások bekerülnek a cache-be, aztán

○ A write-through cache-ek az írásokat rögtön továbbítják a memóriába is○ A write-back policy-k pedig csak bizonyos esetekben (pl. cache miss) írnak vissza

Page 49: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Többszintű cache● Probléma, hogy kisméretű cache

○ Nagyon gyors○ De sok cache miss-t generál

● A nagyméretű viszont○ Fizikailag nem tud olyan közel kerülni a CPU-hoz○ De cserébe több cache hit lesz

● Ezért csinálnak többszintű cache-eket, egyre nagyobb méretben

Page 50: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Cache-ek többmagos környezetben

Page 51: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Cache-ek többmagos környezetben● Többmagos környezetben előjön a cache konzisztencia problémája● Vagyis hogy minden mag lokális cache-e a fizikai memóriában található valódi

értéket tükrözze● Két elterjedt protokoll erre a MESI (modified, exclusive, shared, invalid) és a

MOESI (modified, owned, exclusive, shared, invalid): https://en.wikipedia.org/wiki/MOESI_protocol

Page 52: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

A kétféle cache● Instruction cache: a programunk kódját cache-eli. Elágazásokkal ezt tudjuk

tönkrevágni, ezért van szükség branch prediction-re stb.● Data cache: a programunk adatait cache-eli● A fenti kettő független egymástól

Page 53: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

D-cache-re optimalizálásAz adatainkat

● a memóriában folytonosan tároljuk,● a lehető legkisebb méretben● és szekvenciálisan dolgozzuk fel őket

Page 54: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

I-cache-re optimalizálás● A teljesítmény-kritikus ciklusok a lehető legrövidebbek legyenek kódméret

szempontjából● Ne hívjunk belőlük függvényeket

○ Ha mégis kell és kicsi a függvény, akkor inline-oljuk○ Ha nem kicsi és csak igazi függvényhívással érhető el, ekkor érjük el, hogy a memóriában a

függvény gépi kódja a ciklushoz a lehető legközelebb legyen

Page 55: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

I-cache-re optimalizálás● Az utóbbi nagyon nehéz, mert a linker és a compiler dönti el, hogy fizikailag

hová kerül az executable image-ben a függvény törzse● Viszont van néhány rule-of-thumb amit általában be szoktak tartani:

○ Egy függvény kódja a memóriában szinte mindig folytonos, azaz a linker nem szúr bele más kódot a függvénybe (kivéve ha az a más kód egy inline-olt függvény hívása)

○ A függvények a fordítási egységük forráskódjában (.cpp) található sorrendjüknek megfelelően kerülnek a memóriába

○ Vagyis egyetlen fordítási egység függvényei a memóriában folytonosan helyezkednek el (általában)

Page 56: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Tartalom1. Pipeline-ing, szuperskalár architektúra2. A programunk a memóriában3. Cache-ek: I cache és D cache4. Néhány probléma (stalling, branch prediction, load-hit-store)

Page 57: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Stalling● Előfordulhat, hogy egy utasítás végrehajtása csak akkor kezdődhet meg,

hogy ha egy előtte lévő utasítás teljesen végigment a pipeline-on● Ekkor a függő utasítás beakasztja a pipeline-t - ez a stalling

Page 58: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Stalling - adatfüggőség miatt

Page 59: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Stalling● A fenti példában az add utasításnak be kellett várni a mul befejeződését● Az ilyenek elkerülése érdekében a compilerek megpróbálják automatikusan

átrendezni az utasításaink sorrendjét, hogy a függés miatt várakozó utasítás helyett független utasítások futhassanak

Page 60: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Branch prediction● Amikor a végrehajtás elágazáshoz ér, akkor a pipeline-nak döntenie kell,

hogy a then vagy az else ág kódját kezdi végrehajtani● Ha rosszul tippelt, akkor a pipeline-t flush-olni kell (az eddigi műveleteket

érvényteleníteni) és újratölteni a helyes elágazási irány kódjával● A legegyszerűbb (statikus) stratégia a CPU részéről hogy a backward

branch-et tekinti valószínűbbnek (azaz az elágazás azon ágát, aminek a gépi kód memóriacíme kisebb, mint az aktuális cím - ilyen például az az eset, amikor a ciklusunk még folytatja a futását)

○ Következmény: if-nél, többfelé ágazó if-nél, switch-case-nél először a leggyakoribb eseteket rakjátok

Page 61: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Stalling + branching megoldások + O(e) => meltdown

Page 62: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Load-Hit-Store● A probléma lényege, hogy egy változóba történő írás után közvetlenül

próbáljuk újra olvasni a változó értékét● A klasszikus példa: az oris-en stallol:

stfs fr3,0(r3) ;Store the float lwz r9,0(r3) ;Read it back into an integer register oris r9,r9,0x8000 ;Force to negative

Page 63: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Load-Hit-Storeint CauseLHS( int *ptrA ) {

int a,b;

int * ptrB = ptrA; // B and A point to the same direction

*ptrA = 5; // Write data to address prtA

b = *ptrB; // Read that data back again (won't be available for 40/80 cycles)

a = b + 10;// Stall! The data b isn't available yet

}

Nem lehetett *ptrA értékét regiszterben/cache-ben tartani, mert nem tudja a compiler, hogy nem

módosította-e a ptrA területét egy másik pointer a függvényben, ami ugyanoda mutat!

Page 64: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Load-Hit-Storeint slow( int * a, int * b) {

*a = 5;

*b = 7;

return *a + *b;// Stall! The compiler doesn't know whether

// a==b, so it has to reload both

// before the add

}

Page 65: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Irodalom és javasolt néznivalók● Jason Gregory: Game Engine Architecture (az ábrák forrása is ez)● Agner: Optimization manuals (ingyenes)● Kitekintésnek C++ HPC workshop:

https://www.youtube.com/watch?v=7xwvLFzRKsk&list=PL1tk5lGm7zvQh6RkurOpDmDOmhB6LzcWL

Page 66: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Polinomok kiértékelése● Forrás: http://lolengine.net/blog/2011/9/17/playing-with-the-cpu-pipeline ● A probléma: hogyan lehet kiértékelni egy polinomot hatékonyan?● Most: a sin(x) hatványsoros közelítését, elvágva az x^15-edikenes tagnál

Page 67: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Polinomok kiértékelésestatic double a0 = +1.0;static double a1 = -1.666666666666580809419428987894207e-1;static double a2 = +8.333333333262716094425037738346873e-3;static double a3 = -1.984126982005911439283646346964929e-4;static double a4 = +2.755731607338689220657382272783309e-6;static double a5 = -2.505185130214293595900283001271652e-8;static double a6 = +1.604729591825977403374012010065495e-10;static double a7 = -7.364589573262279913270651228486670e-13;

double sin1(double x){ return a0 * x + a1 * x * x * x + a2 * x * x * x * x * x + a3 * x * x * x * x * x * x * x + a4 * x * x * x * x * x * x * x * x * x + a5 * x * x * x * x * x * x * x * x * x * x * x + a6 * x * x * x * x * x * x * x * x * x * x * x * x * x + a7 * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x;}

64 szorzás + 7 összeadás

Page 68: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Polinomok kiértékelésedouble sin2(double x){ double ret,

y = x, x2 = x * x;

ret = a0 * y; y *= x2; ret += a1 * y; y *= x2; ret += a2 * y; y *= x2; ret += a3 * y; y *= x2; ret += a4 * y; y *= x2; ret += a5 * y; y *= x2; ret += a6 * y; y *= x2; ret += a7 * y; return ret;}

16 szorzás + 7 összeadás

Page 69: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Polinomok kiértékelésedouble sin3(double x) // Horner{ double x2 = x * x; return x * (a0 + x2 * (a1 + x2 * (a2 + x2 * (a3 + x2 * (a4 + x2 * (a5 + x2 * (a6 + x2 * a7)))))));}

9 szorzás + 7 összeadás

Page 70: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

MérésekIntel® Core™ i7-2620M CPU at 2.70GHz. The functions were compiled using -O3 -ffast-math:

function sin sin1 sin2 sin3nanoseconds per call 22.518 16.406 16.658 25.276

Page 71: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

MérésekIntel® Core™ i7-2620M CPU at 2.70GHz. The functions were compiled using -O3 -ffast-math:

function sin sin1 sin2 sin3nanoseconds per call 22.518 16.406 16.658 25.276

Page 72: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Polinomok kiértékelésedouble sin3(double x) // Horner{ double x2 = x * x; return x * (a0 + x2 * (a1 + x2 * (a2 + x2 * (a3 + x2 * (a4 + x2 * (a5 + x2 * (a6 + x2 * a7)))))));}

Page 73: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Polinomok kiértékelésedouble sin2(double x){ double ret,

y = x, x2 = x * x;

ret = a0 * y; y *= x2; ret += a1 * y; y *= x2; ret += a2 * y; y *= x2; ret += a3 * y; y *= x2; ret += a4 * y; y *= x2; ret += a5 * y; y *= x2; ret += a6 * y; y *= x2; ret += a7 * y; return ret;}

Page 74: Hatékony CPU kódcg.elte.hu/~gpgpu/opengl/GyorsCPUKod.pdf · Hatékony kód régen és most Amíg az órajelek relatíve alacsonyak voltak, megérte minimalizálni a számítások

Polinomok kiértékelése● Kézzel optimalizálni:

● Persze nem árt a compiler flag-ekkel is kicsit foglalkozni (csak -O3):

● De akár használhatsz irodalmat is: Estrin séma

function sin sin1 sin2 sin3 sin4 sin5 sin6 sin7nanoseconds per call 22.518 16.406 16.658 25.276 18.666 18.582 16.366 17.470

function sin sin1 sin2 sin3 sin4 sin5 sin6 sin7nanoseconds per call 22.497 30.250 19.865 25.279 18.587 18.958 16.362 15.891