IPA - Lab.3 SSE instrukce, zarovnání dat Ondřej Klubal http://www.fit.vutbr.cz/~iklubal/IPA/
2015
Ondřej Klubal
IPA - Lab.3
1 / 25
Osnova
Data alignment Data alignment ve VS 2010 Příklad na data alignment Struktury a padding SSE úvod SSE a instrukce CPUID SSE komplexní čísla
Ondřej Klubal
IPA - Lab.3
2 / 25
Data alignment
CPU nečte paměť po bytech, ale čte ji po 2, 4, 8, 16 nebo 32 bytových blocích (memory access granularity). Výkonnost a architektura pamětí.
Ondřej Klubal
IPA - Lab.3
3 / 25
Data alignment - ukázka - špatné zarovnání Pokud jsou data špatně zarovnána k 4-bytové hranici, CPU musí udělat mnoho zbytečných kroků.
Ondřej Klubal
IPA - Lab.3
4 / 25
Data alignment - důsledky
Výrazné snížení výkonu při nezarovnaném přístupu (načítání i dat které nepotřebujeme) Některé architektury nezarovnané přístupy do paměti odmítají a vyvolají vyjímku. Alpha, IA-64, MIPS a SuperH architektury. 32bit x86 architektura původně nevyžaduje zarovnané přístupy do paměti. Nicméně od SSE existují instrukce,které vyžadují 16-bytové zarovnání dat.
Paralelismus. Operace čtení/zápis do na nezarovnanou adresu není atomická a tak jiné zařízení může číst/zapisovat do stejného paměťového bloku, pokud obsahuje jinou proměnnou. Takové chyby se těžko odhalují.
Ondřej Klubal
IPA - Lab.3
5 / 25
Data alignment - jazyk C/C++ a x86
char (1 byte) bude 1-byte aligned. short (2 bytes) bude 2-byte aligned. int (4 bytes) bude 4-byte aligned. float (4 bytes) bude 4-byte aligned. double (8 bytes) bude 8-byte aligned (Windows) a 4-byte aligned (Linux). long double (12 bytes) bude 4-byte aligned (Linux) pointer (4 bytes) bude 4-byte aligned (Linux). (e.g.: char*, int*)
Ondřej Klubal
IPA - Lab.3
6 / 25
Data alignment - statická alokace C/C++ __declspec( align( ♯ ) ) declarator __declspec(align(32)) double a; ♯ hodnota musí být mocnina dvou ♯ < sizeof(object) - ignoruje se Nezarovná data na hromadě (pouze statická/lokální)! Struktury, cache line... typedef __declspec(align(8)) struct { int a, b; } cacheStruct; __alignof(TYPE) vrátí nastavené zarovnání např. pro dynamickou alokaci.
Ondřej Klubal
IPA - Lab.3
7 / 25
Data alignment - dynamická alokace zarovnané paměti 1/2
Více možností jak alokovat zarovnaná data na hromadě: Úpravou adresy /* Make new p a pointer to a 64-bit aligned array of NUM_ELEMENTS 64-bit elements. */ double *p, *newp; p = (double*)malloc (sizeof(double)*(NUM_ELEMENTS+1)); newp = (p+7) & (~0x7);
Ondřej Klubal
IPA - Lab.3
8 / 25
Data alignment - dynamická alokace zarovnané paměti 2/2
Knihovna malloc.h void *_aligned_malloc(size_t size, size_t alignment) void *_aligned_offset_malloc(size_t size, size_t alignment, size_t offset) void _aligned_free(void *aligned_block) void *_aligned_realloc(void *aligned_block, size_t size, size_t alignment) void *_aligned_offset_realloc(void *aligned_block, size_t size, size_t alignment, size_t offset)
Ondřej Klubal
IPA - Lab.3
9 / 25
Data alignment - cache, stack Existence cache "problém" ještě více komplikuje. Můžeme mluvit o zarovnání na cache-line (32, 64 byte) Více úrovní cache (až tři), kde každá cache má jinou velikost (32 kB až 2 MB).
Zarovnání na stacku se liší od platformy. U x86 záleží na kompileru - prakticky nelze vynutit požadované zarovnání, zpravidla však 4 byty (heuristika kompileru). U x64 je dáno, že každý stackframe zásobníku je zarovnán na 16 bytů (128 bitů). Ondřej Klubal
IPA - Lab.3
10 / 25
Data alignment - řešení
8-bit data zarovnáme na libovolnou adresu 16-bit data - první zarovnáme na adresu dělitelnou 4, všechny další na adresu dělitelnou 2 32-bit data zarovnáme na adresu dělitelnou 4 64-bit data zarovnáme na adresu dělitelnou 8 80-bit data zarovnáme na adresu dělitelnou 16 128-bit data zarovnáme na adresu dělitelnou 16 256-bit data zarovnáme na adresu dělitelnou 32 512-bit data zarovnáváme na adresu dělitelnou 64
Ondřej Klubal
IPA - Lab.3
11 / 25
Data alignment - struktury a padding v C/C++
Struktura je rozšířena tak, aby velikost struktury byla nejmenším násobkem velikosti největšího prvku struktury.
Ondřej Klubal
IPA - Lab.3
12 / 25
Data alignment a SSE
Speciální datový typ __m128 (automaticky zarovnáno na 16 bytů, mapuje se na XMM registr). ♯include <xmmintrin.h> Lze nahradit typedef _declspec(align(16)) struct double a, b; xmm_reg; Nelze předávat hodnotou (__m128 ano)! SSE má instrukce i pro manipulaci s nezarovnanou paměti, avšak dojde ke snížení výkonu.
Ondřej Klubal
IPA - Lab.3
13 / 25
Data alignment a SSE - příklad s __m128
// Pole __m128 dat. typu __m128 xmma[50]; xmma[0] = _mm_setzero_ps(); 012F1690 xorps xmm0,xmm0 012F1693 movaps xmmword ptr [ebp-460h],xmm0
// Volání funkce s parametrem __m128 f(xmma[0]); // void __cdecl f(__m128 a) 012F16A8 movaps xmm0,xmmword ptr [ebp-370h] 012F16AF call f (12F1253h)
Ondřej Klubal
IPA - Lab.3
14 / 25
Data alignment a SSE - předávání parametrů
Ondřej Klubal
IPA - Lab.3
15 / 25
Data alignment a NASM ; ALIGN a ALIGNB makra align 4 ; zarovnání na 4-bytovou hranici align 4, resb 1 ; zarovnání na 4-bytovou hranici v bss alignb 4 ; stejné jako předchozí ... ; (viz NASM manuál)
První argument musí být mocninnou dvou. ALIGN - default NOP, ALIGNB - default RESB 1 ALIGN pro .text a .data sekci, ALIGNB pro .bss POZOR! Zarovnává pouze relativně vůči sekci, nikoliv vůči finální adrese! ALIGNB nebo ALIGN s RESB 1 může být použit pro zarovnání ve strukturách - dorovnává se vůči bázové adrese. struc mytype2 mt_byte: mt_word: mt_long: mt_str: endstruc
resb 1 alignb 2 resw 1 alignb 4 resd 1 resb 32 Ondřej Klubal
IPA - Lab.3
16 / 25
Úvod SSE
1999 - Pentium III - odpověd na instrukční sadu 3DNow! od AMD (1998). Původně známo pod názvem Katmai New Instruction (KNI). 70 nových instrukcí, 8 x 128 bitových registrů XMM (4x single precision) nové registry - nutná podpora OS (>=Win98,Linux kernel 2.4) AMD implementuje v procesoru AthlonXP. Kontrolní registr MXCSR - 32 bitový. Rozšíření instrukce CPUID.
Ondřej Klubal
IPA - Lab.3
17 / 25
SSE - instrukce 1 Skalární i vektorové S plovoucí desetinnou čárkou (pracující s xmm registry) přesuny (z paměti do registru, z registru do paměti, mezi registry) skalární - MOVSS vektorové - MOVAPS, MOVUPS, MOVLPS, MOVHPS, MOVLHPS, MOVHLPS
aritmetické (sčítání, odečítání, násobení, dělení, převrácená hodnota, určení maxima a minima, 2. mocnina a odmocnina) skalární - ADDSS, SUBSS, MULSS, DIVSS, RCPSS, MAXSS, MINSS, SQRTSS vektorové - ADDPS, SUBPS, MULPS, DIVPS, RCPPS, MAXPS, MINPS, SQRTPS, RSQRTPS
bitové operace vektorové - ANDPS, ORPS, XORPS, ANDNPS
porovnávání skalární - CMPSS, COMISS, UCOMISS vektorové - CMPPS Ondřej Klubal
IPA - Lab.3
18 / 25
SSE - instrukce 2 "zamíchání" a "rozbalení" dat (shuffle and unpack manipulace s jednotlivými bitovými částmi 128bitového registru) vektorové - SHUFPS, UNPCKHPS, UNPCKLPS
datový převod (MMX ↔ XMM) skalární - CVTSI2SS, CVTSS2SI, CVTTSS2SI vektorové - CVTPI2PS, CVTPS2PI, CVTTPS2PI
celočíselné (mmx registry (_m64) - rozšíření instrukcí MMX) aritmetické PMULHUW, PSADBW, PAVGB, PAVGW, PMAXUB, PMINUB, PMAXSW, PMINSW
přenos dat PEXTRW, PINSRW
ostatní správa MXCSR LDMXCSR, STMXCSR
správa cache a paměti MOVNTQ, MOVNTPS, MASKMOVQ, PREFETCH0, PREFETCH1, PREFETCH2, PREFETCHNTA, SFENCE Ondřej Klubal
IPA - Lab.3
19 / 25
SSE2, SSE3, ... x64 - navíc registry xmm8 - xmm15 SSE2 - (2001) podpora typu double celočíselné MMX instrukce rozšířeny na 128 bit registry XMM 144 nových instrukcí
SSE3 - (2004) "horizontální" přístup k registru, 13 nových instrukcí
SSSE3 (Supplemental Streaming SIMD Extensions 3) - (2006) SSE4.1 - (2006) - 54 nových instrukcí SSE4.2 - 7 nových instrukcí AES-NI - (2008) - 7 specializovaných instrukcí pro akcelereaci AES
Ondřej Klubal
IPA - Lab.3
20 / 25
AVX, AVX2, AVX512F AVX - Sandy Bridge (2011) rozšíření XMM registrů na 256 bitů - registry YMM nutná podpora OS: (>= Win 7 SP1, >= Linux kernel 2.6.30) instrukce se třemi operandy celočíselné operace pouze 128 bitů (SSE ve VEX)
AVX2 - Haswell (2013) rozšíření celočíselných instrukcí na 256 bitů FMA3 (fused multiply acumulate 3op a + b * c), Gather operace
AVX512 - Knights Landing (2016 Q3) 512 bit registry ZMM x64 - navíc registry zmm16 - zmm31 opět nutná podpora OS AVX512F (Foundation - povinný základ), AVX512VL (vector extensions), AVX512BW (byte/word), AVX512DQ (doubleword/quadword), AVX152PFI (prefetch), AVX512CRI (conflict detection), AVX512ERI (exponencial) Ondřej Klubal
IPA - Lab.3
21 / 25
SIMD - Hlavičkové soubory pro C/C++ x86intrin.h: x86 instructions mmintrin.h: MMX (Pentium MMX!) xmmintrin.h: SSE + MMX (Pentium 3, Athlon XP) emmintrin.h: SSE2 + SSE + MMX (Pentium 4, Ahtlon 64) pmmintrin.h: SSE3 + SSE2 + SSE + MMX (Pentium 4 Prescott, Ahtlon 64 San Diego) tmmintrin.h: SSSE3 + SSE3 + SSE2 + SSE + MMX (Core 2, Bulldozer) smmintrin.h: SSE4.1 + SSSE3 + SSE3 + SSE2 + SSE + MMX (Core i7, Bulldozer) nmmintrin.h: SSE4.2 + SSE4.1 + SSSE3 + SSE3 + SSE2 + SSE + MMX (Core i7, Bulldozer) wmmintrin.h: AES (Core i7 Westmere, Bulldozer) immintrin.h: AVX2, AVX, SSE4.2 + SSE4.1 + SSSE3 + SSE3 + SSE2 + SSE + MMX (Core i7 Sandy Bridge, Bulldozer) Ondřej Klubal
IPA - Lab.3
22 / 25
Úloha 1) SSE podpora v CPU
Doplňte v inline assembleru tělíčka funkcí v projektu sse_support tak, aby vracely true v případě podpory dané instrukční sady, jinak false. Otevřte si projekt sse_support Doplňte tělíčka funkcí Ověřte správnost implementace
Ondřej Klubal
IPA - Lab.3
23 / 25
Úloha 2) SSE počítání s kompexními čísly
Doplňte v inline assembleru tělíčka funkcí v projektu sse_complex_numbers (a + ib) + (c + id) = (a + c) + i(b + d) (a + ib) − (c + id) = (a − c) + i(b − d) (a + ib) · (c + id) = (ac − bd) + i(ad + bc) (a+ib)/(c +id) = [(ac +bd)/(c 2 +d 2 )]+i[(bc −ad)/(c 2 +d 2 )] |a + ib| = sqrt(a2 + b 2 )
Ondřej Klubal
IPA - Lab.3
24 / 25
Děkuji za Vaši pozornost
(příště SSE aplikace)
Ondřej Klubal
IPA - Lab.3
25 / 25