Hlavní navigace

Rozšíření instrukční sady „Advanced Vector Extensions“ na platformě x86–64

1. 11. 2022
Doba čtení: 69 minut

Sdílet

 Autor: Depositphotos
Článek je věnován rozšíření instrukční sady „Advanced Vector Extension“ neboli AVX. Oproti MMX či SSE se jedná o výrazné vylepšení podpory SIMD operací s delšími vektory a s novými instrukcemi.

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

4. Tříadresový kód

5. Nový způsob kódování instrukcí

6. Praktická část

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 floatdouble

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

20. Odkazy na Internetu

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 YMM0ZMM0:

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
Poznámka: vzhledem ke způsobu pojmenování XMMxYMMxZMMx není zřejmé, jaká budou jména registrů v budoucím rozšíření. Podle ASCII tabulky by jména měla začínat na [MMx :-)

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]
Poznámka: samozřejmě je nutné použít širší instrukční slova, protože je nutné zakódovat index jednoho ze třiceti dvou pracovních registrů.

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.

Poznámka: další rozšíření bylo zavedeno v rámci AVX-512 a jmenuje se EVEX.

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á):

  1. 32 prvků s šířkou 8bitů (unsigned char)
  2. 16 prvků s šířkou 16bitů (unsigned short)
  3. 8 prvků s šířkou 32bitů (unsigned int)
  4. 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:

  1. S využitím instrukční sady SSE, ale nikoli AVX
  2. S využitím instrukční sady AVX
  3. 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:

  1. 32 prvků s šířkou 8bitů (signed char)
  2. 16 prvků s šířkou 16bitů (signed short)
  3. 8 prvků s šířkou 32bitů (signed int)
  4. 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:

  1. S využitím instrukční sady SSE, ale nikoli AVX
  2. S využitím instrukční sady AVX
  3. 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 floatdouble

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
Poznámka: povšimněte si, že je sice použit tříadresový kód, ovšem registr YMM0 je současně registrem zdrojovým i cílovým.

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:

ict ve školství 24

    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/pre­sentations. 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/pre­sentations/blob/master/SIM­D/simd01.c
2 simd02.c ukázka použití vektorů s celočíselnými typy bez znaménka https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd02.c
3 simd03.c ukázka použití vektorů s celočíselnými typy se znaménkem https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd03.c
4 simd04.c paralelní součet celočíselných prvků vektorů https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd04.c
5 simd04B.c úprava pro další datové typy https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd04B.c
6 simd05.c přístup k jednotlivým prvkům vektorů https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd05.c
7 simd05B.c korektnější výpočet počtu prvků vektoru https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd05B.c
8 simd05C.c definice typu vektoru https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd05C.c
9 simd06.c vektor čísel s plovoucí řádovou čárkou https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd06.c
10 simd07.c paralelní součet prvků vektorů (typ float) https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd07.c
11 simd08.c paralelní součet prvků vektorů (typ double) https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd08.c
12 simd09.c překročení délky vektoru https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd09.c
13 simd10.c přístup k jednotlivým prvkům vektorů https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd10.c
14 simd11.c překročení délky vektoru https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd11.c
15 simd12.c dlouhý vektor s 256 bajty https://github.com/tisnik/pre­sentations/blob/master/SIM­D/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/pre­sentations/blob/master/SIM­D/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/pre­sentations/blob/master/SIM­D/simd14.c
18 simd15.c operace součtu pro vektory s prvky rozličné bitové šířky s plovoucí řádovou čárkou https://github.com/tisnik/pre­sentations/blob/master/SIM­D/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/pre­sentations/blob/master/SIM­D/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/pre­sentations/blob/master/SIM­D/simd17.c
21 simd18.c všechny podporované binární operace nad vektory s prvky typu float https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd18.c
       
23 intrinsic_mmx1.c intrinsic pro technologii MMX: instrukce paddb https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_mmx1.c
24 intrinsic_mmx2.c intrinsic pro technologii MMX: instrukce paddw https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_mmx2.c
25 intrinsic_mmx3.c intrinsic pro technologii MMX: instrukce paddb (přetečení) https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_mmx3.c
26 intrinsic_mmx4.c intrinsic pro technologii MMX: instrukce paddsb (saturace) https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_mmx4.c
27 intrinsic_mmx5.c intrinsic pro technologii MMX: instrukce pupckhbw (kombinace dvou vektorů) https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_mmx5.c
       
28 intrinsic_sse1.c součet dvou vektorů s šestnácti prvky typu char instrukcí paddb128 https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_sse1.c
29 intrinsic_sse2.c součet dvou vektorů s osmi prvky typu short instrukcí paddw128 https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_sse2.c
30 intrinsic_sse3.c součet dvou vektorů se čtyřmi prvky typu int instrukcí paddd128 https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_sse3.c
31 intrinsic_sse4.c součet dvou vektorů se dvěma prvky typu long instrukcí paddq128 https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_sse4.c
32 intrinsic_sse5.c součet dvou vektorů se čtyřmi prvky typu float instrukcí addps https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_sse5.c
33 intrinsic_sse6.c součet dvou vektorů se dvěma prvky typu double instrukcí addpd https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_sse6.c
34 intrinsic_sse7.c porovnání celočíselných prvků instrukcemi pcmpeqb128 a pcmpgtb128 https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_sse7.c
35 intrinsic_sse8.c všech šest relačních operací pro vektory s prvky typu float https://github.com/tisnik/pre­sentations/blob/master/SIM­D/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/pre­sentations/blob/master/SIM­D/intrinsic_sse9.c
37 intrinsic_sse_A.c instrukce shufps a její intrinsic https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_sse_A.c
38 intrinsic_sse_B.c instrukce unpckhps a unpcklps a jejich intrinsics https://github.com/tisnik/pre­sentations/blob/master/SIM­D/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/pre­sentations/blob/master/SIM­D/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/pre­sentations/blob/master/SIM­D/simd20.c
41 simd21.c operace součtu vektorů o délce 256 bitů s prvky typu floatdouble https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd21.c
       
42 intrinsic_avx1.c operace součtu vektorů o délce 256 bitů s prvky typu float https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_avx1.c
43 intrinsic_avx2.c operace součtu vektorů o délce 256 bitů s prvky typu double https://github.com/tisnik/pre­sentations/blob/master/SIM­D/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/pre­sentations/blob/master/SIM­D/intrinsic_avx3.c
       
45 Makefile Makefile pro překlad demonstračních příkladů https://github.com/tisnik/pre­sentations/blob/master/SIM­D/Makefile

Soubory vzniklé překladem z jazyka C do assembleru procesorů x86–64:

# Příklad Stručný popis Adresa
1 simd04_1.lst překlad zdrojového kódu simd04_1.c s přepínači -O0 -mno-sse -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd04_1.lst
2 simd04_2.lst překlad zdrojového kódu simd04_2.c s přepínači -O0 -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd04_2.lst
3 simd04B1.lst překlad zdrojového kódu simd04B1.c s přepínači -O0 -mno-sse -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd04B1.lst
4 simd04B2.lst překlad zdrojového kódu simd04B2.c s přepínači -O0 -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd04B2.lst
5 simd07_1.lst překlad zdrojového kódu simd07_1.c s přepínači -mno-sse -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd07_1.lst
6 simd07_2.lst překlad zdrojového kódu simd07_2.c s přepínači -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd07_2.lst
7 simd08_1.lst překlad zdrojového kódu simd08_1.c s přepínači -mno-sse -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd08_1.lst
8 simd08_2.lst překlad zdrojového kódu simd08_2.c s přepínači -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd08_2.lst
9 simd12_1.lst překlad zdrojového kódu simd12_1.c s přepínači -O0 -mno-sse -g  https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd12_1.lst
10 simd12_2.lst překlad zdrojového kódu simd12_2.c s přepínači -O0 -g  https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd12_2.lst
11 simd13_1.lst překlad zdrojového kódu simd13_1.c s přepínači -O0 -mno-sse -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd13_1.lst
12 simd13_2.lst překlad zdrojového kódu simd13_2.c s přepínači -O0 -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd13_2.lst
13 simd13_3.lst překlad zdrojového kódu simd13_3.c s přepínači -O3 -mno-sse -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd13_3.lst
14 simd13_4.lst překlad zdrojového kódu simd13_4.c s přepínači -O3 -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd13_4.lst
15 simd14_1.lst překlad zdrojového kódu simd14_1.c s přepínači -O0 -mno-sse -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd14_1.lst
16 simd14_2.lst překlad zdrojového kódu simd14_2.c s přepínači -O0 -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd14_2.lst
17 simd14_3.lst překlad zdrojového kódu simd14_3.c s přepínači -O3 -mno-sse -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd14_3.lst
18 simd14_4.lst překlad zdrojového kódu simd14_4.c s přepínači -O3 -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd14_4.lst
19 simd15_1.lst překlad zdrojového kódu simd15_1.c s přepínači -O0 -mno-sse -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd15_1.lst
20 simd15_2.lst překlad zdrojového kódu simd15_2.c s přepínači -O0 -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd15_2.lst
21 simd15_3.lst překlad zdrojového kódu simd15_3.c s přepínači -O3 -mno-sse -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd15_3.lst
22 simd15_4.lst překlad zdrojového kódu simd15_4.c s přepínači -O3 -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd15_4.lst
23 simd16_1.lst překlad zdrojového kódu simd16_1.c s přepínači -O0 -mno-sse -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd16_1.lst
24 simd16_2.lst překlad zdrojového kódu simd16_2.c s přepínači -O0 -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd16_2.lst
25 simd16_3.lst překlad zdrojového kódu simd16_3.c s přepínači -O3 -mno-sse -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd16_3.lst
26 simd16_4.lst překlad zdrojového kódu simd16_4.c s přepínači -O3 -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd16_4.lst
27 simd17_1.lst překlad zdrojového kódu simd17_1.c s přepínači -O0 -mno-sse -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd17_1.lst
28 simd17_2.lst překlad zdrojového kódu simd17_2.c s přepínači -O0 -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd17_2.lst
29 simd17_3.lst překlad zdrojového kódu simd17_3.c s přepínači -O3 -mno-sse -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd17_3.lst
30 simd17_4.lst překlad zdrojového kódu simd17_4.c s přepínači -O3 -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd17_4.lst
31 simd18_1.lst překlad zdrojového kódu simd18_1.c s přepínači -O0 -mno-sse -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd18_1.lst
32 simd18_2.lst překlad zdrojového kódu simd18_2.c s přepínači -O0 -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd18_2.lst
33 simd18_3.lst překlad zdrojového kódu simd18_3.c s přepínači -O3 -mno-sse -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd18_3.lst
34 simd18_4.lst překlad zdrojového kódu simd18_4.c s přepínači -O3 -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd18_4.lst
       
35 intrinsic_mmx1.lst překlad zdrojového kódu intrinsic_mmx1.c https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_mmx1.lst
36 intrinsic_mmx2.lst překlad zdrojového kódu intrinsic_mmx2.c https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_mmx2.lst
37 intrinsic_mmx3.lst překlad zdrojového kódu intrinsic_mmx3.c https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_mmx3.lst
39 intrinsic_mmx5.lst překlad zdrojového kódu intrinsic_mmx5.c https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_mmx5.lst
       
40 intrinsic_sse1.lst překlad zdrojového kódu intrinsic_sse1.c https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_sse1.lst
41 intrinsic_sse2.lst překlad zdrojového kódu intrinsic_sse2.c https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_sse2.lst
42 intrinsic_sse3.lst překlad zdrojového kódu intrinsic_sse3.c https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_sse3.lst
43 intrinsic_sse4.lst překlad zdrojového kódu intrinsic_sse4.c https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_sse4.lst
44 intrinsic_sse5.lst překlad zdrojového kódu intrinsic_sse5.c https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_sse5.lst
45 intrinsic_sse6.lst překlad zdrojového kódu intrinsic_sse6.c https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_sse6.lst
46 intrinsic_sse7.lst překlad zdrojového kódu intrinsic_sse7.c https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_sse7.lst
47 intrinsic_sse8.lst překlad zdrojového kódu intrinsic_sse8.c https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_sse8.lst
48 intrinsic_sse9.lst překlad zdrojového kódu intrinsic_sse9.c https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_sse9.lst
49 intrinsic_sse_A.lst překlad zdrojového kódu intrinsic_sse_A.c https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_sse_A.lst
50 intrinsic_sse_B.lst překlad zdrojového kódu intrinsic_sse_B.c https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_sse_B.lst
       
51 simd19_sse.lst překlad zdrojového kódu simd19.c s přepínačem -msse -mno-avx https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd19_sse.lst
52 simd19_avx.lst překlad zdrojového kódu simd19.c s přepínačem -mavx https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd19_avx.lst
53 simd19_avx2.lst překlad zdrojového kódu simd19.c s přepínačem -mavx2 https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd19_avx2.lst
54 simd20_sse.lst překlad zdrojového kódu simd20.c s přepínačem -msse -mno-avx https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd20_sse.lst
55 simd20_avx.lst překlad zdrojového kódu simd20.c s přepínačem -mavx https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd20_avx.lst
56 simd20_avx2.lst překlad zdrojového kódu simd20.c s přepínačem -mavx2 https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd20_avx2.lst
57 simd21_sse.lst překlad zdrojového kódu simd21.c s přepínačem -msse -mno-avx https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd21_sse.lst
58 simd21_avx.lst překlad zdrojového kódu simd21.c s přepínačem -mavx https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd21_avx.lst
59 simd21_avx2.lst překlad zdrojového kódu simd21.c s přepínačem -mavx3 https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd21_avx2.lst

20. Odkazy na Internetu

  1. GCC documentation: Extensions to the C Language Family
    https://gcc.gnu.org/onlinedocs/gcc/C-Extensions.html#C-Extensions
  2. GCC documentation: Using Vector Instructions through Built-in Functions
    https://gcc.gnu.org/online­docs/gcc/Vector-Extensions.html
  3. SSE (Streaming SIMD Extentions)
    http://www.songho.ca/misc/sse/sse­.html
  4. Timothy A. Chagnon: SSE and SSE2
    http://www.cs.drexel.edu/~tc365/mpi-wht/sse.pdf
  5. Intel corporation: Extending the Worldr's Most Popular Processor Architecture
    http://download.intel.com/techno­logy/architecture/new-instructions-paper.pdf
  6. SIMD architectures:
    http://arstechnica.com/ol­d/content/2000/03/simd.ar­s/
  7. Tour of the Black Holes of Computing!: Floating Point
    http://www.cs.hmc.edu/~ge­off/classes/hmc.cs105…/sli­des/class02_floats.ppt
  8. 3Dnow! Technology Manual
    AMD Inc., 2000
  9. Intel MMXTM Technology Overview
    Intel corporation, 1996
  10. MultiMedia eXtensions
    http://softpixel.com/~cwrig­ht/programming/simd/mmx.phpi
  11. AMD K5 („K5“ / „5k86“)
    http://www.pcguide.com/ref/cpu/fam/g5K5-c.html
  12. Sixth Generation Processors
    http://www.pcguide.com/ref/cpu/fam/g6­.htm
  13. Great Microprocessors of the Past and Present
    http://www.cpushack.com/CPU/cpu1.html
  14. Very long instruction word (Wikipedia)
    http://en.wikipedia.org/wi­ki/Very_long_instruction_word
  15. CPU design (Wikipedia)
    http://en.wikipedia.org/wi­ki/CPU_design
  16. Bulldozer (microarchitecture)
    https://en.wikipedia.org/wi­ki/Bulldozer_(microarchitec­ture)
  17. SIMD Instructions Considered Harmful
    https://www.sigarch.org/simd-instructions-considered-harmful/
  18. GCC Compiler Intrinsics
    https://iq.opengenus.org/gcc-compiler-intrinsics/
  19. Scalable_Vector_Extension_(SVE)
    https://en.wikipedia.org/wi­ki/AArch64#Scalable_Vector_Ex­tension_(SVE)
  20. FADD/FADDP/FIADD — Add
    https://www.felixcloutier­.com/x86/fadd:faddp:fiadd
  21. ADDPS — Add Packed Single-Precision Floating-Point Values
    https://www.felixcloutier­.com/x86/addps
  22. ADDPD — Add Packed Double-Precision Floating-Point Values
    https://www.felixcloutier­.com/x86/addpd
  23. FDIV/FDIVP/FIDIV — Divide
    https://www.felixcloutier­.com/x86/fdiv:fdivp:fidiv
  24. IDIV — Signed Divide
    https://www.felixcloutier­.com/x86/idiv
  25. PADDB/PADDW/PADDD/PADDQ — Add Packed Integers
    https://www.felixcloutier­.com/x86/paddb:paddw:paddd:pad­dq
  26. PSUBB/PSUBW/PSUBD — Subtract Packed Integers
    https://www.felixcloutier­.com/x86/psubb:psubw:psubd
  27. PMULLW — Multiply Packed Signed Integers and Store Low Result
    https://www.felixcloutier­.com/x86/pmullw
  28. PUNPCKLBW/PUNPCKLWD/PUNPCKLDQ/PUN­PCKLQDQ — Unpack Low Data
    https://www.felixcloutier­.com/x86/punpcklbw:punpcklwd:pun­pckldq:punpcklqdq
  29. PUNPCKHBW/PUNPCKHWD/PUNPCKHDQ/PUN­PCKHQDQ — Unpack High Data
    https://www.felixcloutier­.com/x86/punpckhbw:punpckhwd:pun­pckhdq:punpckhqdq
  30. PACKUSWB — Pack with Unsigned Saturation
    https://www.felixcloutier­.com/x86/packuswb
  31. ADDPS — Add Packed Single-Precision Floating-Point Values
    https://www.felixcloutier­.com/x86/addps
  32. SUBPS — Subtract Packed Single-Precision Floating-Point Values
    https://www.felixcloutier­.com/x86/subps
  33. MULPS — Multiply Packed Single-Precision Floating-Point Values
    https://www.felixcloutier­.com/x86/mulps
  34. DIVPS — Divide Packed Single-Precision Floating-Point Values
    https://www.felixcloutier­.com/x86/divps
  35. CBW/CWDE/CDQE — Convert Byte to Word/Convert Word to Doubleword/Convert Doubleword to Quadword
    https://www.felixcloutier­.com/x86/cbw:cwde:cdqe
  36. PAND — Logical AND
    https://www.felixcloutier­.com/x86/pand
  37. POR — Bitwise Logical OR
    https://www.felixcloutier.com/x86/por
  38. PXOR — Logical Exclusive OR
    https://www.felixcloutier­.com/x86/pxor
  39. Improve the Multimedia User Experience
    https://www.arm.com/technologies/neon
  40. NEON Technology (stránky ARM)
    https://developer.arm.com/techno­logies/neon
  41. SIMD Assembly Tutorial: ARM NEON – Xiph.org
    https://people.xiph.org/~tte­rribe/daala/neon_tutorial­.pdf
  42. Ne10
    http://projectne10.github.io/Ne10/
  43. NEON and Floating-Point architecture
    http://infocenter.arm.com/hel­p/index.jsp?topic=/com.ar­m.doc.den0024a/BABIGHEB.html
  44. An Introduction to ARM NEON
    http://peterdn.com/post/an-introduction-to-ARM-NEON.aspx
  45. ARM NEON Intrinsics Reference
    http://infocenter.arm.com/hel­p/topic/com.arm.doc.ihi0073a/I­HI0073A_arm_neon_intrinsic­s_ref.pdf
  46. Arm Neon Intrinsics vs hand assembly
    https://stackoverflow.com/qu­estions/9828567/arm-neon-intrinsics-vs-hand-assembly
  47. ARM NEON Optimization. An Example
    http://hilbert-space.de/?p=22
  48. AArch64 NEON instruction format
    https://developer.arm.com/doc­s/den0024/latest/7-aarch64-floating-point-and-neon/73-aarch64-neon-instruction-format
  49. ARM SIMD instructions
    https://developer.arm.com/do­cumentation/dht0002/a/Intro­ducing-NEON/What-is-SIMD-/ARM-SIMD-instructions
  50. Learn the architecture – Migrate Neon to SVE Version 1.0
    https://developer.arm.com/do­cumentation/102131/0100/?lan­g=en
  51. 1.2.2. Comparison between NEON technology and other SIMD solutions
    https://developer.arm.com/do­cumentation/den0018/a/Intro­duction/Comparison-between-ARM-NEON-technology-and-other-implementations/Comparison-between-NEON-technology-and-other-SIMD-solutions?lang=en
  52. NEON Programmer’s Guide
    https://documentation-service.arm.com/static/63299276e68c6809a6b4­1308
  53. 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/
  54. Other Built-in Functions Provided by GCC
    https://gcc.gnu.org/online­docs/gcc/Other-Builtins.html
  55. GCC: 6.60 Built-in Functions Specific to Particular Target Machines
    https://gcc.gnu.org/online­docs/gcc/Target-Builtins.html#Target-Builtins
  56. Advanced Vector Extensions
    https://en.wikipedia.org/wi­ki/Advanced_Vector_Extensi­ons
  57. AVX-512
    https://en.wikipedia.org/wiki/AVX-512
  58. AVX-512
    https://iq.opengenus.org/avx512/
  59. Downclocking pro AVX-512
    https://en.wikipedia.org/wi­ki/Advanced_Vector_Extensi­ons#Downclocking
  60. BLENDPS — Blend Packed Single Precision Floating-Point Values
    https://www.felixcloutier­.com/x86/blendps
  61. BLENDPD — Blend Packed Double Precision Floating-Point Values
    https://www.felixcloutier­.com/x86/blendpd

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.