Obsah
1. Rozšíření instrukční sady „Advanced Vector Extensions“ na platformě x86–64
2. Od SSE k AVX a posléze k AVX-512
3. Datové typy podporované v instrukčních sadách AVX a AVX2
5. Nový způsob kódování instrukcí
7. Operace součtu vektorů o délce 256 bitů s celočíselnými prvky bez znaménka
8. Překlad příkladu do assembleru s využitím instrukcí SSE, AVX, nebo AVX2
9. Operace součtu vektorů o délce 256 bitů s celočíselnými prvky se znaménkem
10. Překlad příkladu do assembleru s využitím instrukcí SSE, AVX, nebo AVX2
11. Operace součtu vektorů o délce 256 bitů s prvky typu float a double
12. Překlad příkladu do assembleru s využitím instrukcí SSE, AVX, nebo AVX2 – varianta pro float
13. Překlad příkladu do assembleru s využitím instrukcí SSE, AVX, nebo AVX2 – varianta pro double
14. Intrinsic v GCC pro instrukce AVX
15. Intrinsic v GCC pro instrukce AVX2
16. Intrinsic __builtin_ia32_addps256 – součet vektorů s prvky typu float
17. Intrinsic __builtin_ia32_addpd256 – součet vektorů s prvky typu double
18. Výběr prvků do cílového vektoru pomocí intrinsic __builtin_ia32_blendps256
19. Repositář s demonstračními příklady
1. Rozšíření instrukční sady „Advanced Vector Extensions“ na platformě x86–64
V pořadí již šestý článek o podpoře SIMD operací v překladači GCC C je věnován rozšíření instrukční sady nazvané „Advanced Vector Extension“ neboli zkráceně pouze AVX. Oproti již popsaným rozšířením MMX či SSE se jedná o výrazné vylepšení podpory SIMD operací, které se mj. projevilo prodloužením vektorů a zcela novými instrukcemi, takže slovo „advanced“ je zde namístě. Původní technologie AVX byla představena v roce 2008, přičemž první mikroprocesory vybavené tímto rozšířením začaly být ve větších sériích prodávány v roce 2011. Jedná se tedy o (z pohledu vývojáře) relativně novou technologii, která je však v samotném hardware dnes již široce podporována a lze ji bez větších problémů začít využívat.
Původní rozšíření AVX bylo v roce 2013 doplněno rozšířením nazvaným AVX2 a jen o několik měsíců později byla představena specifikace nazvaná AVX-512. V tomto případě se nejedná o jediné rozšíření instrukční sady, ale o specifikaci hned několika rozšíření, které mohou být (ale nemusí) všechny implementovány (většinou bude implementována jen určitá podmnožina AVX-512). AVX-512 je v mnoha ohledech stejně přelomové jako přechod na AVX, ovšem prozatím ho doprovází technické problémy – některé procesory se při vykonávání nových instrukcí AVX-512 zpomalují, a to mnohdy až na 60% svého limitu (to však již poněkud předbíháme).
2. Od SSE k AVX a posléze k AVX-512
Na technologii AVX se můžeme dívat jako na další krok, kterým se původně čistě skalární architektura x86 postupně rozšiřuje o vektorové operace. V souladu s Moorovým zákonem totiž mohou výrobci mikroprocesorů vytvářet na čipech nové (a delší) registry, zvětšovat počet aritmeticko-logických jednotek atd., což nepřímo přináší i nutnost změn (rozšíření) instrukční sady. A přesně tento postupný vývoj můžeme vidět i na platformě x86 a x86–64, která byla rozšířena o MMX, SSE(x), AVX(x) a nyní o AVX-512. Kromě rozšíření instrukční sady se postupně zvětšoval i počet pracovních registrů a taktéž jejich šířka. Ostatně tyto změny jsou patrné i z následující tabulky:
# | Typ registrů | Počet registrů (x86) | Počet registrů (x86–64) | Bitová šířka registru | Jména registrů |
---|---|---|---|---|---|
1 | Pracovní registry MMX | 8 | 8 | 64 bitů | MM0 .. MM7 |
2 | Pracovní registry SSE | 8 | 16 | 128 bitů | XMM0 .. XMM7 (XMM15) |
3 | Pracovní registry AVX | 8 | 16 | 256 bitů | YMM0 .. YMM7 (YMM15) |
4 | Pracovní registry AVX-512 | 32 | 32 | 512 bitů | ZMM0 .. ZMM31 |
Z výše uvedené tabulky je patrný dramatický skok v případě AVX-512, kdy se zdvojnásobil (zečtyřnásobil) počet registrů a současně se i zdvojnásobila jejich bitová šířka. Ve skutečnosti vznikly nové registry AVX se jmény YMMx i registry AVX-512 se jmény ZMMx rozšířením registrů SSE na 256 nebo 512 bitů a přidáním nových registrů. To například znamená, že operace s registrem XMM0 ve skutečnosti může změnit spodních 128 bitů registru YMM0 i ZMM0:
512..256 | 255..128 | 127..0 |
---|---|---|
ZMM0 | YMM0 | XMM0 |
ZMM1 | YMM1 | XMM1 |
ZMM2 | YMM2 | XMM2 |
ZMM3 | YMM3 | XMM3 |
ZMM4 | YMM4 | XMM4 |
ZMM5 | YMM5 | XMM5 |
ZMM6 | YMM6 | XMM6 |
ZMM7 | YMM7 | XMM7 |
ZMM8 | YMM8 | XMM8 |
ZMM9 | YMM9 | XMM9 |
ZMM10 | YMM10 | XMM10 |
ZMM11 | YMM11 | XMM11 |
ZMM12 | YMM12 | XMM12 |
ZMM13 | YMM13 | XMM13 |
ZMM14 | YMM14 | XMM14 |
ZMM15 | YMM15 | XMM15 |
ZMM16 | YMM16 | XMM16 |
ZMM17 | YMM17 | XMM17 |
ZMM18 | YMM18 | XMM18 |
ZMM19 | YMM19 | XMM19 |
ZMM20 | YMM20 | XMM20 |
ZMM21 | YMM21 | XMM21 |
ZMM22 | YMM22 | XMM22 |
ZMM23 | YMM23 | XMM23 |
ZMM24 | YMM24 | XMM24 |
ZMM25 | YMM25 | XMM25 |
ZMM26 | YMM26 | XMM26 |
ZMM27 | YMM27 | XMM27 |
ZMM28 | YMM28 | XMM28 |
ZMM29 | YMM29 | XMM29 |
ZMM30 | YMM30 | XMM30 |
ZMM31 | YMM31 | XMM31 |
3. Datové typy podporované v instrukčních sadách AVX a AVX2
Při použití technologie AVX se používají vektory o šířce 256 bitů. Tyto vektory je možné rozdělit na celočíselné prvky popř. na prvky s hodnotami reprezentovanými s využitím systému plovoucí řádové čárky. V následující tabulce jsou všechny možné a podporované kombinace vypsány:
Typ v C | Význam | Deklarace |
---|---|---|
_v32qi | 32 celočíselných prvků, každý o šířce 8bitů (pro C++) | typedef char __v32qi __attribute__ ((__vector_size__ (32))); |
_v32qs | 32 celočíselných prvků se znaménkem, každý o šířce 8bitů | typedef signed char __v32qs __attribute__ ((__vector_size__ (32))); |
_v16hi | 16 celočíselných prvků se znaménkem, každý o šířce 16bitů | typedef short __v16hi __attribute__ ((__vector_size__ (32))); |
_v8si | 8 celočíselných prvků se znaménkem, každý o šířce 32bitů | typedef int __v8si __attribute__ ((__vector_size__ (32))); |
_v4di | 4 celočíselné prvky se znaménkem, každý o šířce 64bitů | typedef long long __v4di __attribute__ ((__vector_size__ (32))); |
_v32qu | 32 celočíselných prvků bez znaménka, každý o šířce 8bitů | typedef unsigned char __v32qu __attribute__ ((__vector_size__ (32))); |
_v16hu | 16 celočíselných prvků bez znaménka, každý o šířce 16bitů | typedef unsigned short __v16hu __attribute__ ((__vector_size__ (32))); |
_v8su | 8 celočíselných prvků bez znaménka, každý o šířce 32bitů | typedef unsigned int __v8su __attribute__ ((__vector_size__ (32))); |
_v4du | 4 celočíselné prvky bez znaménka, každý o šířce 64bitů | typedef unsigned long long __v4du __attribute__ ((__vector_size__ (32))); |
_v8sf | osm prvků typu float | typedef float __v8sf __attribute__ ((__vector_size__ (32))); |
_v4df | čtyři prvky typu double | typedef double __v4df __attribute__ ((__vector_size__ (32))); |
Což je vhodný doplněk ke (stále podporovaným) datovým typům pro SSE:
Typ v C | Význam | Deklarace |
---|---|---|
_v16qi | 16 celočíselných prvků, každý o šířce 8bitů (pro C++) | typedef char __v16qi __attribute__ ((__vector_size__ (16))); |
_v16qs | 16 celočíselných prvků se znaménkem, každý o šířce 8bitů | typedef signed char __v16qs __attribute__ ((__vector_size__ (16))); |
_v8hi | 8 celočíselných prvků se znaménkem, každý o šířce 16bitů | typedef short __v8hi __attribute__ ((__vector_size__ (16))); |
_v4si | 4 celočíselné prvky se znaménkem, každý o šířce 32bitů | typedef int __v4si __attribute__ ((__vector_size__ (16))); |
_v2di | 2 celočíselné prvky se znaménkem, každý o šířce 64bitů | typedef long long __v2di __attribute__ ((__vector_size__ (16))); |
_v16qu | 16 celočíselných prvků bez znaménka, každý o šířce 8bitů | typedef unsigned char __v16qu __attribute__ ((__vector_size__ (16))); |
_v8hu | 8 celočíselných prvků bez znaménka, každý o šířce 16bitů | typedef unsigned short __v8hu __attribute__ ((__vector_size__ (16))); |
_v4su | 4 celočíselné prvky bez znaménka, každý o šířce 32bitů | typedef unsigned int __v4su __attribute__ ((__vector_size__ (16))); |
_v2du | 2 celočíselné prvky bez znaménka, každý o šířce 64bitů | typedef unsigned long long __v2du __attribute__ ((__vector_size__ (16))); |
_v4sf | čtyři prvky typu float | typedef float __v4sf __attribute__ ((__vector_size__ (16))); |
_v2df | dva prvky typu double | typedef double __v2df __attribute__ ((__vector_size__ (16))); |
4. Tříadresový kód
„Novým objevem“ (ve skutečnosti starým přibližně padesát let) je použití takzvaného tříadresového kódu v AVX instrukcích. Co to znamená? V instrukčním slovu jsou zakódovány jak dva zdrojové operandy (registry popř. adresa), tak i jeden cílový operand. To znamená, že – na rozdíl od SSE – není jeden ze zdrojových operandů (konkrétně registr) i operandem cílovým – není tedy přepsán výsledkem operace. To může pomoci překladači s alokací registrů, ovšem navíc se mnohdy ušetří operace kopie dat mezi dvojicí registrů.
Podívejme se na rozdíly mezi dvouadresovým a tříadresovým kódem u instrukce součtu. Ve dvouadresovém kódu je jeden ze zdrojových registrů i registrem cílovým:
c: 0f 28 4d 10 movaps xmm1,XMMWORD PTR [rbp+0x10] 10: 0f 28 45 30 movaps xmm0,XMMWORD PTR [rbp+0x30] 14: 0f 58 c8 addps xmm1,xmm0
U tříadresového kódu tomu tak není (navíc je jedním ze zdrojů adresa a nikoli registr):
1d: c5 fc 28 44 24 e0 vmovaps ymm1,YMMWORD PTR [rsp-0x20] 23: c5 fc 58 44 24 c0 vaddps ymm0,ymm1,YMMWORD PTR [rsp-0x40]
5. Nový způsob kódování instrukcí
V souvislosti s AVX byl navržen nový (kolikátý už? asi osmý?) způsob kódování instrukcí nazvaný VEX. Díky tomuto rozšíření bylo umožněno jak zvýšení počtu instrukcí (s prostorem pro další rozšiřování), tak i rozšíření vektorů ze 128 bitů na 256 bitů a v neposlední řadě je taktéž ve VAX implementován výše zmíněný tříadresový kód.
Instrukce mají proměnnou délku (až jedenáct bajtů, nepočítaje v to možné prefixy), jejich kódování je velmi složité a navíc existují kolize v 32bitovém režimu:
Část | Délka v bajtech |
---|---|
prefixy | proměnná |
VEX | 0, 2 nebo 3 |
OPCODE | 1 |
ModR/M | 1 |
SIB | 0 nebo 1 |
DISP | 0, 1, 2 nebo 4 |
IMM | 0 nebo 1 |
V ModR/M je zakódován způsob adresování operandů, které muselo být rozšířeno o SIB, v němž se specifikuje adresování přes index registr, bázový registr nebo vynásobení offsetu krátkou konstantou (2, 4, …). Navíc se na x86–64 zavádí prefix REX, který modifikuje význam ModR/M tak, že lze použít nové pracovní registry (uff). Krátce: dnes již nemá význam pokoušet se o dekódování instrukcí ručně; lepší je se spolehnout na debuggery, disassemblery nebo nástroj objdump.
6. Praktická část
V praktické části dnešního článku si ve stručnosti ukážeme, jakým způsobem jsou instrukce z rozšířené instrukční sady AVX a AVX2 podporovány v překladači GNU C, přičemž na tomto místě je vhodné poznamenat, že velmi podobné konstrukce nalezneme i v dalších překladačích (jedná se například clang pro LLVM, překladač icc atd.). Nejdříve si řekneme, do jaké míry rozšíření GCC pro vektory podporuje nové instrukce a nové vektory o délce 256 bitů (prozatím jsme používali vektory o délce 128 bitů) a posléze si ukážeme i využití základních intrinsic, tedy pseudofunkcí dostupných překladači, jejichž použití vede k vložení nějaké instrukce AVX nebo AVX2 do generovaného strojového kódu (popř. do kódu v assembleru). Všechny dále uvedené demonstrační příklady jsou pochopitelně otestovány, a to konkrétně na počítači s mikroprocesorem i7 s osmi (shodnými) jádry:
processor : 0 vendor_id : GenuineIntel cpu family : 6 model : 142 model name : Intel(R) Core(TM) i7-8665U CPU @ 1.90GHz stepping : 12 microcode : 0xf0 cpu MHz : 802.091 cache size : 8192 KB physical id : 0 siblings : 8 core id : 0 cpu cores : 4 apicid : 0 initial apicid : 0 fpu : yes fpu_exception : yes cpuid level : 22 wp : yes flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb invpcid_single ssbd ibrs ibpb stibp ibrs_enhanced tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid mpx rdseed adx smap clflushopt intel_pt xsaveopt xsavec xgetbv1 xsaves dtherm ida arat pln pts hwp hwp_notify hwp_act_window hwp_epp md_clear flush_l1d arch_capabilities bugs : spectre_v1 spectre_v2 spec_store_bypass swapgs taa itlb_multihit srbds mmio_stale_data bogomips : 4199.88 clflush size : 64 cache_alignment : 64 address sizes : 39 bits physical, 48 bits virtual power management: processor : 1 vendor_id : GenuineIntel cpu family : 6 model : 142 model name : Intel(R) Core(TM) i7-8665U CPU @ 1.90GHz stepping : 12 microcode : 0xf0 cpu MHz : 800.618 cache size : 8192 KB physical id : 0 siblings : 8 core id : 1 cpu cores : 4 apicid : 2 initial apicid : 2 fpu : yes fpu_exception : yes cpuid level : 22 wp : yes flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb invpcid_single ssbd ibrs ibpb stibp ibrs_enhanced tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid mpx rdseed adx smap clflushopt intel_pt xsaveopt xsavec xgetbv1 xsaves dtherm ida arat pln pts hwp hwp_notify hwp_act_window hwp_epp md_clear flush_l1d arch_capabilities bugs : spectre_v1 spectre_v2 spec_store_bypass swapgs taa itlb_multihit srbds mmio_stale_data bogomips : 4199.88 clflush size : 64 cache_alignment : 64 address sizes : 39 bits physical, 48 bits virtual power management: processor : 2 vendor_id : GenuineIntel cpu family : 6 model : 142 model name : Intel(R) Core(TM) i7-8665U CPU @ 1.90GHz stepping : 12 microcode : 0xf0 cpu MHz : 804.468 cache size : 8192 KB physical id : 0 siblings : 8 core id : 2 cpu cores : 4 apicid : 4 initial apicid : 4 fpu : yes fpu_exception : yes cpuid level : 22 wp : yes flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb invpcid_single ssbd ibrs ibpb stibp ibrs_enhanced tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid mpx rdseed adx smap clflushopt intel_pt xsaveopt xsavec xgetbv1 xsaves dtherm ida arat pln pts hwp hwp_notify hwp_act_window hwp_epp md_clear flush_l1d arch_capabilities bugs : spectre_v1 spectre_v2 spec_store_bypass swapgs taa itlb_multihit srbds mmio_stale_data bogomips : 4199.88 clflush size : 64 cache_alignment : 64 address sizes : 39 bits physical, 48 bits virtual power management: processor : 3 vendor_id : GenuineIntel cpu family : 6 model : 142 model name : Intel(R) Core(TM) i7-8665U CPU @ 1.90GHz stepping : 12 microcode : 0xf0 cpu MHz : 827.064 cache size : 8192 KB physical id : 0 siblings : 8 core id : 3 cpu cores : 4 apicid : 6 initial apicid : 6 fpu : yes fpu_exception : yes cpuid level : 22 wp : yes flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb invpcid_single ssbd ibrs ibpb stibp ibrs_enhanced tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid mpx rdseed adx smap clflushopt intel_pt xsaveopt xsavec xgetbv1 xsaves dtherm ida arat pln pts hwp hwp_notify hwp_act_window hwp_epp md_clear flush_l1d arch_capabilities bugs : spectre_v1 spectre_v2 spec_store_bypass swapgs taa itlb_multihit srbds mmio_stale_data bogomips : 4199.88 clflush size : 64 cache_alignment : 64 address sizes : 39 bits physical, 48 bits virtual power management: processor : 4 vendor_id : GenuineIntel cpu family : 6 model : 142 model name : Intel(R) Core(TM) i7-8665U CPU @ 1.90GHz stepping : 12 microcode : 0xf0 cpu MHz : 803.579 cache size : 8192 KB physical id : 0 siblings : 8 core id : 0 cpu cores : 4 apicid : 1 initial apicid : 1 fpu : yes fpu_exception : yes cpuid level : 22 wp : yes flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb invpcid_single ssbd ibrs ibpb stibp ibrs_enhanced tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid mpx rdseed adx smap clflushopt intel_pt xsaveopt xsavec xgetbv1 xsaves dtherm ida arat pln pts hwp hwp_notify hwp_act_window hwp_epp md_clear flush_l1d arch_capabilities bugs : spectre_v1 spectre_v2 spec_store_bypass swapgs taa itlb_multihit srbds mmio_stale_data bogomips : 4199.88 clflush size : 64 cache_alignment : 64 address sizes : 39 bits physical, 48 bits virtual power management: processor : 5 vendor_id : GenuineIntel cpu family : 6 model : 142 model name : Intel(R) Core(TM) i7-8665U CPU @ 1.90GHz stepping : 12 microcode : 0xf0 cpu MHz : 805.257 cache size : 8192 KB physical id : 0 siblings : 8 core id : 1 cpu cores : 4 apicid : 3 initial apicid : 3 fpu : yes fpu_exception : yes cpuid level : 22 wp : yes flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb invpcid_single ssbd ibrs ibpb stibp ibrs_enhanced tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid mpx rdseed adx smap clflushopt intel_pt xsaveopt xsavec xgetbv1 xsaves dtherm ida arat pln pts hwp hwp_notify hwp_act_window hwp_epp md_clear flush_l1d arch_capabilities bugs : spectre_v1 spectre_v2 spec_store_bypass swapgs taa itlb_multihit srbds mmio_stale_data bogomips : 4199.88 clflush size : 64 cache_alignment : 64 address sizes : 39 bits physical, 48 bits virtual power management: processor : 6 vendor_id : GenuineIntel cpu family : 6 model : 142 model name : Intel(R) Core(TM) i7-8665U CPU @ 1.90GHz stepping : 12 microcode : 0xf0 cpu MHz : 803.016 cache size : 8192 KB physical id : 0 siblings : 8 core id : 2 cpu cores : 4 apicid : 5 initial apicid : 5 fpu : yes fpu_exception : yes cpuid level : 22 wp : yes flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb invpcid_single ssbd ibrs ibpb stibp ibrs_enhanced tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid mpx rdseed adx smap clflushopt intel_pt xsaveopt xsavec xgetbv1 xsaves dtherm ida arat pln pts hwp hwp_notify hwp_act_window hwp_epp md_clear flush_l1d arch_capabilities bugs : spectre_v1 spectre_v2 spec_store_bypass swapgs taa itlb_multihit srbds mmio_stale_data bogomips : 4199.88 clflush size : 64 cache_alignment : 64 address sizes : 39 bits physical, 48 bits virtual power management: processor : 7 vendor_id : GenuineIntel cpu family : 6 model : 142 model name : Intel(R) Core(TM) i7-8665U CPU @ 1.90GHz stepping : 12 microcode : 0xf0 cpu MHz : 799.372 cache size : 8192 KB physical id : 0 siblings : 8 core id : 3 cpu cores : 4 apicid : 7 initial apicid : 7 fpu : yes fpu_exception : yes cpuid level : 22 wp : yes flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb invpcid_single ssbd ibrs ibpb stibp ibrs_enhanced tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid mpx rdseed adx smap clflushopt intel_pt xsaveopt xsavec xgetbv1 xsaves dtherm ida arat pln pts hwp hwp_notify hwp_act_window hwp_epp md_clear flush_l1d arch_capabilities bugs : spectre_v1 spectre_v2 spec_store_bypass swapgs taa itlb_multihit srbds mmio_stale_data bogomips : 4199.88 clflush size : 64 cache_alignment : 64 address sizes : 39 bits physical, 48 bits virtual power management:
7. Operace součtu vektorů o délce 256 bitů s celočíselnými prvky bez znaménka
Nejprve si pro jednoduchost ukažme, jakým způsobem je možné provádět součty 256bitových vektorů (tedy vektorů o šířce třiceti dvou bajtů), v nichž jsou uloženy celočíselné prvky. Na výběr jsou čtyři typy těchto vektorů (minimálně v případě, že vynecháme celočíselný typ se šířkou 128 bitů, který se příliš nepoužívá):
- 32 prvků s šířkou 8bitů (unsigned char)
- 16 prvků s šířkou 16bitů (unsigned short)
- 8 prvků s šířkou 32bitů (unsigned int)
- 4 prvky s šířkou 64bitů (unsigned long)
Všechny operace součtu jsou realizovány v tomto zdrojovém kódu:
#include <stdio.h> typedef unsigned char v32ub __attribute__((vector_size(32))); void add32ub(v32ub x, v32ub y, v32ub * z) { *z = x + y; } typedef unsigned short v32us __attribute__((vector_size(32))); void add32us(v32us x, v32us y, v32us * z) { *z = x + y; } typedef unsigned int v32ui __attribute__((vector_size(32))); void add32ui(v32ui x, v32ui y, v32ui * z) { *z = x + y; } typedef unsigned long int v32ul __attribute__((vector_size(32))); void add32ul(v32ul x, v32ul y, v32ul * z) { *z = x + y; } int main(void) { { v32ub x = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 }; v32ub y = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }; v32ub z; add32ub(x, y, &z); int i; puts("vector of unsigned chars"); for (i = 0; i < sizeof(v32ub) / sizeof(unsigned char); i++) { printf("%d %u\n", i, z[i]); } } putchar('\n'); { v32us x = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; v32us y = { 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff }; v32us z; add32us(x, y, &z); int i; puts("vector of unsigned short ints"); for (i = 0; i < sizeof(v32us) / sizeof(unsigned short); i++) { printf("%d %u\n", i, z[i]); } } putchar('\n'); { v32ui x = { 0, 1, 2, 3, 4, 5, 6, 7 }; v32ui y = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }; v32ui z; add32ui(x, y, &z); int i; puts("vector of unsigned ints"); for (i = 0; i < sizeof(v32ui) / sizeof(unsigned int); i++) { printf("%d %u\n", i, z[i]); } } putchar('\n'); { v32ul x = { 0, 1, 2, 3 }; v32ul y = { 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff }; v32ul z; add32ul(x, y, &z); int i; puts("vector of unsigned longs"); for (i = 0; i < sizeof(v32ul) / sizeof(unsigned long); i++) { printf("%d %lu\n", i, z[i]); } } return 0; }
Výsledek získaný po překladu a spuštění tohoto demonstračního příkladu ukazuje, jak (mj. ) dochází k přetečení hodnot příslušných prvků, což je očekávaná vlastnost:
vector of unsigned chars 0 255 1 0 2 1 3 2 4 3 5 4 6 5 7 6 8 7 9 8 10 9 11 10 12 11 13 12 14 13 15 14 16 15 17 16 18 17 19 18 20 19 21 20 22 21 23 22 24 23 25 24 26 25 27 26 28 27 29 28 30 29 31 30 vector of unsigned short ints 0 65535 1 0 2 1 3 2 4 3 5 4 6 5 7 6 8 7 9 8 10 9 11 10 12 11 13 12 14 13 15 14 vector of unsigned ints 0 4294967295 1 0 2 1 3 2 4 3 5 4 6 5 7 6 vector of unsigned longs 0 18446744073709551615 1 0 2 1 3 2
8. Překlad příkladu do assembleru s využitím instrukcí SSE, AVX, nebo AVX2
Zdrojový kód zmíněný v předchozí kapitole přeložíme třemi způsoby:
- S využitím instrukční sady SSE, ale nikoli AVX
- S využitím instrukční sady AVX
- S využitím instrukční sady AVX2
Vyzkoušejme si nejdřív překlad s využitím pouze SSE, nikoli ovšem AVX:
$ gcc -c -O0 -msse -mno-avx -g
V tomto případě je každý součet implementován dvojicí instrukcí pro součet 128bitových vektorů. Konkrétně se jedná o instrukce paddb, paddw, paddd a paddq. Povšimněte si, že se pracuje s registry XMMx, konkrétně s registry XMM0, XMM1 a XMM2:
void add32ub(v32ub x, v32ub y, v32ub * z) *z = x + y; c: 66 0f 6f 4d 10 movdqa xmm1,XMMWORD PTR [rbp+0x10] 11: 66 0f 6f 45 30 movdqa xmm0,XMMWORD PTR [rbp+0x30] 16: 66 0f fc c8 paddb xmm1,xmm0 1a: 66 0f 6f 55 20 movdqa xmm2,XMMWORD PTR [rbp+0x20] 1f: 66 0f 6f 45 40 movdqa xmm0,XMMWORD PTR [rbp+0x40] 24: 66 0f fc c2 paddb xmm0,xmm2 28: 0f 29 4d a0 movaps XMMWORD PTR [rbp-0x60],xmm1 2c: 0f 29 45 b0 movaps XMMWORD PTR [rbp-0x50],xmm0 void add32us(v32us x, v32us y, v32us * z) *z = x + y; 82: 66 0f 6f 4d 10 movdqa xmm1,XMMWORD PTR [rbp+0x10] 87: 66 0f 6f 45 30 movdqa xmm0,XMMWORD PTR [rbp+0x30] 8c: 66 0f fd c8 paddw xmm1,xmm0 90: 66 0f 6f 55 20 movdqa xmm2,XMMWORD PTR [rbp+0x20] 95: 66 0f 6f 45 40 movdqa xmm0,XMMWORD PTR [rbp+0x40] 9a: 66 0f fd c2 paddw xmm0,xmm2 9e: 0f 29 4d a0 movaps XMMWORD PTR [rbp-0x60],xmm1 a2: 0f 29 45 b0 movaps XMMWORD PTR [rbp-0x50],xmm0 void add32ui(v32ui x, v32ui y, v32ui * z) *z = x + y; f8: 66 0f 6f 4d 10 movdqa xmm1,XMMWORD PTR [rbp+0x10] fd: 66 0f 6f 45 30 movdqa xmm0,XMMWORD PTR [rbp+0x30] 102: 66 0f fe c8 paddd xmm1,xmm0 106: 66 0f 6f 55 20 movdqa xmm2,XMMWORD PTR [rbp+0x20] 10b: 66 0f 6f 45 40 movdqa xmm0,XMMWORD PTR [rbp+0x40] 110: 66 0f fe c2 paddd xmm0,xmm2 114: 0f 29 4d a0 movaps XMMWORD PTR [rbp-0x60],xmm1 118: 0f 29 45 b0 movaps XMMWORD PTR [rbp-0x50],xmm0 void add32ul(v32ul x, v32ul y, v32ul * z) *z = x + y; 16e: 66 0f 6f 4d 10 movdqa xmm1,XMMWORD PTR [rbp+0x10] 173: 66 0f 6f 45 30 movdqa xmm0,XMMWORD PTR [rbp+0x30] 178: 66 0f d4 c8 paddq xmm1,xmm0 17c: 66 0f 6f 55 20 movdqa xmm2,XMMWORD PTR [rbp+0x20] 181: 66 0f 6f 45 40 movdqa xmm0,XMMWORD PTR [rbp+0x40] 186: 66 0f d4 c2 paddq xmm0,xmm2 18a: 0f 29 4d a0 movaps XMMWORD PTR [rbp-0x60],xmm1 18e: 0f 29 45 b0 movaps XMMWORD PTR [rbp-0x50],xmm0
Překlad s využitím instrukční sady AVX, ale nikoli AVX2:
$ gcc -c -O0 -mavx -g
V tomto případě se stále používají pracovní registry nazvané XMM0, XMM1 a XMM2 a pracující se 128bitovými vektory. Jedná se však o odlišné instrukce, konkrétně o instrukce vpaddb, vpaddw, vpaddd a vpaddq:
void add32ub(v32ub x, v32ub y, v32ub * z) *z = x + y; 1d: c5 f9 6f 4c 24 e0 vmovdqa xmm1,XMMWORD PTR [rsp-0x20] 23: c5 f9 6f 44 24 c0 vmovdqa xmm0,XMMWORD PTR [rsp-0x40] 29: c5 f1 fc c0 vpaddb xmm0,xmm1,xmm0 2d: c5 f9 6f 54 24 f0 vmovdqa xmm2,XMMWORD PTR [rsp-0x10] 33: c5 f9 6f 4c 24 d0 vmovdqa xmm1,XMMWORD PTR [rsp-0x30] 39: c5 e9 fc c9 vpaddb xmm1,xmm2,xmm1 void add32us(v32us x, v32us y, v32us * z) *z = x + y; 6c: c5 f9 6f 4c 24 e0 vmovdqa xmm1,XMMWORD PTR [rsp-0x20] 72: c5 f9 6f 44 24 c0 vmovdqa xmm0,XMMWORD PTR [rsp-0x40] 78: c5 f1 fd c0 vpaddw xmm0,xmm1,xmm0 7c: c5 f9 6f 54 24 f0 vmovdqa xmm2,XMMWORD PTR [rsp-0x10] 82: c5 f9 6f 4c 24 d0 vmovdqa xmm1,XMMWORD PTR [rsp-0x30] 88: c5 e9 fd c9 vpaddw xmm1,xmm2,xmm1 void add32ui(v32ui x, v32ui y, v32ui * z) *z = x + y; bb: c5 f9 6f 4c 24 e0 vmovdqa xmm1,XMMWORD PTR [rsp-0x20] c1: c5 f9 6f 44 24 c0 vmovdqa xmm0,XMMWORD PTR [rsp-0x40] c7: c5 f1 fe c0 vpaddd xmm0,xmm1,xmm0 cb: c5 f9 6f 54 24 f0 vmovdqa xmm2,XMMWORD PTR [rsp-0x10] d1: c5 f9 6f 4c 24 d0 vmovdqa xmm1,XMMWORD PTR [rsp-0x30] d7: c5 e9 fe c9 vpaddd xmm1,xmm2,xmm1 void add32ul(v32ul x, v32ul y, v32ul * z) *z = x + y; 10a: c5 f9 6f 4c 24 e0 vmovdqa xmm1,XMMWORD PTR [rsp-0x20] 110: c5 f9 6f 44 24 c0 vmovdqa xmm0,XMMWORD PTR [rsp-0x40] 116: c5 f1 d4 c0 vpaddq xmm0,xmm1,xmm0 11a: c5 f9 6f 54 24 f0 vmovdqa xmm2,XMMWORD PTR [rsp-0x10] 120: c5 f9 6f 4c 24 d0 vmovdqa xmm1,XMMWORD PTR [rsp-0x30] 126: c5 e9 d4 c9 vpaddq xmm1,xmm2,xmm1
Překlad s využitím instrukční sady AVX2:
$ gcc -c -O0 -mavx2 -g
Teprve nyní můžeme vidět použití 256bitových registrů YMMx a dokonce i použití tříadresového kódu. Využívají se zde instrukce vpaddb, vpaddw, vpaddd a vpaddq:
void add32ub(v32ub x, v32ub y, v32ub * z) *z = x + y; 1d: c5 fd 6f 44 24 e0 vmovdqa ymm0,YMMWORD PTR [rsp-0x20] 23: c5 fd fc 44 24 c0 vpaddb ymm0,ymm0,YMMWORD PTR [rsp-0x40] 29: 48 8b 44 24 b8 mov rax,QWORD PTR [rsp-0x48] 2e: c5 fd 7f 00 vmovdqa YMMWORD PTR [rax],ymm0 void add32us(v32us x, v32us y, v32us * z) *z = x + y; 52: c5 fd 6f 44 24 e0 vmovdqa ymm0,YMMWORD PTR [rsp-0x20] 58: c5 fd fd 44 24 c0 vpaddw ymm0,ymm0,YMMWORD PTR [rsp-0x40] 5e: 48 8b 44 24 b8 mov rax,QWORD PTR [rsp-0x48] 63: c5 fd 7f 00 vmovdqa YMMWORD PTR [rax],ymm0 void add32ui(v32ui x, v32ui y, v32ui * z) *z = x + y; 87: c5 fd 6f 44 24 e0 vmovdqa ymm0,YMMWORD PTR [rsp-0x20] 8d: c5 fd fe 44 24 c0 vpaddd ymm0,ymm0,YMMWORD PTR [rsp-0x40] 93: 48 8b 44 24 b8 mov rax,QWORD PTR [rsp-0x48] 98: c5 fd 7f 00 vmovdqa YMMWORD PTR [rax],ymm0 void add32ul(v32ul x, v32ul y, v32ul * z) *z = x + y; bc: c5 fd 6f 44 24 e0 vmovdqa ymm0,YMMWORD PTR [rsp-0x20] c2: c5 fd d4 44 24 c0 vpaddq ymm0,ymm0,YMMWORD PTR [rsp-0x40] c8: 48 8b 44 24 b8 mov rax,QWORD PTR [rsp-0x48] cd: c5 fd 7f 00 vmovdqa YMMWORD PTR [rax],ymm0
9. Operace součtu vektorů o délce 256 bitů s celočíselnými prvky se znaménkem
Demonstrační příklad ze sedmé kapitoly je možné velmi snadno přepsat takovým způsobem, že se do vektorů o délce 256 bitů uloží celočíselné prvky se znaménkem. Opět se může jednat o čtyři standardní kombinace:
- 32 prvků s šířkou 8bitů (signed char)
- 16 prvků s šířkou 16bitů (signed short)
- 8 prvků s šířkou 32bitů (signed int)
- 4 prvky s šířkou 64bitů (signed long)
Všechny operace součtu jsou realizovány v tomto zdrojovém kódu:
#include <stdio.h> typedef signed char v32sb __attribute__((vector_size(32))); void add32sb(v32sb x, v32sb y, v32sb * z) { *z = x + y; } typedef signed short v32ss __attribute__((vector_size(32))); void add32ss(v32ss x, v32ss y, v32ss * z) { *z = x + y; } typedef signed int v32si __attribute__((vector_size(32))); void add32si(v32si x, v32si y, v32si * z) { *z = x + y; } typedef signed long int v32sl __attribute__((vector_size(32))); void add32sl(v32sl x, v32sl y, v32sl * z) { *z = x + y; } int main(void) { { v32sb x = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 }; v32sb y = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }; v32sb z; add32sb(x, y, &z); int i; puts("vector of signed chars"); for (i = 0; i < sizeof(v32sb) / sizeof(signed char); i++) { printf("%d %d\n", i, z[i]); } } putchar('\n'); { v32ss x = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; v32ss y = { 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff }; v32ss z; add32ss(x, y, &z); int i; puts("vector of signed short ints"); for (i = 0; i < sizeof(v32ss) / sizeof(signed short); i++) { printf("%d %d\n", i, z[i]); } } putchar('\n'); { v32si x = { 0, 1, 2, 3, 4, 5, 6, 7 }; v32si y = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }; v32si z; add32si(x, y, &z); int i; puts("vector of signed ints"); for (i = 0; i < sizeof(v32si) / sizeof(signed int); i++) { printf("%d %d\n", i, z[i]); } } putchar('\n'); { v32sl x = { 0, 1, 2, 3 }; v32sl y = { 0x7fffffffffffffff, 0x7fffffffffffffff, 0x7fffffffffffffff, 0x7fffffffffffffff }; v32sl z; add32sl(x, y, &z); int i; puts("vector of signed longs"); for (i = 0; i < sizeof(v32sl) / sizeof(signed long); i++) { printf("%d %ld\n", i, z[i]); } } return 0; }
Výsledek získaný po překladu a spuštění tohoto demonstračního příkladu opět ukazuje, jak dochází k přetečení hodnot (zde očekávanému):
vector of signed chars 0 -1 1 0 2 1 3 2 4 3 5 4 6 5 7 6 8 7 9 8 10 9 11 10 12 11 13 12 14 13 15 14 16 15 17 16 18 17 19 18 20 19 21 20 22 21 23 22 24 23 25 24 26 25 27 26 28 27 29 28 30 29 31 30 vector of signed short ints 0 -1 1 0 2 1 3 2 4 3 5 4 6 5 7 6 8 7 9 8 10 9 11 10 12 11 13 12 14 13 15 14 vector of signed ints 0 -1 1 0 2 1 3 2 4 3 5 4 6 5 7 6 vector of signed longs 0 9223372036854775807 1 -9223372036854775808 2 -9223372036854775807 3 -9223372036854775806
10. Překlad příkladu do assembleru s využitím instrukcí SSE, AVX, nebo AVX2
Opět se podívejme na způsob překladu výše uvedeného demonstračního příkladu do assembleru, a to pro tři varianty zmíněné výše:
- S využitím instrukční sady SSE, ale nikoli AVX
- S využitím instrukční sady AVX
- S využitím instrukční sady AVX2
Překlad pro SSE opět využívá instrukce paddb, paddw, paddd a paddq a pracuje se 128bitovými registry XMMx, konkrétně s registry XMM0, XMM1 a XMM2:
void add32sb(v32sb x, v32sb y, v32sb * z) *z = x + y; c: 66 0f 6f 4d 10 movdqa xmm1,XMMWORD PTR [rbp+0x10] 11: 66 0f 6f 45 30 movdqa xmm0,XMMWORD PTR [rbp+0x30] 16: 66 0f fc c8 paddb xmm1,xmm0 1a: 66 0f 6f 55 20 movdqa xmm2,XMMWORD PTR [rbp+0x20] 1f: 66 0f 6f 45 40 movdqa xmm0,XMMWORD PTR [rbp+0x40] 24: 66 0f fc c2 paddb xmm0,xmm2 void add32ss(v32ss x, v32ss y, v32ss * z) *z = x + y; 82: 66 0f 6f 4d 10 movdqa xmm1,XMMWORD PTR [rbp+0x10] 87: 66 0f 6f 45 30 movdqa xmm0,XMMWORD PTR [rbp+0x30] 8c: 66 0f fd c8 paddw xmm1,xmm0 90: 66 0f 6f 55 20 movdqa xmm2,XMMWORD PTR [rbp+0x20] 95: 66 0f 6f 45 40 movdqa xmm0,XMMWORD PTR [rbp+0x40] 9a: 66 0f fd c2 paddw xmm0,xmm2 void add32si(v32si x, v32si y, v32si * z) *z = x + y; f8: 66 0f 6f 4d 10 movdqa xmm1,XMMWORD PTR [rbp+0x10] fd: 66 0f 6f 45 30 movdqa xmm0,XMMWORD PTR [rbp+0x30] 102: 66 0f fe c8 paddd xmm1,xmm0 106: 66 0f 6f 55 20 movdqa xmm2,XMMWORD PTR [rbp+0x20] 10b: 66 0f 6f 45 40 movdqa xmm0,XMMWORD PTR [rbp+0x40] 110: 66 0f fe c2 paddd xmm0,xmm2 void add32sl(v32sl x, v32sl y, v32sl * z) *z = x + y; 16e: 66 0f 6f 4d 10 movdqa xmm1,XMMWORD PTR [rbp+0x10] 173: 66 0f 6f 45 30 movdqa xmm0,XMMWORD PTR [rbp+0x30] 178: 66 0f d4 c8 paddq xmm1,xmm0 17c: 66 0f 6f 55 20 movdqa xmm2,XMMWORD PTR [rbp+0x20] 181: 66 0f 6f 45 40 movdqa xmm0,XMMWORD PTR [rbp+0x40] 186: 66 0f d4 c2 paddq xmm0,xmm2
Překlad pro AVX je založen na použití registrů XMM0, XMM1 a XMM2 pracujících se 128bitovými vektory. Použity jsou ovšem instrukce vpaddb, vpaddw, vpaddd a vpaddq:
typedef signed char v32sb __attribute__((vector_size(32))); *z = x + y; 1d: c5 f9 6f 4c 24 e0 vmovdqa xmm1,XMMWORD PTR [rsp-0x20] 23: c5 f9 6f 44 24 c0 vmovdqa xmm0,XMMWORD PTR [rsp-0x40] 29: c5 f1 fc c0 vpaddb xmm0,xmm1,xmm0 2d: c5 f9 6f 54 24 f0 vmovdqa xmm2,XMMWORD PTR [rsp-0x10] 33: c5 f9 6f 4c 24 d0 vmovdqa xmm1,XMMWORD PTR [rsp-0x30] 39: c5 e9 fc c9 vpaddb xmm1,xmm2,xmm1 void add32ss(v32ss x, v32ss y, v32ss * z) *z = x + y; 6c: c5 f9 6f 4c 24 e0 vmovdqa xmm1,XMMWORD PTR [rsp-0x20] 72: c5 f9 6f 44 24 c0 vmovdqa xmm0,XMMWORD PTR [rsp-0x40] 78: c5 f1 fd c0 vpaddw xmm0,xmm1,xmm0 7c: c5 f9 6f 54 24 f0 vmovdqa xmm2,XMMWORD PTR [rsp-0x10] 82: c5 f9 6f 4c 24 d0 vmovdqa xmm1,XMMWORD PTR [rsp-0x30] 88: c5 e9 fd c9 vpaddw xmm1,xmm2,xmm1 void add32si(v32si x, v32si y, v32si * z) *z = x + y; bb: c5 f9 6f 4c 24 e0 vmovdqa xmm1,XMMWORD PTR [rsp-0x20] c1: c5 f9 6f 44 24 c0 vmovdqa xmm0,XMMWORD PTR [rsp-0x40] c7: c5 f1 fe c0 vpaddd xmm0,xmm1,xmm0 cb: c5 f9 6f 54 24 f0 vmovdqa xmm2,XMMWORD PTR [rsp-0x10] d1: c5 f9 6f 4c 24 d0 vmovdqa xmm1,XMMWORD PTR [rsp-0x30] d7: c5 e9 fe c9 vpaddd xmm1,xmm2,xmm1 void add32sl(v32sl x, v32sl y, v32sl * z) *z = x + y; 10a: c5 f9 6f 4c 24 e0 vmovdqa xmm1,XMMWORD PTR [rsp-0x20] 110: c5 f9 6f 44 24 c0 vmovdqa xmm0,XMMWORD PTR [rsp-0x40] 116: c5 f1 d4 c0 vpaddq xmm0,xmm1,xmm0 11a: c5 f9 6f 54 24 f0 vmovdqa xmm2,XMMWORD PTR [rsp-0x10] 120: c5 f9 6f 4c 24 d0 vmovdqa xmm1,XMMWORD PTR [rsp-0x30] 126: c5 e9 d4 c9 vpaddq xmm1,xmm2,xmm1
Překlad pro AVX2 s využitím instrukcí vpaddb, vpaddw, vpaddd a vpaddq, tříadresového kódu a 256bitových registrů:
void add32sb(v32sb x, v32sb y, v32sb * z) *z = x + y; 1d: c5 fd 6f 44 24 e0 vmovdqa ymm0,YMMWORD PTR [rsp-0x20] 23: c5 fd fc 44 24 c0 vpaddb ymm0,ymm0,YMMWORD PTR [rsp-0x40] 29: 48 8b 44 24 b8 mov rax,QWORD PTR [rsp-0x48] 2e: c5 fd 7f 00 vmovdqa YMMWORD PTR [rax],ymm0 void add32ss(v32ss x, v32ss y, v32ss * z) *z = x + y; 52: c5 fd 6f 44 24 e0 vmovdqa ymm0,YMMWORD PTR [rsp-0x20] 58: c5 fd fd 44 24 c0 vpaddw ymm0,ymm0,YMMWORD PTR [rsp-0x40] 5e: 48 8b 44 24 b8 mov rax,QWORD PTR [rsp-0x48] 63: c5 fd 7f 00 vmovdqa YMMWORD PTR [rax],ymm0 void add32si(v32si x, v32si y, v32si * z) *z = x + y; 87: c5 fd 6f 44 24 e0 vmovdqa ymm0,YMMWORD PTR [rsp-0x20] 8d: c5 fd fe 44 24 c0 vpaddd ymm0,ymm0,YMMWORD PTR [rsp-0x40] 93: 48 8b 44 24 b8 mov rax,QWORD PTR [rsp-0x48] 98: c5 fd 7f 00 vmovdqa YMMWORD PTR [rax],ymm0 void add32sl(v32sl x, v32sl y, v32sl * z) *z = x + y; bc: c5 fd 6f 44 24 e0 vmovdqa ymm0,YMMWORD PTR [rsp-0x20] c2: c5 fd d4 44 24 c0 vpaddq ymm0,ymm0,YMMWORD PTR [rsp-0x40] c8: 48 8b 44 24 b8 mov rax,QWORD PTR [rsp-0x48] cd: c5 fd 7f 00 vmovdqa YMMWORD PTR [rax],ymm0
11. Operace součtu vektorů o délce 256 bitů s prvky typu float a double
Posledním demonstračním příkladem, v němž budeme používat standardní jazyk C rozšířený pouze o datový typ „vektor zadané délky“ bude příklad, v němž se provádí součet dvou vektorů o délce 256 bitů s prvky typu float (první část příkladu) a double (druhá část příkladu):
#include <stdio.h> typedef float v32float __attribute__((vector_size(32))); void add32float(v32float x, v32float y, v32float * z) { *z = x + y; } typedef double v32double __attribute__((vector_size(32))); void add32double(v32double x, v32double y, v32double * z) { *z = x + y; } int main(void) { { v32float x = { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0 }; v32float y = { 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5 }; v32float z; int i; add32float(x, y, &z); puts("vector of floats"); for (i = 0; i < sizeof(v32float) / sizeof(float); i++) { printf("%d %f + %f = %f\n", i, x[i], y[i], z[i]); } } putchar('\n'); { v32double x = { 1.0, 2.0, 3.0, 4.0 }; v32double y = { 0.5, 0.5, 0.5, 0.5 }; v32double z; int i; add32double(x, y, &z); puts("vector of doubles"); for (i = 0; i < sizeof(v32double) / sizeof(double); i++) { printf("%d %f + %f = %f\n", i, x[i], y[i], z[i]); } } return 0; }
Výsledky získané po spuštění tohoto demonstračního příkladu by měly vypadat následovně:
vector of floats 0 1.000000 + 0.500000 = 1.500000 1 2.000000 + 0.500000 = 2.500000 2 3.000000 + 0.500000 = 3.500000 3 4.000000 + 0.500000 = 4.500000 4 5.000000 + 0.500000 = 5.500000 5 6.000000 + 0.500000 = 6.500000 6 7.000000 + 0.500000 = 7.500000 7 8.000000 + 0.500000 = 8.500000 vector of doubles 0 1.000000 + 0.500000 = 1.500000 1 2.000000 + 0.500000 = 2.500000 2 3.000000 + 0.500000 = 3.500000 3 4.000000 + 0.500000 = 4.500000
12. Překlad příkladu do assembleru s využitím instrukcí SSE, AVX, nebo AVX2 – varianta pro float
Podívejme se nejdříve na překlad operací součtu v případě použití instrukční sady SSE. V této variantě je použita dvojice instrukcí addps, z nichž každá sečte 128bitový vektor:
void add32float(v32float x, v32float y, v32float * z) *z = x + y; c: 0f 28 4d 10 movaps xmm1,XMMWORD PTR [rbp+0x10] 10: 0f 28 45 30 movaps xmm0,XMMWORD PTR [rbp+0x30] 14: 0f 58 c8 addps xmm1,xmm0 17: 0f 28 55 20 movaps xmm2,XMMWORD PTR [rbp+0x20] 1b: 0f 28 45 40 movaps xmm0,XMMWORD PTR [rbp+0x40] 1f: 0f 58 c2 addps xmm0,xmm2 22: 0f 29 4d a0 movaps XMMWORD PTR [rbp-0x60],xmm1 26: 0f 29 45 b0 movaps XMMWORD PTR [rbp-0x50],xmm0
Dále si ukážeme, jakým způsobem je součet vektorů implementován s využitím instrukční sady AVX. Výpočet je založen na použití jediné instrukce vaddps s tříadresovým kódem a 256bitovými registry YMMx:
void add32float(v32float x, v32float y, v32float * z) *z = x + y; 1d: c5 fc 28 44 24 e0 vmovaps ymm0,YMMWORD PTR [rsp-0x20] 23: c5 fc 58 44 24 c0 vaddps ymm0,ymm0,YMMWORD PTR [rsp-0x40] 29: 48 8b 44 24 b8 mov rax,QWORD PTR [rsp-0x48] 2e: c5 fc 29 00 vmovaps YMMWORD PTR [rax],ymm0
A konečně se podíváme na to, jakým způsobem je součet vektorů implementován s využitím instrukční sady AVX2. Zde je naprosto stejný, jako při použití AVX!:
void add32float(v32float x, v32float y, v32float * z) *z = x + y; 1d: c5 fc 28 44 24 e0 vmovaps ymm0,YMMWORD PTR [rsp-0x20] 23: c5 fc 58 44 24 c0 vaddps ymm0,ymm0,YMMWORD PTR [rsp-0x40] 29: 48 8b 44 24 b8 mov rax,QWORD PTR [rsp-0x48] 2e: c5 fc 29 00 vmovaps YMMWORD PTR [rax],ymm0
13. Překlad příkladu do assembleru s využitím instrukcí SSE, AVX, nebo AVX2 – varianta pro double
Překlad s využitím instrukcí SSE (a nikoli AVX) vede k použití dvojice instrukcí addpd, z nichž každá sečte 128bitový vektor:
void add32double(v32double x, v32double y, v32double * z) *z = x + y; 7c: 66 0f 28 4d 10 movapd xmm1,XMMWORD PTR [rbp+0x10] 81: 66 0f 28 45 30 movapd xmm0,XMMWORD PTR [rbp+0x30] 86: 66 0f 58 c8 addpd xmm1,xmm0 8a: 66 0f 28 55 20 movapd xmm2,XMMWORD PTR [rbp+0x20] 8f: 66 0f 28 45 40 movapd xmm0,XMMWORD PTR [rbp+0x40] 94: 66 0f 58 c2 addpd xmm0,xmm2 98: 0f 29 4d a0 movaps XMMWORD PTR [rbp-0x60],xmm1 9c: 0f 29 45 b0 movaps XMMWORD PTR [rbp-0x50],xmm0
Realizace výpočtu s využitím AVX se (jak uvidíme o odstavec níže) nijak neliší od AVX2. Vše je založeno na instrukci vaddpd:
void add32double(v32double x, v32double y, v32double * z) *z = x + y; 52: c5 fd 28 44 24 e0 vmovapd ymm0,YMMWORD PTR [rsp-0x20] 58: c5 fd 58 44 24 c0 vaddpd ymm0,ymm0,YMMWORD PTR [rsp-0x40] 5e: 48 8b 44 24 b8 mov rax,QWORD PTR [rsp-0x48] 63: c5 fd 29 00 vmovapd YMMWORD PTR [rax],ymm0
Součet realizovaný instrukcemi ze sady AVX2 je založen na operaci vaddpd, jinak se (opět) neliší od varianty float:
void add32double(v32double x, v32double y, v32double * z) *z = x + y; 52: c5 fd 28 44 24 e0 vmovapd ymm0,YMMWORD PTR [rsp-0x20] 58: c5 fd 58 44 24 c0 vaddpd ymm0,ymm0,YMMWORD PTR [rsp-0x40] 5e: 48 8b 44 24 b8 mov rax,QWORD PTR [rsp-0x48] 63: c5 fd 29 00 vmovapd YMMWORD PTR [rax],ymm0
14. Intrinsic v GCC pro instrukce AVX
V případě, že se při překladu zdrojových kódů s využitím GCC C použije přepínač -mavx, bude mít vývojář automaticky k dispozici následující nové intrinsic:
v4df __builtin_ia32_addpd256 (v4df,v4df); v8sf __builtin_ia32_addps256 (v8sf,v8sf); v4df __builtin_ia32_addsubpd256 (v4df,v4df); v8sf __builtin_ia32_addsubps256 (v8sf,v8sf); v4df __builtin_ia32_andnpd256 (v4df,v4df); v8sf __builtin_ia32_andnps256 (v8sf,v8sf); v4df __builtin_ia32_andpd256 (v4df,v4df); v8sf __builtin_ia32_andps256 (v8sf,v8sf); v4df __builtin_ia32_blendpd256 (v4df,v4df,int); v8sf __builtin_ia32_blendps256 (v8sf,v8sf,int); v4df __builtin_ia32_blendvpd256 (v4df,v4df,v4df); v8sf __builtin_ia32_blendvps256 (v8sf,v8sf,v8sf); v2df __builtin_ia32_cmppd (v2df,v2df,int); v4df __builtin_ia32_cmppd256 (v4df,v4df,int); v4sf __builtin_ia32_cmpps (v4sf,v4sf,int); v8sf __builtin_ia32_cmpps256 (v8sf,v8sf,int); v2df __builtin_ia32_cmpsd (v2df,v2df,int); v4sf __builtin_ia32_cmpss (v4sf,v4sf,int); v4df __builtin_ia32_cvtdq2pd256 (v4si); v8sf __builtin_ia32_cvtdq2ps256 (v8si); v4si __builtin_ia32_cvtpd2dq256 (v4df); v4sf __builtin_ia32_cvtpd2ps256 (v4df); v8si __builtin_ia32_cvtps2dq256 (v8sf); v4df __builtin_ia32_cvtps2pd256 (v4sf); v4si __builtin_ia32_cvttpd2dq256 (v4df); v8si __builtin_ia32_cvttps2dq256 (v8sf); v4df __builtin_ia32_divpd256 (v4df,v4df); v8sf __builtin_ia32_divps256 (v8sf,v8sf); v8sf __builtin_ia32_dpps256 (v8sf,v8sf,int); v4df __builtin_ia32_haddpd256 (v4df,v4df); v8sf __builtin_ia32_haddps256 (v8sf,v8sf); v4df __builtin_ia32_hsubpd256 (v4df,v4df); v8sf __builtin_ia32_hsubps256 (v8sf,v8sf); v32qi __builtin_ia32_lddqu256 (pcchar); v32qi __builtin_ia32_loaddqu256 (pcchar); v4df __builtin_ia32_loadupd256 (pcdouble); v8sf __builtin_ia32_loadups256 (pcfloat); v2df __builtin_ia32_maskloadpd (pcv2df,v2df); v4df __builtin_ia32_maskloadpd256 (pcv4df,v4df); v4sf __builtin_ia32_maskloadps (pcv4sf,v4sf); v8sf __builtin_ia32_maskloadps256 (pcv8sf,v8sf); void __builtin_ia32_maskstorepd (pv2df,v2df,v2df); void __builtin_ia32_maskstorepd256 (pv4df,v4df,v4df); void __builtin_ia32_maskstoreps (pv4sf,v4sf,v4sf); void __builtin_ia32_maskstoreps256 (pv8sf,v8sf,v8sf); v4df __builtin_ia32_maxpd256 (v4df,v4df); v8sf __builtin_ia32_maxps256 (v8sf,v8sf); v4df __builtin_ia32_minpd256 (v4df,v4df); v8sf __builtin_ia32_minps256 (v8sf,v8sf); v4df __builtin_ia32_movddup256 (v4df); int __builtin_ia32_movmskpd256 (v4df); int __builtin_ia32_movmskps256 (v8sf); v8sf __builtin_ia32_movshdup256 (v8sf); v8sf __builtin_ia32_movsldup256 (v8sf); v4df __builtin_ia32_mulpd256 (v4df,v4df); v8sf __builtin_ia32_mulps256 (v8sf,v8sf); v4df __builtin_ia32_orpd256 (v4df,v4df); v8sf __builtin_ia32_orps256 (v8sf,v8sf); v2df __builtin_ia32_pd_pd256 (v4df); v4df __builtin_ia32_pd256_pd (v2df); v4sf __builtin_ia32_ps_ps256 (v8sf); v8sf __builtin_ia32_ps256_ps (v4sf); int __builtin_ia32_ptestc256 (v4di,v4di,ptest); int __builtin_ia32_ptestnzc256 (v4di,v4di,ptest); int __builtin_ia32_ptestz256 (v4di,v4di,ptest); v8sf __builtin_ia32_rcpps256 (v8sf); v4df __builtin_ia32_roundpd256 (v4df,int); v8sf __builtin_ia32_roundps256 (v8sf,int); v8sf __builtin_ia32_rsqrtps_nr256 (v8sf); v8sf __builtin_ia32_rsqrtps256 (v8sf); v4df __builtin_ia32_shufpd256 (v4df,v4df,int); v8sf __builtin_ia32_shufps256 (v8sf,v8sf,int); v4si __builtin_ia32_si_si256 (v8si); v8si __builtin_ia32_si256_si (v4si); v4df __builtin_ia32_sqrtpd256 (v4df); v8sf __builtin_ia32_sqrtps_nr256 (v8sf); v8sf __builtin_ia32_sqrtps256 (v8sf); void __builtin_ia32_storedqu256 (pchar,v32qi); void __builtin_ia32_storeupd256 (pdouble,v4df); void __builtin_ia32_storeups256 (pfloat,v8sf); v4df __builtin_ia32_subpd256 (v4df,v4df); v8sf __builtin_ia32_subps256 (v8sf,v8sf); v4df __builtin_ia32_unpckhpd256 (v4df,v4df); v8sf __builtin_ia32_unpckhps256 (v8sf,v8sf); v4df __builtin_ia32_unpcklpd256 (v4df,v4df); v8sf __builtin_ia32_unpcklps256 (v8sf,v8sf); v4df __builtin_ia32_vbroadcastf128_pd256 (pcv2df); v8sf __builtin_ia32_vbroadcastf128_ps256 (pcv4sf); v4df __builtin_ia32_vbroadcastsd256 (pcdouble); v4sf __builtin_ia32_vbroadcastss (pcfloat); v8sf __builtin_ia32_vbroadcastss256 (pcfloat); v2df __builtin_ia32_vextractf128_pd256 (v4df,int); v4sf __builtin_ia32_vextractf128_ps256 (v8sf,int); v4si __builtin_ia32_vextractf128_si256 (v8si,int); v4df __builtin_ia32_vinsertf128_pd256 (v4df,v2df,int); v8sf __builtin_ia32_vinsertf128_ps256 (v8sf,v4sf,int); v8si __builtin_ia32_vinsertf128_si256 (v8si,v4si,int); v4df __builtin_ia32_vperm2f128_pd256 (v4df,v4df,int); v8sf __builtin_ia32_vperm2f128_ps256 (v8sf,v8sf,int); v8si __builtin_ia32_vperm2f128_si256 (v8si,v8si,int); v2df __builtin_ia32_vpermil2pd (v2df,v2df,v2di,int); v4df __builtin_ia32_vpermil2pd256 (v4df,v4df,v4di,int); v4sf __builtin_ia32_vpermil2ps (v4sf,v4sf,v4si,int); v8sf __builtin_ia32_vpermil2ps256 (v8sf,v8sf,v8si,int); v2df __builtin_ia32_vpermilpd (v2df,int); v4df __builtin_ia32_vpermilpd256 (v4df,int); v4sf __builtin_ia32_vpermilps (v4sf,int); v8sf __builtin_ia32_vpermilps256 (v8sf,int); v2df __builtin_ia32_vpermilvarpd (v2df,v2di); v4df __builtin_ia32_vpermilvarpd256 (v4df,v4di); v4sf __builtin_ia32_vpermilvarps (v4sf,v4si); v8sf __builtin_ia32_vpermilvarps256 (v8sf,v8si); int __builtin_ia32_vtestcpd (v2df,v2df,ptest); int __builtin_ia32_vtestcpd256 (v4df,v4df,ptest); int __builtin_ia32_vtestcps (v4sf,v4sf,ptest); int __builtin_ia32_vtestcps256 (v8sf,v8sf,ptest); int __builtin_ia32_vtestnzcpd (v2df,v2df,ptest); int __builtin_ia32_vtestnzcpd256 (v4df,v4df,ptest); int __builtin_ia32_vtestnzcps (v4sf,v4sf,ptest); int __builtin_ia32_vtestnzcps256 (v8sf,v8sf,ptest); int __builtin_ia32_vtestzpd (v2df,v2df,ptest); int __builtin_ia32_vtestzpd256 (v4df,v4df,ptest); int __builtin_ia32_vtestzps (v4sf,v4sf,ptest); int __builtin_ia32_vtestzps256 (v8sf,v8sf,ptest); void __builtin_ia32_vzeroall (void); void __builtin_ia32_vzeroupper (void); v4df __builtin_ia32_xorpd256 (v4df,v4df); v8sf __builtin_ia32_xorps256 (v8sf,v8sf);
15. Intrinsic v GCC pro instrukce AVX2
Dalších několik desítek intrinsic bude k dispozici v případě, že se použije přepínač -mavx2; pochopitelně za předpokladu, že cílový procesor bude tyto instrukce rozpoznávat a provádět:
v32qi __builtin_ia32_mpsadbw256 (v32qi,v32qi,int); v32qi __builtin_ia32_pabsb256 (v32qi); v16hi __builtin_ia32_pabsw256 (v16hi); v8si __builtin_ia32_pabsd256 (v8si); v16hi __builtin_ia32_packssdw256 (v8si,v8si); v32qi __builtin_ia32_packsswb256 (v16hi,v16hi); v16hi __builtin_ia32_packusdw256 (v8si,v8si); v32qi __builtin_ia32_packuswb256 (v16hi,v16hi); v32qi __builtin_ia32_paddb256 (v32qi,v32qi); v16hi __builtin_ia32_paddw256 (v16hi,v16hi); v8si __builtin_ia32_paddd256 (v8si,v8si); v4di __builtin_ia32_paddq256 (v4di,v4di); v32qi __builtin_ia32_paddsb256 (v32qi,v32qi); v16hi __builtin_ia32_paddsw256 (v16hi,v16hi); v32qi __builtin_ia32_paddusb256 (v32qi,v32qi); v16hi __builtin_ia32_paddusw256 (v16hi,v16hi); v4di __builtin_ia32_palignr256 (v4di,v4di,int); v4di __builtin_ia32_andsi256 (v4di,v4di); v4di __builtin_ia32_andnotsi256 (v4di,v4di); v32qi __builtin_ia32_pavgb256 (v32qi,v32qi); v16hi __builtin_ia32_pavgw256 (v16hi,v16hi); v32qi __builtin_ia32_pblendvb256 (v32qi,v32qi,v32qi); v16hi __builtin_ia32_pblendw256 (v16hi,v16hi,int); v32qi __builtin_ia32_pcmpeqb256 (v32qi,v32qi); v16hi __builtin_ia32_pcmpeqw256 (v16hi,v16hi); v8si __builtin_ia32_pcmpeqd256 (c8si,v8si); v4di __builtin_ia32_pcmpeqq256 (v4di,v4di); v32qi __builtin_ia32_pcmpgtb256 (v32qi,v32qi); v16hi __builtin_ia32_pcmpgtw256 (16hi,v16hi); v8si __builtin_ia32_pcmpgtd256 (v8si,v8si); v4di __builtin_ia32_pcmpgtq256 (v4di,v4di); v16hi __builtin_ia32_phaddw256 (v16hi,v16hi); v8si __builtin_ia32_phaddd256 (v8si,v8si); v16hi __builtin_ia32_phaddsw256 (v16hi,v16hi); v16hi __builtin_ia32_phsubw256 (v16hi,v16hi); v8si __builtin_ia32_phsubd256 (v8si,v8si); v16hi __builtin_ia32_phsubsw256 (v16hi,v16hi); v32qi __builtin_ia32_pmaddubsw256 (v32qi,v32qi); v16hi __builtin_ia32_pmaddwd256 (v16hi,v16hi); v32qi __builtin_ia32_pmaxsb256 (v32qi,v32qi); v16hi __builtin_ia32_pmaxsw256 (v16hi,v16hi); v8si __builtin_ia32_pmaxsd256 (v8si,v8si); v32qi __builtin_ia32_pmaxub256 (v32qi,v32qi); v16hi __builtin_ia32_pmaxuw256 (v16hi,v16hi); v8si __builtin_ia32_pmaxud256 (v8si,v8si); v32qi __builtin_ia32_pminsb256 (v32qi,v32qi); v16hi __builtin_ia32_pminsw256 (v16hi,v16hi); v8si __builtin_ia32_pminsd256 (v8si,v8si); v32qi __builtin_ia32_pminub256 (v32qi,v32qi); v16hi __builtin_ia32_pminuw256 (v16hi,v16hi); v8si __builtin_ia32_pminud256 (v8si,v8si); int __builtin_ia32_pmovmskb256 (v32qi); v16hi __builtin_ia32_pmovsxbw256 (v16qi); v8si __builtin_ia32_pmovsxbd256 (v16qi); v4di __builtin_ia32_pmovsxbq256 (v16qi); v8si __builtin_ia32_pmovsxwd256 (v8hi); v4di __builtin_ia32_pmovsxwq256 (v8hi); v4di __builtin_ia32_pmovsxdq256 (v4si); v16hi __builtin_ia32_pmovzxbw256 (v16qi); v8si __builtin_ia32_pmovzxbd256 (v16qi); v4di __builtin_ia32_pmovzxbq256 (v16qi); v8si __builtin_ia32_pmovzxwd256 (v8hi); v4di __builtin_ia32_pmovzxwq256 (v8hi); v4di __builtin_ia32_pmovzxdq256 (v4si); v4di __builtin_ia32_pmuldq256 (v8si,v8si); v16hi __builtin_ia32_pmulhrsw256 (v16hi, v16hi); v16hi __builtin_ia32_pmulhuw256 (v16hi,v16hi); v16hi __builtin_ia32_pmulhw256 (v16hi,v16hi); v16hi __builtin_ia32_pmullw256 (v16hi,v16hi); v8si __builtin_ia32_pmulld256 (v8si,v8si); v4di __builtin_ia32_pmuludq256 (v8si,v8si); v4di __builtin_ia32_por256 (v4di,v4di); v16hi __builtin_ia32_psadbw256 (v32qi,v32qi); v32qi __builtin_ia32_pshufb256 (v32qi,v32qi); v8si __builtin_ia32_pshufd256 (v8si,int); v16hi __builtin_ia32_pshufhw256 (v16hi,int); v16hi __builtin_ia32_pshuflw256 (v16hi,int); v32qi __builtin_ia32_psignb256 (v32qi,v32qi); v16hi __builtin_ia32_psignw256 (v16hi,v16hi); v8si __builtin_ia32_psignd256 (v8si,v8si); v4di __builtin_ia32_pslldqi256 (v4di,int); v16hi __builtin_ia32_psllwi256 (16hi,int); v16hi __builtin_ia32_psllw256(v16hi,v8hi); v8si __builtin_ia32_pslldi256 (v8si,int); v8si __builtin_ia32_pslld256(v8si,v4si); v4di __builtin_ia32_psllqi256 (v4di,int); v4di __builtin_ia32_psllq256(v4di,v2di); v16hi __builtin_ia32_psrawi256 (v16hi,int); v16hi __builtin_ia32_psraw256 (v16hi,v8hi); v8si __builtin_ia32_psradi256 (v8si,int); v8si __builtin_ia32_psrad256 (v8si,v4si); v4di __builtin_ia32_psrldqi256 (v4di, int); v16hi __builtin_ia32_psrlwi256 (v16hi,int); v16hi __builtin_ia32_psrlw256 (v16hi,v8hi); v8si __builtin_ia32_psrldi256 (v8si,int); v8si __builtin_ia32_psrld256 (v8si,v4si); v4di __builtin_ia32_psrlqi256 (v4di,int); v4di __builtin_ia32_psrlq256(v4di,v2di); v32qi __builtin_ia32_psubb256 (v32qi,v32qi); v32hi __builtin_ia32_psubw256 (v16hi,v16hi); v8si __builtin_ia32_psubd256 (v8si,v8si); v4di __builtin_ia32_psubq256 (v4di,v4di); v32qi __builtin_ia32_psubsb256 (v32qi,v32qi); v16hi __builtin_ia32_psubsw256 (v16hi,v16hi); v32qi __builtin_ia32_psubusb256 (v32qi,v32qi); v16hi __builtin_ia32_psubusw256 (v16hi,v16hi); v32qi __builtin_ia32_punpckhbw256 (v32qi,v32qi); v16hi __builtin_ia32_punpckhwd256 (v16hi,v16hi); v8si __builtin_ia32_punpckhdq256 (v8si,v8si); v4di __builtin_ia32_punpckhqdq256 (v4di,v4di); v32qi __builtin_ia32_punpcklbw256 (v32qi,v32qi); v16hi __builtin_ia32_punpcklwd256 (v16hi,v16hi); v8si __builtin_ia32_punpckldq256 (v8si,v8si); v4di __builtin_ia32_punpcklqdq256 (v4di,v4di); v4di __builtin_ia32_pxor256 (v4di,v4di); v4di __builtin_ia32_movntdqa256 (pv4di); v4sf __builtin_ia32_vbroadcastss_ps (v4sf); v8sf __builtin_ia32_vbroadcastss_ps256 (v4sf); v4df __builtin_ia32_vbroadcastsd_pd256 (v2df); v4di __builtin_ia32_vbroadcastsi256 (v2di); v4si __builtin_ia32_pblendd128 (v4si,v4si); v8si __builtin_ia32_pblendd256 (v8si,v8si); v32qi __builtin_ia32_pbroadcastb256 (v16qi); v16hi __builtin_ia32_pbroadcastw256 (v8hi); v8si __builtin_ia32_pbroadcastd256 (v4si); v4di __builtin_ia32_pbroadcastq256 (v2di); v16qi __builtin_ia32_pbroadcastb128 (v16qi); v8hi __builtin_ia32_pbroadcastw128 (v8hi); v4si __builtin_ia32_pbroadcastd128 (v4si); v2di __builtin_ia32_pbroadcastq128 (v2di); v8si __builtin_ia32_permvarsi256 (v8si,v8si); v4df __builtin_ia32_permdf256 (v4df,int); v8sf __builtin_ia32_permvarsf256 (v8sf,v8sf); v4di __builtin_ia32_permdi256 (v4di,int); v4di __builtin_ia32_permti256 (v4di,v4di,int); v4di __builtin_ia32_extract128i256 (v4di,int); v4di __builtin_ia32_insert128i256 (v4di,v2di,int); v8si __builtin_ia32_maskloadd256 (pcv8si,v8si); v4di __builtin_ia32_maskloadq256 (pcv4di,v4di); v4si __builtin_ia32_maskloadd (pcv4si,v4si); v2di __builtin_ia32_maskloadq (pcv2di,v2di); void __builtin_ia32_maskstored256 (pv8si,v8si,v8si); void __builtin_ia32_maskstoreq256 (pv4di,v4di,v4di); void __builtin_ia32_maskstored (pv4si,v4si,v4si); void __builtin_ia32_maskstoreq (pv2di,v2di,v2di); v8si __builtin_ia32_psllv8si (v8si,v8si); v4si __builtin_ia32_psllv4si (v4si,v4si); v4di __builtin_ia32_psllv4di (v4di,v4di); v2di __builtin_ia32_psllv2di (v2di,v2di); v8si __builtin_ia32_psrav8si (v8si,v8si); v4si __builtin_ia32_psrav4si (v4si,v4si); v8si __builtin_ia32_psrlv8si (v8si,v8si); v4si __builtin_ia32_psrlv4si (v4si,v4si); v4di __builtin_ia32_psrlv4di (v4di,v4di); v2di __builtin_ia32_psrlv2di (v2di,v2di); v2df __builtin_ia32_gathersiv2df (v2df, pcdouble,v4si,v2df,int); v4df __builtin_ia32_gathersiv4df (v4df, pcdouble,v4si,v4df,int); v2df __builtin_ia32_gatherdiv2df (v2df, pcdouble,v2di,v2df,int); v4df __builtin_ia32_gatherdiv4df (v4df, pcdouble,v4di,v4df,int); v4sf __builtin_ia32_gathersiv4sf (v4sf, pcfloat,v4si,v4sf,int); v8sf __builtin_ia32_gathersiv8sf (v8sf, pcfloat,v8si,v8sf,int); v4sf __builtin_ia32_gatherdiv4sf (v4sf, pcfloat,v2di,v4sf,int); v4sf __builtin_ia32_gatherdiv4sf256 (v4sf, pcfloat,v4di,v4sf,int); v2di __builtin_ia32_gathersiv2di (v2di, pcint64,v4si,v2di,int); v4di __builtin_ia32_gathersiv4di (v4di, pcint64,v4si,v4di,int); v2di __builtin_ia32_gatherdiv2di (v2di, pcint64,v2di,v2di,int); v4di __builtin_ia32_gatherdiv4di (v4di, pcint64,v4di,v4di,int); v4si __builtin_ia32_gathersiv4si (v4si, pcint,v4si,v4si,int); v8si __builtin_ia32_gathersiv8si (v8si, pcint,v8si,v8si,int); v4si __builtin_ia32_gatherdiv4si (v4si, pcint,v2di,v4si,int); v4si __builtin_ia32_gatherdiv4si256 (v4si, pcint,v4di,v4si,int);
16. Intrinsic __builtin_ia32_addps256 – součet vektorů s prvky typu float
Pro součet dvou 256bitových vektorů obsahujících osm prvků typu float je v GCC C k dispozici intrinsic nazvaná __builtin_ia32_addps256. Tuto intrinsic si můžeme velmi snadno otestovat:
#include <stdio.h> #include <immintrin.h> int main(void) { __v8sf x = { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0 }; __v8sf y = { 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1 }; __v8sf z; int i; z = __builtin_ia32_addps256(x, y); for (i = 0; i < sizeof(x) / sizeof(float); i++) { printf("%2d %f %f %f\n", i, x[i], y[i], z[i]); } }
S výsledkem:
0 1.000000 0.100000 1.100000 1 2.000000 0.100000 2.100000 2 3.000000 0.100000 3.100000 3 4.000000 0.100000 4.100000 4 5.000000 0.100000 5.100000 5 6.000000 0.100000 6.100000 6 7.000000 0.100000 7.100000 7 8.000000 0.100000 8.100000
Překlad této instrukce do assembleru je přímočarý:
z = __builtin_ia32_addps256(x, y); 4a: c5 fc 28 45 90 vmovaps ymm0,YMMWORD PTR [rbp-0x70] 4f: c5 fc 28 8d 70 ff ff vmovaps ymm1,YMMWORD PTR [rbp-0x90] 56: ff 57: c5 f4 58 c0 vaddps ymm0,ymm1,ymm0 5b: c5 fc 29 45 b0 vmovaps YMMWORD PTR [rbp-0x50],ymm0
17. Intrinsic __builtin_ia32_addpd256 – součet vektorů s prvky typu double
V případě, že se má sečíst dvojice vektorů popř. matic s prvky typu double, můžeme takovou operaci rozdělit na součet po čtveřicích hodnot typu double. V tomto případě se tedy bude v každé iteraci sčítat vektor o délce 256 bitů a použije se intrinsic __builtin_ia32_addpd256:
#include <stdio.h> #include <immintrin.h> int main(void) { __v4df x = { 1.0, 2.0, 3.0, 4.0 }; __v4df y = { 0.1, 0.1, 0.1, 0.1 }; __v4df z; int i; z = __builtin_ia32_addpd256(x, y); for (i = 0; i < sizeof(x) / sizeof(double); i++) { printf("%2d %f %f %f\n", i, x[i], y[i], z[i]); } }
Výsledek:
0 1.000000 0.100000 1.100000 1 2.000000 0.100000 2.100000 2 3.000000 0.100000 3.100000 3 4.000000 0.100000 4.100000
A takto vypadá překlad intrinsic do assembleru:
z = __builtin_ia32_addpd256(x, y); 4a: c5 fd 28 45 90 vmovapd ymm0,YMMWORD PTR [rbp-0x70] 4f: c5 fd 28 8d 70 ff ff vmovapd ymm1,YMMWORD PTR [rbp-0x90] 56: ff 57: c5 f5 58 c0 vaddpd ymm0,ymm1,ymm0 5b: c5 fd 29 45 b0 vmovapd YMMWORD PTR [rbp-0x50],ymm0
18. Výběr prvků do cílového vektoru pomocí intrinsic __builtin_ia32_blendps256
V rozšíření instrukční sady AVX nalezneme celou řadu zajímavých instrukcí, z nichž mnohé mají poněkud matoucí jméno. Týká se to například instrukce nazvané BLENDPS pro typ single/float a BLENDPD pro typ double. Tato instrukce, i když to její název naznačuje, ovšem neprovádí blending hodnot uložených ve dvou vektorech na základě zadané váhy (blending factor, což může být u obrázků alfa kanál). Tato instrukce sice skutečně „michá“ prvky dvou vektorů, ovšem na základě celočíselné konstanty, která obsahuje binárně zakódované selektory. Nejlépe se prováděná operace ukáže na dvou vektorech, z nichž každý obsahuje osm prvků typu single/float. Celočíselná konstanta v tomto případě obsahuje osm významových bitů, z nichž každý bit určuje, zda se daný n-tý prvek výsledného vektoru získá z vektoru prvního nebo naopak z vektoru druhého.
Podívejme se nyní na příklad, v němž se postupně používají konstanty/selektory 0×55 (tedy 0b01010101) 0×f0 (0b11110000) a 0×0f (0b00001111):
#include <stdio.h> #include <immintrin.h> int main(void) { __v8sf x = { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0 }; __v8sf y = { 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8 }; __v8sf z; int i; z = __builtin_ia32_blendps256(x, y, 0x55); for (i = 0; i < sizeof(x) / sizeof(float); i++) { printf("%2d %f %f %f\n", i, x[i], y[i], z[i]); } putchar('\n'); z = __builtin_ia32_blendps256(x, y, 0xf0); for (i = 0; i < sizeof(x) / sizeof(float); i++) { printf("%2d %f %f %f\n", i, x[i], y[i], z[i]); } putchar('\n'); z = __builtin_ia32_blendps256(x, y, 0x0f); for (i = 0; i < sizeof(x) / sizeof(float); i++) { printf("%2d %f %f %f\n", i, x[i], y[i], z[i]); } }
Výsledek výpočtů vypadá následovně (hodnoty z prvního vektoru, které se dostaly do výsledku, jsou zvýrazněny):
0 1.000000 0.100000 0.100000 1 2.000000 0.200000 2.000000 2 3.000000 0.300000 0.300000 3 4.000000 0.400000 4.000000 4 5.000000 0.500000 0.500000 5 6.000000 0.600000 6.000000 6 7.000000 0.700000 0.700000 7 8.000000 0.800000 8.000000 0 1.000000 0.100000 1.000000 1 2.000000 0.200000 2.000000 2 3.000000 0.300000 3.000000 3 4.000000 0.400000 4.000000 4 5.000000 0.500000 0.500000 5 6.000000 0.600000 0.600000 6 7.000000 0.700000 0.700000 7 8.000000 0.800000 0.800000 0 1.000000 0.100000 0.100000 1 2.000000 0.200000 0.200000 2 3.000000 0.300000 0.300000 3 4.000000 0.400000 0.400000 4 5.000000 0.500000 5.000000 5 6.000000 0.600000 6.000000 6 7.000000 0.700000 7.000000 7 8.000000 0.800000 8.000000
Způsob překladu této intrinsic do assembleru je založen na použití instrukce vblendps (nebo jen blendps), kde s na konci značí operaci s vektorem s prvky typu single neboli float:
z = __builtin_ia32_blendps256(x, y, 0x55); 4a: c5 fc 28 4d 90 vmovaps ymm1,YMMWORD PTR [rbp-0x70] 4f: c5 fc 28 85 70 ff ff vmovaps ymm0,YMMWORD PTR [rbp-0x90] 56: ff 57: c4 e3 7d 0c c1 55 vblendps ymm0,ymm0,ymm1,0x55 5d: c5 fc 29 45 b0 vmovaps YMMWORD PTR [rbp-0x50],ymm0 z = __builtin_ia32_blendps256(x, y, 0xf0); dc: c5 fc 28 4d 90 vmovaps ymm1,YMMWORD PTR [rbp-0x70] e1: c5 fc 28 85 70 ff ff vmovaps ymm0,YMMWORD PTR [rbp-0x90] e8: ff e9: c4 e3 7d 0c c1 f0 vblendps ymm0,ymm0,ymm1,0xf0 ef: c5 fc 29 45 b0 vmovaps YMMWORD PTR [rbp-0x50],ymm0 z = __builtin_ia32_blendps256(x, y, 0x0f); 16e: c5 fc 28 4d 90 vmovaps ymm1,YMMWORD PTR [rbp-0x70] 173: c5 fc 28 85 70 ff ff vmovaps ymm0,YMMWORD PTR [rbp-0x90] 17a: ff 17b: c4 e3 7d 0c c1 0f vblendps ymm0,ymm0,ymm1,0xf 181: c5 fc 29 45 b0 vmovaps YMMWORD PTR [rbp-0x50],ymm0
19. Repositář s demonstračními příklady
Demonstrační příklady napsané v jazyku C, které jsou určené pro překlad pomocí překladače GCC C, byly uložen do Git repositáře, který je dostupný na adrese https://github.com/tisnik/presentations. Jednotlivé demonstrační příklady si můžete v případě potřeby stáhnout i jednotlivě bez nutnosti klonovat celý (dnes již poměrně velmi rozsáhlý) repositář:
# | Příklad | Stručný popis | Adresa |
---|---|---|---|
1 | simd01.c | vektor celých čísel typu short int | https://github.com/tisnik/presentations/blob/master/SIMD/simd01.c |
2 | simd02.c | ukázka použití vektorů s celočíselnými typy bez znaménka | https://github.com/tisnik/presentations/blob/master/SIMD/simd02.c |
3 | simd03.c | ukázka použití vektorů s celočíselnými typy se znaménkem | https://github.com/tisnik/presentations/blob/master/SIMD/simd03.c |
4 | simd04.c | paralelní součet celočíselných prvků vektorů | https://github.com/tisnik/presentations/blob/master/SIMD/simd04.c |
5 | simd04B.c | úprava pro další datové typy | https://github.com/tisnik/presentations/blob/master/SIMD/simd04B.c |
6 | simd05.c | přístup k jednotlivým prvkům vektorů | https://github.com/tisnik/presentations/blob/master/SIMD/simd05.c |
7 | simd05B.c | korektnější výpočet počtu prvků vektoru | https://github.com/tisnik/presentations/blob/master/SIMD/simd05B.c |
8 | simd05C.c | definice typu vektoru | https://github.com/tisnik/presentations/blob/master/SIMD/simd05C.c |
9 | simd06.c | vektor čísel s plovoucí řádovou čárkou | https://github.com/tisnik/presentations/blob/master/SIMD/simd06.c |
10 | simd07.c | paralelní součet prvků vektorů (typ float) | https://github.com/tisnik/presentations/blob/master/SIMD/simd07.c |
11 | simd08.c | paralelní součet prvků vektorů (typ double) | https://github.com/tisnik/presentations/blob/master/SIMD/simd08.c |
12 | simd09.c | překročení délky vektoru | https://github.com/tisnik/presentations/blob/master/SIMD/simd09.c |
13 | simd10.c | přístup k jednotlivým prvkům vektorů | https://github.com/tisnik/presentations/blob/master/SIMD/simd10.c |
14 | simd11.c | překročení délky vektoru | https://github.com/tisnik/presentations/blob/master/SIMD/simd11.c |
15 | simd12.c | dlouhý vektor s 256 bajty | https://github.com/tisnik/presentations/blob/master/SIMD/simd12.c |
16 | simd13.c | operace součtu pro vektory s celočíselnými prvky rozličné bitové šířky bez znaménka | https://github.com/tisnik/presentations/blob/master/SIMD/simd13.c |
17 | simd14.c | operace součtu pro vektory s celočíselnými prvky rozličné bitové šířky se znaménkem | https://github.com/tisnik/presentations/blob/master/SIMD/simd14.c |
18 | simd15.c | operace součtu pro vektory s prvky rozličné bitové šířky s plovoucí řádovou čárkou | https://github.com/tisnik/presentations/blob/master/SIMD/simd15.c |
19 | simd16.c | operace součtu pro dlouhé vektory s prvky rozličné bitové šířky s plovoucí řádovou čárkou | https://github.com/tisnik/presentations/blob/master/SIMD/simd16.c |
20 | simd17.c | všechny podporované binární operace nad vektory s celočíselnými prvky se znaménkem | https://github.com/tisnik/presentations/blob/master/SIMD/simd17.c |
21 | simd18.c | všechny podporované binární operace nad vektory s prvky typu float | https://github.com/tisnik/presentations/blob/master/SIMD/simd18.c |
23 | intrinsic_mmx1.c | intrinsic pro technologii MMX: instrukce paddb | https://github.com/tisnik/presentations/blob/master/SIMD/intrinsic_mmx1.c |
24 | intrinsic_mmx2.c | intrinsic pro technologii MMX: instrukce paddw | https://github.com/tisnik/presentations/blob/master/SIMD/intrinsic_mmx2.c |
25 | intrinsic_mmx3.c | intrinsic pro technologii MMX: instrukce paddb (přetečení) | https://github.com/tisnik/presentations/blob/master/SIMD/intrinsic_mmx3.c |
26 | intrinsic_mmx4.c | intrinsic pro technologii MMX: instrukce paddsb (saturace) | https://github.com/tisnik/presentations/blob/master/SIMD/intrinsic_mmx4.c |
27 | intrinsic_mmx5.c | intrinsic pro technologii MMX: instrukce pupckhbw (kombinace dvou vektorů) | https://github.com/tisnik/presentations/blob/master/SIMD/intrinsic_mmx5.c |
28 | intrinsic_sse1.c | součet dvou vektorů s šestnácti prvky typu char instrukcí paddb128 | https://github.com/tisnik/presentations/blob/master/SIMD/intrinsic_sse1.c |
29 | intrinsic_sse2.c | součet dvou vektorů s osmi prvky typu short instrukcí paddw128 | https://github.com/tisnik/presentations/blob/master/SIMD/intrinsic_sse2.c |
30 | intrinsic_sse3.c | součet dvou vektorů se čtyřmi prvky typu int instrukcí paddd128 | https://github.com/tisnik/presentations/blob/master/SIMD/intrinsic_sse3.c |
31 | intrinsic_sse4.c | součet dvou vektorů se dvěma prvky typu long instrukcí paddq128 | https://github.com/tisnik/presentations/blob/master/SIMD/intrinsic_sse4.c |
32 | intrinsic_sse5.c | součet dvou vektorů se čtyřmi prvky typu float instrukcí addps | https://github.com/tisnik/presentations/blob/master/SIMD/intrinsic_sse5.c |
33 | intrinsic_sse6.c | součet dvou vektorů se dvěma prvky typu double instrukcí addpd | https://github.com/tisnik/presentations/blob/master/SIMD/intrinsic_sse6.c |
34 | intrinsic_sse7.c | porovnání celočíselných prvků instrukcemi pcmpeqb128 a pcmpgtb128 | https://github.com/tisnik/presentations/blob/master/SIMD/intrinsic_sse7.c |
35 | intrinsic_sse8.c | všech šest relačních operací pro vektory s prvky typu float | https://github.com/tisnik/presentations/blob/master/SIMD/intrinsic_sse8.c |
36 | intrinsic_sse9.c | unární operace pro výpočet převrácené hodnoty, druhé odmocniny a převrácené hodnoty druhé odmocniny | https://github.com/tisnik/presentations/blob/master/SIMD/intrinsic_sse9.c |
37 | intrinsic_sse_A.c | instrukce shufps a její intrinsic | https://github.com/tisnik/presentations/blob/master/SIMD/intrinsic_sse_A.c |
38 | intrinsic_sse_B.c | instrukce unpckhps a unpcklps a jejich intrinsics | https://github.com/tisnik/presentations/blob/master/SIMD/intrinsic_sse_B.c |
39 | simd19.c | operace součtu vektorů o délce 256 bitů s celočíselnými prvky bez znaménka | https://github.com/tisnik/presentations/blob/master/SIMD/simd19.c |
40 | simd20.c | operace součtu vektorů o délce 256 bitů s celočíselnými prvky se znaménkem | https://github.com/tisnik/presentations/blob/master/SIMD/simd20.c |
41 | simd21.c | operace součtu vektorů o délce 256 bitů s prvky typu float a double | https://github.com/tisnik/presentations/blob/master/SIMD/simd21.c |
42 | intrinsic_avx1.c | operace součtu vektorů o délce 256 bitů s prvky typu float | https://github.com/tisnik/presentations/blob/master/SIMD/intrinsic_avx1.c |
43 | intrinsic_avx2.c | operace součtu vektorů o délce 256 bitů s prvky typu double | https://github.com/tisnik/presentations/blob/master/SIMD/intrinsic_avx2.c |
44 | intrinsic_avx3.c | operace výběru prvků z vektorů o délce 256 bitů s prvky typu float | https://github.com/tisnik/presentations/blob/master/SIMD/intrinsic_avx3.c |
45 | Makefile | Makefile pro překlad demonstračních příkladů | https://github.com/tisnik/presentations/blob/master/SIMD/Makefile |
Soubory vzniklé překladem z jazyka C do assembleru procesorů x86–64:
20. Odkazy na Internetu
- GCC documentation: Extensions to the C Language Family
https://gcc.gnu.org/onlinedocs/gcc/C-Extensions.html#C-Extensions - GCC documentation: Using Vector Instructions through Built-in Functions
https://gcc.gnu.org/onlinedocs/gcc/Vector-Extensions.html - SSE (Streaming SIMD Extentions)
http://www.songho.ca/misc/sse/sse.html - Timothy A. Chagnon: SSE and SSE2
http://www.cs.drexel.edu/~tc365/mpi-wht/sse.pdf - Intel corporation: Extending the Worldr's Most Popular Processor Architecture
http://download.intel.com/technology/architecture/new-instructions-paper.pdf - SIMD architectures:
http://arstechnica.com/old/content/2000/03/simd.ars/ - Tour of the Black Holes of Computing!: Floating Point
http://www.cs.hmc.edu/~geoff/classes/hmc.cs105…/slides/class02_floats.ppt - 3Dnow! Technology Manual
AMD Inc., 2000 - Intel MMXTM Technology Overview
Intel corporation, 1996 - MultiMedia eXtensions
http://softpixel.com/~cwright/programming/simd/mmx.phpi - AMD K5 („K5“ / „5k86“)
http://www.pcguide.com/ref/cpu/fam/g5K5-c.html - Sixth Generation Processors
http://www.pcguide.com/ref/cpu/fam/g6.htm - Great Microprocessors of the Past and Present
http://www.cpushack.com/CPU/cpu1.html - Very long instruction word (Wikipedia)
http://en.wikipedia.org/wiki/Very_long_instruction_word - CPU design (Wikipedia)
http://en.wikipedia.org/wiki/CPU_design - Bulldozer (microarchitecture)
https://en.wikipedia.org/wiki/Bulldozer_(microarchitecture) - SIMD Instructions Considered Harmful
https://www.sigarch.org/simd-instructions-considered-harmful/ - GCC Compiler Intrinsics
https://iq.opengenus.org/gcc-compiler-intrinsics/ - Scalable_Vector_Extension_(SVE)
https://en.wikipedia.org/wiki/AArch64#Scalable_Vector_Extension_(SVE) - FADD/FADDP/FIADD — Add
https://www.felixcloutier.com/x86/fadd:faddp:fiadd - ADDPS — Add Packed Single-Precision Floating-Point Values
https://www.felixcloutier.com/x86/addps - ADDPD — Add Packed Double-Precision Floating-Point Values
https://www.felixcloutier.com/x86/addpd - FDIV/FDIVP/FIDIV — Divide
https://www.felixcloutier.com/x86/fdiv:fdivp:fidiv - IDIV — Signed Divide
https://www.felixcloutier.com/x86/idiv - PADDB/PADDW/PADDD/PADDQ — Add Packed Integers
https://www.felixcloutier.com/x86/paddb:paddw:paddd:paddq - PSUBB/PSUBW/PSUBD — Subtract Packed Integers
https://www.felixcloutier.com/x86/psubb:psubw:psubd - PMULLW — Multiply Packed Signed Integers and Store Low Result
https://www.felixcloutier.com/x86/pmullw - PUNPCKLBW/PUNPCKLWD/PUNPCKLDQ/PUNPCKLQDQ — Unpack Low Data
https://www.felixcloutier.com/x86/punpcklbw:punpcklwd:punpckldq:punpcklqdq - PUNPCKHBW/PUNPCKHWD/PUNPCKHDQ/PUNPCKHQDQ — Unpack High Data
https://www.felixcloutier.com/x86/punpckhbw:punpckhwd:punpckhdq:punpckhqdq - PACKUSWB — Pack with Unsigned Saturation
https://www.felixcloutier.com/x86/packuswb - ADDPS — Add Packed Single-Precision Floating-Point Values
https://www.felixcloutier.com/x86/addps - SUBPS — Subtract Packed Single-Precision Floating-Point Values
https://www.felixcloutier.com/x86/subps - MULPS — Multiply Packed Single-Precision Floating-Point Values
https://www.felixcloutier.com/x86/mulps - DIVPS — Divide Packed Single-Precision Floating-Point Values
https://www.felixcloutier.com/x86/divps - CBW/CWDE/CDQE — Convert Byte to Word/Convert Word to Doubleword/Convert Doubleword to Quadword
https://www.felixcloutier.com/x86/cbw:cwde:cdqe - PAND — Logical AND
https://www.felixcloutier.com/x86/pand - POR — Bitwise Logical OR
https://www.felixcloutier.com/x86/por - PXOR — Logical Exclusive OR
https://www.felixcloutier.com/x86/pxor - Improve the Multimedia User Experience
https://www.arm.com/technologies/neon - NEON Technology (stránky ARM)
https://developer.arm.com/technologies/neon - SIMD Assembly Tutorial: ARM NEON – Xiph.org
https://people.xiph.org/~tterribe/daala/neon_tutorial.pdf - Ne10
http://projectne10.github.io/Ne10/ - NEON and Floating-Point architecture
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0024a/BABIGHEB.html - An Introduction to ARM NEON
http://peterdn.com/post/an-introduction-to-ARM-NEON.aspx - ARM NEON Intrinsics Reference
http://infocenter.arm.com/help/topic/com.arm.doc.ihi0073a/IHI0073A_arm_neon_intrinsics_ref.pdf - Arm Neon Intrinsics vs hand assembly
https://stackoverflow.com/questions/9828567/arm-neon-intrinsics-vs-hand-assembly - ARM NEON Optimization. An Example
http://hilbert-space.de/?p=22 - AArch64 NEON instruction format
https://developer.arm.com/docs/den0024/latest/7-aarch64-floating-point-and-neon/73-aarch64-neon-instruction-format - ARM SIMD instructions
https://developer.arm.com/documentation/dht0002/a/Introducing-NEON/What-is-SIMD-/ARM-SIMD-instructions - Learn the architecture – Migrate Neon to SVE Version 1.0
https://developer.arm.com/documentation/102131/0100/?lang=en - 1.2.2. Comparison between NEON technology and other SIMD solutions
https://developer.arm.com/documentation/den0018/a/Introduction/Comparison-between-ARM-NEON-technology-and-other-implementations/Comparison-between-NEON-technology-and-other-SIMD-solutions?lang=en - NEON Programmer’s Guide
https://documentation-service.arm.com/static/63299276e68c6809a6b41308 - Brain Floating Point – nový formát uložení čísel pro strojové učení a chytrá čidla
https://www.root.cz/clanky/brain-floating-point-ndash-novy-format-ulozeni-cisel-pro-strojove-uceni-a-chytra-cidla/ - Other Built-in Functions Provided by GCC
https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html - GCC: 6.60 Built-in Functions Specific to Particular Target Machines
https://gcc.gnu.org/onlinedocs/gcc/Target-Builtins.html#Target-Builtins - Advanced Vector Extensions
https://en.wikipedia.org/wiki/Advanced_Vector_Extensions - AVX-512
https://en.wikipedia.org/wiki/AVX-512 - AVX-512
https://iq.opengenus.org/avx512/ - Downclocking pro AVX-512
https://en.wikipedia.org/wiki/Advanced_Vector_Extensions#Downclocking - BLENDPS — Blend Packed Single Precision Floating-Point Values
https://www.felixcloutier.com/x86/blendps - BLENDPD — Blend Packed Double Precision Floating-Point Values
https://www.felixcloutier.com/x86/blendpd