Obsah
1. Podpora SIMD operací v GCC s využitím intrinsic
2. SIMD operace nad vektory s celočíselnými prvky různých typů
3. Způsob překladu do assembleru při povolení SIMD operací
4. Základní „vektorové“ operace pro vektory s prvky typu float a double
5. Překlad operace součtu a rozdílu s vektory s prvky typu float
6. Překlad operace součinu a podílu s vektory s prvky typu float
7. Protipříklad – překlad instrukcí pro součin a podíl prvku typu signed char
8. Překlad kódu s dlouhým vektorem s využitím SIMD instrukcí
9. Podpora SIMD operací NEON v programovacích jazycích
10. Na pomezí mezi C a assemblerem – intrinsic v GCC
11. Instrinsic pro SIMD operace na platformě x86–64
12. Intrinsic pro využití technologie MMX
13. Ukázka použití: součet vektorů s celočíselnými operandy
14. Způsob překladu obou demonstračních příkladů do assembleru
15. Součet s přetečením a součet se saturací
16. Způsob překladu obou demonstračních příkladů do assembleru
17. Převody mezi různými typy vektorů, kombinace dvou vektorů (pack, unpack)
18. Způsob překladu demonstračního příkladu do assembleru
19. Repositář s demonstračními příklady
1. Podpora SIMD operací v GCC s využitím intrinsic
V pořadí již čtvrtý článek o podpoře SIMD (tedy „vektorových“) operací v překladači GCC C je rozdělen, jak již ostatně bylo napsáno v perexu, na dvě části. V části první dokončíme popis problematiky SIMD technologie NEON na architektuře ARM. Zaměříme se na ukázku podobných operací, s nimiž jsme se seznámili na konkurenční architektuře x86–64. Část druhá bude věnována takzvaným intrinsic (někdy též nazývaným built-ins), které programátorům umožňují přímo v programovacím jazyku C provádět i nízkoúrovňové optimalizace, a to bez nutnosti použití assembleru, který již vyžaduje mnohdy zbytečné přemýšlení o alokaci registrů atd. Použití intrinsic s sebou přináší možnost velmi přesné optimalizace, ovšem na druhou stranu nebude výsledný program přenositelný na další platformy či procesory bez podpory konkrétní SIMD technologie (to je problematika, kterou je nutno řešit separátně).
2. SIMD operace nad vektory s celočíselnými prvky různých typů
processor : 0 BogoMIPS : 400.00 Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics cpuid asimdrdm CPU implementer : 0x43 CPU architecture: 8 CPU variant : 0x1 CPU part : 0x0af CPU revision : 1 processor : 1 BogoMIPS : 400.00 Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics cpuid asimdrdm CPU implementer : 0x43 CPU architecture: 8 CPU variant : 0x1 CPU part : 0x0af CPU revision : 1 processor : 2 BogoMIPS : 400.00 Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics cpuid asimdrdm CPU implementer : 0x43 CPU architecture: 8 CPU variant : 0x1 CPU part : 0x0af CPU revision : 1 processor : 3 BogoMIPS : 400.00 Features : fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics cpuid asimdrdm CPU implementer : 0x43 CPU architecture: 8 CPU variant : 0x1 CPU part : 0x0af CPU revision : 1
Nejprve se podívejme, jakým způsobem se provádí aritmetické operace (zde konkrétně operace součtu) nad vektory s prvky celočíselných typů různé bitové délky. Použijeme přitom čtyři typy vektorů, vždy o velikosti šestnácti bajtů, pokaždé ovšem s jinými typy prvků (char, short int atd.):
#include <stdio.h> typedef signed char v16ub __attribute__((vector_size(16))); typedef signed short int v16us __attribute__((vector_size(16))); typedef signed int v16ui __attribute__((vector_size(16))); typedef signed long int v16ul __attribute__((vector_size(16))); int main(void) { { v16ub x = { 1, 2, 3, 4, 5, 6, 7, 8 }; v16ub y = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; v16ub z = x + y; } { v16us x = { 1, 2, 3, 4, 5, 6, 7, 8 }; v16us y = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; v16us z = x + y; } { v16ui x = { 1, 2, 3, 4 }; v16ui y = { 0xff, 0xff, 0xff, 0xff }; v16ui z = x + y; } { v16ul x = { 1, 2 }; v16ul y = { 0xff, 0xff }; v16ul z = x + y; } return 0; }
3. Způsob překladu do assembleru při povolení SIMD operací
Zajímavé bude zjistit, jakým způsobem se předchozí program přeložil do assembleru mikroprocesorů s architekturou AArch64 v případě, že jsou povoleny instrukce typu SIMD (což na této architektuře prakticky vždy jsou).
Nejprve se podívejme na součet vektorů s osmi prvky typu char. Pro tento účel se používá instrukce add s „vektorovými“ registry Vx, u nichž se v postfixu uvádí typ prvků (tedy to, jakým způsobem je registr rozdělen na jednotlivé prvky):
{ v16ub x = { 1, 2, 3, 4, 5, 6, 7, 8 }; 4: 90000000 adrp x0, 0 8: 3dc00000 ldr q0, [x0] c: 3d802fe0 str q0, [sp, #176] v16ub y = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 10: 90000000 adrp x0, 0 14: 3dc00000 ldr q0, [x0] 18: 3d802be0 str q0, [sp, #160] v16ub z = x + y; 1c: 3dc02fe1 ldr q1, [sp, #176] 20: 3dc02be0 ldr q0, [sp, #160] 24: 4e208420 add v0.16b, v1.16b, v0.16b 28: 3d8027e0 str q0, [sp, #144] }
Podobným způsobem se pracuje s vektory s prvky typu short, což odpovídá postfixu h (half word):
{ v16us x = { 1, 2, 3, 4, 5, 6, 7, 8 }; 2c: 90000000 adrp x0, 0 30: 3dc00000 ldr q0, [x0] 34: 3d8023e0 str q0, [sp, #128] v16us y = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 38: 4f0787e0 movi v0.8h, #0xff 3c: 3d801fe0 str q0, [sp, #112] v16us z = x + y; 40: 3dc023e1 ldr q1, [sp, #128] 44: 3dc01fe0 ldr q0, [sp, #112] 48: 4e608420 add v0.8h, v1.8h, v0.8h 4c: 3d801be0 str q0, [sp, #96] }
Součet celočíselných prvků typu int je jednoduché:
{ v16ui x = { 1, 2, 3, 4 }; 50: 90000000 adrp x0, 0 54: 3dc00000 ldr q0, [x0] 58: 3d8017e0 str q0, [sp, #80] v16ui y = { 0xff, 0xff, 0xff, 0xff }; 5c: 4f0707e0 movi v0.4s, #0xff 60: 3d8013e0 str q0, [sp, #64] v16ui z = x + y; 64: 3dc017e1 ldr q1, [sp, #80] 68: 3dc013e0 ldr q0, [sp, #64] 6c: 4ea08420 add v0.4s, v1.4s, v0.4s 70: 3d800fe0 str q0, [sp, #48] }
A konečně si ukážeme součet dvou vektorů, z nichž každý obsahuje dva prvky typu long:
{ v16ul x = { 1, 2 }; 74: 90000000 adrp x0, 0 78: 3dc00000 ldr q0, [x0] 7c: 3d800be0 str q0, [sp, #32] v16ul y = { 0xff, 0xff }; 80: 6f00e420 movi v0.2d, #0xff 84: 3d8007e0 str q0, [sp, #16] v16ul z = x + y; 88: 3dc00be1 ldr q1, [sp, #32] 8c: 3dc007e0 ldr q0, [sp, #16] 90: 4ee08420 add v0.2d, v1.2d, v0.2d 94: 3d8003e0 str q0, [sp] }
24: 4e208420 add v0.16b, v1.16b, v0.16b 48: 4e608420 add v0.8h, v1.8h, v0.8h 6c: 4ea08420 add v0.4s, v1.4s, v0.4s 90: 4ee08420 add v0.2d, v1.2d, v0.2d
4. Základní „vektorové“ operace pro vektory s prvky typu float a double
Nyní se podívejme na způsob překladu aritmetických operací ve chvíli, kdy se sčítají vektory s prvky typu float a double. Opět použijeme zdrojový kód příkladu, který jsme již použili na architektuře x86–64:
#include <stdio.h> typedef float v16float __attribute__((vector_size(16))); void add16float(v16float x, v16float y, v16float * z) { *z = x + y; } void sub16float(v16float x, v16float y, v16float * z) { *z = x - y; } void mul16float(v16float x, v16float y, v16float * z) { *z = x * y; } void div16float(v16float x, v16float y, v16float * z) { *z = x / y; } void print_vectors(const char *message, const char op, v16float * x, v16float * y, v16float * z) { int i; puts(message); for (i = 0; i < sizeof(v16float) / sizeof(float); i++) { printf("%2d %5.3f %c %5.3f = %5.3f\n", i, (*x)[i], op, (*y)[i], (*z)[i]); } putchar('\n'); } int main(void) { v16float x; v16float y; v16float z; int i; for (i = 0; i < sizeof(v16float) / sizeof(float); i++) { x[i] = i; y[i] = i + 0.1; } add16float(x, y, &z); print_vectors("vector addition", '+', &x, &y, &z); sub16float(x, y, &z); print_vectors("vector subtraction", '-', &x, &y, &z); mul16float(x, y, &z); print_vectors("vector multiply", '*', &x, &y, &z); div16float(x, y, &z); print_vectors("vector divide", '/', &x, &y, &z); return 0; }
Výsledek získaný po spuštění tohoto příkladu by měl vypadat následovně:
vector addition 0 0.000 + 0.100 = 0.100 1 1.000 + 1.100 = 2.100 2 2.000 + 2.100 = 4.100 3 3.000 + 3.100 = 6.100 vector subtraction 0 0.000 - 0.100 = -0.100 1 1.000 - 1.100 = -0.100 2 2.000 - 2.100 = -0.100 3 3.000 - 3.100 = -0.100 vector multiply 0 0.000 * 0.100 = 0.000 1 1.000 * 1.100 = 1.100 2 2.000 * 2.100 = 4.200 3 3.000 * 3.100 = 9.300 vector divide 0 0.000 / 0.100 = 0.000 1 1.000 / 1.100 = 0.909 2 2.000 / 2.100 = 0.952 3 3.000 / 3.100 = 0.968
5. Překlad operace součtu a rozdílu s vektory s prvky typu float
Pro vektorové operace součtu a rozdílu pro vektory s prvky typu float existují v technologii NEON dedikované instrukce nazvané fadd a fsub, samozřejmě za předpokladu, že jsou použity společně s operandy typu „vektorový registr“. Nejprve se podívejme na způsob překladu součtu realizovaný nad registry V0 a V1, které jsou jmennými aliasy pro pracovní registry Q0 a Q1:
void add16float(v16float x, v16float y, v16float * z) { 0: d100c3ff sub sp, sp, #0x30 4: 3d800be0 str q0, [sp, #32] 8: 3d8007e1 str q1, [sp, #16] c: f90007e0 str x0, [sp, #8] *z = x + y; 10: 3dc00be1 ldr q1, [sp, #32] 14: 3dc007e0 ldr q0, [sp, #16] 18: 4e20d420 fadd v0.4s, v1.4s, v0.4s 1c: f94007e0 ldr x0, [sp, #8] 20: 3d800000 str q0, [x0] } 24: d503201f nop 28: 9100c3ff add sp, sp, #0x30 2c: d65f03c0 ret
Prakticky stejným způsobem je realizována operace rozdílu:
void sub16float(v16float x, v16float y, v16float * z) { 30: d100c3ff sub sp, sp, #0x30 34: 3d800be0 str q0, [sp, #32] 38: 3d8007e1 str q1, [sp, #16] 3c: f90007e0 str x0, [sp, #8] *z = x - y; 40: 3dc00be1 ldr q1, [sp, #32] 44: 3dc007e0 ldr q0, [sp, #16] 48: 4ea0d420 fsub v0.4s, v1.4s, v0.4s 4c: f94007e0 ldr x0, [sp, #8] 50: 3d800000 str q0, [x0] } 54: d503201f nop 58: 9100c3ff add sp, sp, #0x30 5c: d65f03c0 ret
6. Překlad operace součinu a podílu s vektory s prvky typu float
I pro součin vektorů prvek po prvku existují v instrukční sadě NEON specializované SIMD instrukce. Konkrétně pro prvky typu float se jedná o instrukce fmul a fdiv; opět za předpokladu, že jsou použity s vektorovými a nikoli se skalárními registry. Na rozdíl on MMX/SSE se zde tedy žádné speciální skalární operace nemusí provádět:
void mul16float(v16float x, v16float y, v16float * z) { 60: d100c3ff sub sp, sp, #0x30 64: 3d800be0 str q0, [sp, #32] 68: 3d8007e1 str q1, [sp, #16] 6c: f90007e0 str x0, [sp, #8] *z = x * y; 70: 3dc00be1 ldr q1, [sp, #32] 74: 3dc007e0 ldr q0, [sp, #16] 78: 6e20dc20 fmul v0.4s, v1.4s, v0.4s 7c: f94007e0 ldr x0, [sp, #8] 80: 3d800000 str q0, [x0] } 84: d503201f nop 88: 9100c3ff add sp, sp, #0x30 8c: d65f03c0 ret
Operace dělení:
void div16float(v16float x, v16float y, v16float * z) { 90: d100c3ff sub sp, sp, #0x30 94: 3d800be0 str q0, [sp, #32] 98: 3d8007e1 str q1, [sp, #16] 9c: f90007e0 str x0, [sp, #8] *z = x / y; a0: 3dc00be1 ldr q1, [sp, #32] a4: 3dc007e0 ldr q0, [sp, #16] a8: 6e20fc20 fdiv v0.4s, v1.4s, v0.4s ac: f94007e0 ldr x0, [sp, #8] b0: 3d800000 str q0, [x0] } b4: d503201f nop b8: 9100c3ff add sp, sp, #0x30 bc: d65f03c0 ret
7. Protipříklad – překlad instrukcí pro součin a podíl prvku typu signed char
Mohlo by se zdát, že NEON dokáže „vektorizovat“ všechny základní aritmetické, popř. logické operace, ovšem u celočíselných operandů tomu tak být nemusí. Příkladem může být rozdíl mezi součinem prvků vektorů typu signed char v porovnání s jejich podílem.
Součin je přímočarý:
void mul16ib(v16ib x, v16ib y, v16ib * z) { 60: d100c3ff sub sp, sp, #0x30 64: 3d800be0 str q0, [sp, #32] 68: 3d8007e1 str q1, [sp, #16] 6c: f90007e0 str x0, [sp, #8] *z = x * y; 70: 3dc00be1 ldr q1, [sp, #32] 74: 3dc007e0 ldr q0, [sp, #16] 78: 4e209c20 mul v0.16b, v1.16b, v0.16b 7c: f94007e0 ldr x0, [sp, #8] 80: 3d800000 str q0, [x0] } 84: d503201f nop 88: 9100c3ff add sp, sp, #0x30 8c: d65f03c0 ret
Naproti tomu podíl je implementován odlišně, a to konkrétně sekvencí skalární operace podílu (k vektorizaci zde tedy vůbec nedochází):
*z = x / y; a8: 1ac00c20 sdiv w0, w1, w0 ac: 13001c10 sxtb w16, w0 b0: 39c087e1 ldrsb w1, [sp, #33] b4: 39c047e0 ldrsb w0, [sp, #17] b8: 1ac00c20 sdiv w0, w1, w0 bc: 13001c0f sxtb w15, w0 c0: 39c08be1 ldrsb w1, [sp, #34] c4: 39c04be0 ldrsb w0, [sp, #18] c8: 1ac00c20 sdiv w0, w1, w0 cc: 13001c0e sxtb w14, w0 d0: 39c08fe1 ldrsb w1, [sp, #35] d4: 39c04fe0 ldrsb w0, [sp, #19] d8: 1ac00c20 sdiv w0, w1, w0 dc: 13001c0d sxtb w13, w0 e0: 39c093e1 ldrsb w1, [sp, #36] e4: 39c053e0 ldrsb w0, [sp, #20] e8: 1ac00c20 sdiv w0, w1, w0 ec: 13001c0c sxtb w12, w0 f0: 39c097e1 ldrsb w1, [sp, #37] f4: 39c057e0 ldrsb w0, [sp, #21] f8: 1ac00c20 sdiv w0, w1, w0 fc: 13001c0b sxtb w11, w0 100: 39c09be1 ldrsb w1, [sp, #38] ... ... ... 178: 1ac00c20 sdiv w0, w1, w0 17c: 13001c03 sxtb w3, w0 180: 39c0bbe1 ldrsb w1, [sp, #46] 184: 39c07be0 ldrsb w0, [sp, #30] 188: 1ac00c20 sdiv w0, w1, w0 18c: 13001c02 sxtb w2, w0 190: 39c0bfe1 ldrsb w1, [sp, #47] 194: 39c07fe0 ldrsb w0, [sp, #31] 198: 1ac00c20 sdiv w0, w1, w0
8. Překlad kódu s dlouhým vektorem s využitím SIMD instrukcí
Překladač GCC C i na platformě ARM generuje obrovské množství instrukcí ve chvíli, kdy se pokusíme vektory použít ve funkci pole. Z jednoho pohledu může kód vypadat rychle („rozbalí všechny smyčky a to je dobře, ne?“), ovšem zaplatíme za to častějšími výpadky cache, které se ještě více projeví na vícejádrových systémech:
typedef float v1024f __attribute__((vector_size(1024))); void addVectors(v1024f * x, v1024f * y, v1024f * z) { *z = *x + *y; } int main(void) { v1024f x = { 1.0 }; v1024f y = { 1.0 }; v1024f z; addVectors(&x, &y, &z); return 0; }
Výsledek překladu je sáhodlouhý:
simd16_2.o: file format elf64-littleaarch64 Disassembly of section .text: 0000000000000000 : typedef float v1024f __attribute__((vector_size(1024))); void addVectors(v1024f * x, v1024f * y, v1024f * z) { 0: d282500c mov x12, #0x1280 // #4736 4: cb2c63ff sub sp, sp, x12 8: a9007bfd stp x29, x30, [sp] c: 910003fd mov x29, sp 10: 6d0127e8 stp d8, d9, [sp, #16] 14: 6d022fea stp d10, d11, [sp, #32] 18: 6d0337ec stp d12, d13, [sp, #48] 1c: 6d043fee stp d14, d15, [sp, #64] 20: f9033fe0 str x0, [sp, #1656] 24: f9033be1 str x1, [sp, #1648] 28: f90337e2 str x2, [sp, #1640] *z = *x + *y; 2c: f9433fe1 ldr x1, [sp, #1656] 30: 913a03e0 add x0, sp, #0xe80 34: aa0103e3 mov x3, x1 38: d2808001 mov x1, #0x400 // #1024 3c: aa0103e2 mov x2, x1 40: aa0303e1 mov x1, x3 44: 94000000 bl 0 <memcpy> 48: f9433be1 ldr x1, [sp, #1648] 4c: 912a03e0 add x0, sp, #0xa80 50: aa0103e3 mov x3, x1 54: d2808001 mov x1, #0x400 // #1024 58: aa0103e2 mov x2, x1 5c: aa0303e1 mov x1, x3 60: 94000000 bl 0 <memcpy> 64: 3dc3a3e1 ldr q1, [sp, #3712] 68: 3dc2a3e0 ldr q0, [sp, #2688] 6c: 4e20d437 fadd v23.4s, v1.4s, v0.4s 70: 3dc3a7e1 ldr q1, [sp, #3728] 74: 3dc2a7e0 ldr q0, [sp, #2704] 78: 4e20d439 fadd v25.4s, v1.4s, v0.4s 7c: 3dc3abe1 ldr q1, [sp, #3744] 80: 3dc2abe0 ldr q0, [sp, #2720] 84: 4e20d43a fadd v26.4s, v1.4s, v0.4s 88: 3dc3afe1 ldr q1, [sp, #3760] 8c: 3dc2afe0 ldr q0, [sp, #2736] 90: 4e20d43b fadd v27.4s, v1.4s, v0.4s 94: 3dc3b3e1 ldr q1, [sp, #3776] 98: 3dc2b3e0 ldr q0, [sp, #2752] 9c: 4e20d43d fadd v29.4s, v1.4s, v0.4s a0: 3dc3b7e1 ldr q1, [sp, #3792] a4: 3dc2b7e0 ldr q0, [sp, #2768] a8: 4e20d43e fadd v30.4s, v1.4s, v0.4s ac: 3dc3bbe1 ldr q1, [sp, #3808] b0: 3dc2bbe0 ldr q0, [sp, #2784] b4: 4e20d43f fadd v31.4s, v1.4s, v0.4s b8: 3dc3bfe1 ldr q1, [sp, #3824] bc: 3dc2bfe0 ldr q0, [sp, #2800] c0: 4e20d420 fadd v0.4s, v1.4s, v0.4s c4: 3d8017e0 str q0, [sp, #80] c8: 3dc3c3e1 ldr q1, [sp, #3840] cc: 3dc2c3e0 ldr q0, [sp, #2816] d0: 4e20d422 fadd v2.4s, v1.4s, v0.4s d4: 3d801be2 str q2, [sp, #96] d8: 3dc3c7e1 ldr q1, [sp, #3856] dc: 3dc2c7e0 ldr q0, [sp, #2832] e0: 4e20d423 fadd v3.4s, v1.4s, v0.4s e4: 3d801fe3 str q3, [sp, #112] e8: 3dc3cbe1 ldr q1, [sp, #3872] ec: 3dc2cbe0 ldr q0, [sp, #2848] f0: 4e20d424 fadd v4.4s, v1.4s, v0.4s f4: 3d8023e4 str q4, [sp, #128] f8: 3dc3cfe1 ldr q1, [sp, #3888] fc: 3dc2cfe0 ldr q0, [sp, #2864] 100: 4e20d425 fadd v5.4s, v1.4s, v0.4s 104: 3d8027e5 str q5, [sp, #144] 108: 3dc3d3e1 ldr q1, [sp, #3904] 10c: 3dc2d3e0 ldr q0, [sp, #2880] 110: 4e20d426 fadd v6.4s, v1.4s, v0.4s 114: 3d802be6 str q6, [sp, #160] 118: 3dc3d7e1 ldr q1, [sp, #3920] 11c: 3dc2d7e0 ldr q0, [sp, #2896] 120: 4e20d427 fadd v7.4s, v1.4s, v0.4s 124: 3d802fe7 str q7, [sp, #176] 128: 3dc3dbe1 ldr q1, [sp, #3936] 12c: 3dc2dbe0 ldr q0, [sp, #2912] 130: 4e20d428 fadd v8.4s, v1.4s, v0.4s 134: 3d8033e8 str q8, [sp, #192] 138: 3dc3dfe1 ldr q1, [sp, #3952] 13c: 3dc2dfe0 ldr q0, [sp, #2928] 140: 4e20d429 fadd v9.4s, v1.4s, v0.4s 144: 3d8037e9 str q9, [sp, #208] 148: 3dc3e3e1 ldr q1, [sp, #3968] 14c: 3dc2e3e0 ldr q0, [sp, #2944] 150: 4e20d42a fadd v10.4s, v1.4s, v0.4s 154: 3d803bea str q10, [sp, #224] 158: 3dc3e7e1 ldr q1, [sp, #3984] 15c: 3dc2e7e0 ldr q0, [sp, #2960] 160: 4e20d42b fadd v11.4s, v1.4s, v0.4s 164: 3d803feb str q11, [sp, #240] 168: 3dc3ebe1 ldr q1, [sp, #4000] 16c: 3dc2ebe0 ldr q0, [sp, #2976] 170: 4e20d42c fadd v12.4s, v1.4s, v0.4s 174: 3d8043ec str q12, [sp, #256] 178: 3dc3efe1 ldr q1, [sp, #4016] 17c: 3dc2efe0 ldr q0, [sp, #2992] 180: 4e20d42d fadd v13.4s, v1.4s, v0.4s 184: 3d8047ed str q13, [sp, #272] 188: 3dc3f3e1 ldr q1, [sp, #4032] 18c: 3dc2f3e0 ldr q0, [sp, #3008] 190: 4e20d42e fadd v14.4s, v1.4s, v0.4s 194: 3d804bee str q14, [sp, #288] 198: 3dc3f7e1 ldr q1, [sp, #4048] 19c: 3dc2f7e0 ldr q0, [sp, #3024] 1a0: 4e20d42f fadd v15.4s, v1.4s, v0.4s 1a4: 3d804fef str q15, [sp, #304] 1a8: 3dc3fbe1 ldr q1, [sp, #4064] 1ac: 3dc2fbe0 ldr q0, [sp, #3040] 1b0: 4e20d430 fadd v16.4s, v1.4s, v0.4s 1b4: 3d8053f0 str q16, [sp, #320] 1b8: 3dc3ffe1 ldr q1, [sp, #4080] 1bc: 3dc2ffe0 ldr q0, [sp, #3056] 1c0: 4e20d431 fadd v17.4s, v1.4s, v0.4s 1c4: 3d8057f1 str q17, [sp, #336] 1c8: 3dc403e1 ldr q1, [sp, #4096] 1cc: 3dc303e0 ldr q0, [sp, #3072] 1d0: 4e20d432 fadd v18.4s, v1.4s, v0.4s 1d4: 3d805bf2 str q18, [sp, #352] 1d8: 3dc407e1 ldr q1, [sp, #4112] 1dc: 3dc307e0 ldr q0, [sp, #3088] 1e0: 4e20d433 fadd v19.4s, v1.4s, v0.4s 1e4: 3d805ff3 str q19, [sp, #368] 1e8: 3dc40be1 ldr q1, [sp, #4128] 1ec: 3dc30be0 ldr q0, [sp, #3104] 1f0: 4e20d434 fadd v20.4s, v1.4s, v0.4s 1f4: 3d8063f4 str q20, [sp, #384] 1f8: 3dc40fe1 ldr q1, [sp, #4144] 1fc: 3dc30fe0 ldr q0, [sp, #3120] 200: 4e20d435 fadd v21.4s, v1.4s, v0.4s 204: 3d8067f5 str q21, [sp, #400] 208: 3dc413e1 ldr q1, [sp, #4160] 20c: 3dc313e0 ldr q0, [sp, #3136] 210: 4e20d436 fadd v22.4s, v1.4s, v0.4s 214: 3d806bf6 str q22, [sp, #416] 218: 3dc417e1 ldr q1, [sp, #4176] 21c: 3dc317e0 ldr q0, [sp, #3152] 220: 4e20d438 fadd v24.4s, v1.4s, v0.4s 224: 3d806ff8 str q24, [sp, #432] 228: 3dc41be1 ldr q1, [sp, #4192] 22c: 3dc31be0 ldr q0, [sp, #3168] 230: 4e20d43c fadd v28.4s, v1.4s, v0.4s 234: 3d8073fc str q28, [sp, #448] 238: 3dc41fe1 ldr q1, [sp, #4208] 23c: 3dc31fe0 ldr q0, [sp, #3184] 240: 4e20d420 fadd v0.4s, v1.4s, v0.4s 244: 3d8077e0 str q0, [sp, #464] 248: 3dc423e1 ldr q1, [sp, #4224] 24c: 3dc323e0 ldr q0, [sp, #3200] 250: 4e20d422 fadd v2.4s, v1.4s, v0.4s 254: 3d807be2 str q2, [sp, #480] 258: 3dc427e1 ldr q1, [sp, #4240] 25c: 3dc327e0 ldr q0, [sp, #3216] 260: 4e20d423 fadd v3.4s, v1.4s, v0.4s 264: 3d807fe3 str q3, [sp, #496] 268: 3dc42be1 ldr q1, [sp, #4256] 26c: 3dc32be0 ldr q0, [sp, #3232] 270: 4e20d424 fadd v4.4s, v1.4s, v0.4s 274: 3d8083e4 str q4, [sp, #512] 278: 3dc42fe1 ldr q1, [sp, #4272] 27c: 3dc32fe0 ldr q0, [sp, #3248] 280: 4e20d425 fadd v5.4s, v1.4s, v0.4s 284: 3d8087e5 str q5, [sp, #528] 288: 3dc433e1 ldr q1, [sp, #4288] 28c: 3dc333e0 ldr q0, [sp, #3264] 290: 4e20d426 fadd v6.4s, v1.4s, v0.4s 294: 3d808be6 str q6, [sp, #544] 298: 3dc437e1 ldr q1, [sp, #4304] 29c: 3dc337e0 ldr q0, [sp, #3280] 2a0: 4e20d427 fadd v7.4s, v1.4s, v0.4s 2a4: 3d808fe7 str q7, [sp, #560] 2a8: 3dc43be1 ldr q1, [sp, #4320] 2ac: 3dc33be0 ldr q0, [sp, #3296] 2b0: 4e20d428 fadd v8.4s, v1.4s, v0.4s 2b4: 3d8093e8 str q8, [sp, #576] 2b8: 3dc43fe1 ldr q1, [sp, #4336] 2bc: 3dc33fe0 ldr q0, [sp, #3312] 2c0: 4e20d429 fadd v9.4s, v1.4s, v0.4s 2c4: 3d8097e9 str q9, [sp, #592] 2c8: 3dc443e1 ldr q1, [sp, #4352] 2cc: 3dc343e0 ldr q0, [sp, #3328] 2d0: 4e20d43c fadd v28.4s, v1.4s, v0.4s 2d4: 3dc447e1 ldr q1, [sp, #4368] 2d8: 3dc347e0 ldr q0, [sp, #3344] 2dc: 4e20d438 fadd v24.4s, v1.4s, v0.4s 2e0: 3dc44be1 ldr q1, [sp, #4384] 2e4: 3dc34be0 ldr q0, [sp, #3360] 2e8: 4e20d436 fadd v22.4s, v1.4s, v0.4s 2ec: 3dc44fe1 ldr q1, [sp, #4400] 2f0: 3dc34fe0 ldr q0, [sp, #3376] 2f4: 4e20d435 fadd v21.4s, v1.4s, v0.4s 2f8: 3dc453e1 ldr q1, [sp, #4416] 2fc: 3dc353e0 ldr q0, [sp, #3392] 300: 4e20d434 fadd v20.4s, v1.4s, v0.4s 304: 3dc457e1 ldr q1, [sp, #4432] 308: 3dc357e0 ldr q0, [sp, #3408] 30c: 4e20d433 fadd v19.4s, v1.4s, v0.4s 310: 3dc45be1 ldr q1, [sp, #4448] 314: 3dc35be0 ldr q0, [sp, #3424] 318: 4e20d432 fadd v18.4s, v1.4s, v0.4s 31c: 3dc45fe1 ldr q1, [sp, #4464] 320: 3dc35fe0 ldr q0, [sp, #3440] 324: 4e20d431 fadd v17.4s, v1.4s, v0.4s 328: 3dc463e1 ldr q1, [sp, #4480] 32c: 3dc363e0 ldr q0, [sp, #3456] 330: 4e20d430 fadd v16.4s, v1.4s, v0.4s 334: 3dc467e1 ldr q1, [sp, #4496] 338: 3dc367e0 ldr q0, [sp, #3472] 33c: 4e20d42f fadd v15.4s, v1.4s, v0.4s 340: 3dc46be1 ldr q1, [sp, #4512] 344: 3dc36be0 ldr q0, [sp, #3488] 348: 4e20d42e fadd v14.4s, v1.4s, v0.4s 34c: 3dc46fe1 ldr q1, [sp, #4528] 350: 3dc36fe0 ldr q0, [sp, #3504] 354: 4e20d42d fadd v13.4s, v1.4s, v0.4s 358: 3dc473e1 ldr q1, [sp, #4544] 35c: 3dc373e0 ldr q0, [sp, #3520] 360: 4e20d42c fadd v12.4s, v1.4s, v0.4s 364: 3dc477e1 ldr q1, [sp, #4560] 368: 3dc377e0 ldr q0, [sp, #3536] 36c: 4e20d42b fadd v11.4s, v1.4s, v0.4s 370: 3dc47be1 ldr q1, [sp, #4576] 374: 3dc37be0 ldr q0, [sp, #3552] 378: 4e20d42a fadd v10.4s, v1.4s, v0.4s 37c: 3dc47fe1 ldr q1, [sp, #4592] 380: 3dc37fe0 ldr q0, [sp, #3568] 384: 4e20d429 fadd v9.4s, v1.4s, v0.4s 388: 3dc483e1 ldr q1, [sp, #4608] 38c: 3dc383e0 ldr q0, [sp, #3584] 390: 4e20d428 fadd v8.4s, v1.4s, v0.4s 394: 3dc487e1 ldr q1, [sp, #4624] 398: 3dc387e0 ldr q0, [sp, #3600] 39c: 4e20d427 fadd v7.4s, v1.4s, v0.4s 3a0: 3dc48be1 ldr q1, [sp, #4640] 3a4: 3dc38be0 ldr q0, [sp, #3616] 3a8: 4e20d426 fadd v6.4s, v1.4s, v0.4s 3ac: 3dc48fe1 ldr q1, [sp, #4656] 3b0: 3dc38fe0 ldr q0, [sp, #3632] 3b4: 4e20d425 fadd v5.4s, v1.4s, v0.4s 3b8: 3dc493e1 ldr q1, [sp, #4672] 3bc: 3dc393e0 ldr q0, [sp, #3648] 3c0: 4e20d424 fadd v4.4s, v1.4s, v0.4s 3c4: 3dc497e1 ldr q1, [sp, #4688] 3c8: 3dc397e0 ldr q0, [sp, #3664] 3cc: 4e20d423 fadd v3.4s, v1.4s, v0.4s 3d0: 3dc49be1 ldr q1, [sp, #4704] 3d4: 3dc39be0 ldr q0, [sp, #3680] 3d8: 4e20d422 fadd v2.4s, v1.4s, v0.4s 3dc: 3dc49fe1 ldr q1, [sp, #4720] 3e0: 3dc39fe0 ldr q0, [sp, #3696] 3e4: 4e20d420 fadd v0.4s, v1.4s, v0.4s 3e8: 3d809bf7 str q23, [sp, #608] 3ec: 3d809ff9 str q25, [sp, #624] 3f0: 3d80a3fa str q26, [sp, #640] 3f4: 3d80a7fb str q27, [sp, #656] 3f8: 3d80abfd str q29, [sp, #672] 3fc: 3d80affe str q30, [sp, #688] 400: 3d80b3ff str q31, [sp, #704] 404: 3dc017e1 ldr q1, [sp, #80] 408: 3d80b7e1 str q1, [sp, #720] 40c: 3dc01be1 ldr q1, [sp, #96] 410: 3d80bbe1 str q1, [sp, #736] 414: 3dc01fe1 ldr q1, [sp, #112] 418: 3d80bfe1 str q1, [sp, #752] 41c: 3dc023e1 ldr q1, [sp, #128] 420: 3d80c3e1 str q1, [sp, #768] 424: 3dc027e1 ldr q1, [sp, #144] 428: 3d80c7e1 str q1, [sp, #784] 42c: 3dc02be1 ldr q1, [sp, #160] 430: 3d80cbe1 str q1, [sp, #800] 434: 3dc02fe1 ldr q1, [sp, #176] 438: 3d80cfe1 str q1, [sp, #816] 43c: 3dc033e1 ldr q1, [sp, #192] 440: 3d80d3e1 str q1, [sp, #832] 444: 3dc037e1 ldr q1, [sp, #208] 448: 3d80d7e1 str q1, [sp, #848] 44c: 3dc03be1 ldr q1, [sp, #224] 450: 3d80dbe1 str q1, [sp, #864] 454: 3dc03fe1 ldr q1, [sp, #240] 458: 3d80dfe1 str q1, [sp, #880] 45c: 3dc043e1 ldr q1, [sp, #256] 460: 3d80e3e1 str q1, [sp, #896] 464: 3dc047e1 ldr q1, [sp, #272] 468: 3d80e7e1 str q1, [sp, #912] 46c: 3dc04be1 ldr q1, [sp, #288] 470: 3d80ebe1 str q1, [sp, #928] 474: 3dc04fe1 ldr q1, [sp, #304] 478: 3d80efe1 str q1, [sp, #944] 47c: 3dc053e1 ldr q1, [sp, #320] 480: 3d80f3e1 str q1, [sp, #960] 484: 3dc057e1 ldr q1, [sp, #336] 488: 3d80f7e1 str q1, [sp, #976] 48c: 3dc05be1 ldr q1, [sp, #352] 490: 3d80fbe1 str q1, [sp, #992] 494: 3dc05fe1 ldr q1, [sp, #368] 498: 3d80ffe1 str q1, [sp, #1008] 49c: 3dc063e1 ldr q1, [sp, #384] 4a0: 3d8103e1 str q1, [sp, #1024] 4a4: 3dc067e1 ldr q1, [sp, #400] 4a8: 3d8107e1 str q1, [sp, #1040] 4ac: 3dc06be1 ldr q1, [sp, #416] 4b0: 3d810be1 str q1, [sp, #1056] 4b4: 3dc06fe1 ldr q1, [sp, #432] 4b8: 3d810fe1 str q1, [sp, #1072] 4bc: 3dc073e1 ldr q1, [sp, #448] 4c0: 3d8113e1 str q1, [sp, #1088] 4c4: 3dc077e1 ldr q1, [sp, #464] 4c8: 3d8117e1 str q1, [sp, #1104] 4cc: 3dc07be1 ldr q1, [sp, #480] 4d0: 3d811be1 str q1, [sp, #1120] 4d4: 3dc07fe1 ldr q1, [sp, #496] 4d8: 3d811fe1 str q1, [sp, #1136] 4dc: 3dc083e1 ldr q1, [sp, #512] 4e0: 3d8123e1 str q1, [sp, #1152] 4e4: 3dc087e1 ldr q1, [sp, #528] 4e8: 3d8127e1 str q1, [sp, #1168] 4ec: 3dc08be1 ldr q1, [sp, #544] 4f0: 3d812be1 str q1, [sp, #1184] 4f4: 3dc08fe1 ldr q1, [sp, #560] 4f8: 3d812fe1 str q1, [sp, #1200] 4fc: 3dc093e1 ldr q1, [sp, #576] 500: 3d8133e1 str q1, [sp, #1216] 504: 3dc097e1 ldr q1, [sp, #592] 508: 3d8137e1 str q1, [sp, #1232] 50c: 3d813bfc str q28, [sp, #1248] 510: 3d813ff8 str q24, [sp, #1264] 514: 3d8143f6 str q22, [sp, #1280] 518: 3d8147f5 str q21, [sp, #1296] 51c: 3d814bf4 str q20, [sp, #1312] 520: 3d814ff3 str q19, [sp, #1328] 524: 3d8153f2 str q18, [sp, #1344] 528: 3d8157f1 str q17, [sp, #1360] 52c: 3d815bf0 str q16, [sp, #1376] 530: 3d815fef str q15, [sp, #1392] 534: 3d8163ee str q14, [sp, #1408] 538: 3d8167ed str q13, [sp, #1424] 53c: 3d816bec str q12, [sp, #1440] 540: 3d816feb str q11, [sp, #1456] 544: 3d8173ea str q10, [sp, #1472] 548: 3d8177e9 str q9, [sp, #1488] 54c: 3d817be8 str q8, [sp, #1504] 550: 3d817fe7 str q7, [sp, #1520] 554: 3d8183e6 str q6, [sp, #1536] 558: 3d8187e5 str q5, [sp, #1552] 55c: 3d818be4 str q4, [sp, #1568] 560: 3d818fe3 str q3, [sp, #1584] 564: 3d8193e2 str q2, [sp, #1600] 568: 3d8197e0 str q0, [sp, #1616] 56c: 911a03e0 add x0, sp, #0x680 570: 910983e1 add x1, sp, #0x260 574: d2808002 mov x2, #0x400 // #1024 578: 94000000 bl 0 <memcpy> 57c: f94337e0 ldr x0, [sp, #1640] 580: aa0003e3 mov x3, x0 584: 911a03e0 add x0, sp, #0x680 588: d2808001 mov x1, #0x400 // #1024 58c: aa0103e2 mov x2, x1 590: aa0003e1 mov x1, x0 594: aa0303e0 mov x0, x3 598: 94000000 bl 0 <memcpy> } 59c: d503201f nop 5a0: 6d4127e8 ldp d8, d9, [sp, #16] 5a4: 6d422fea ldp d10, d11, [sp, #32] 5a8: 6d4337ec ldp d12, d13, [sp, #48] 5ac: 6d443fee ldp d14, d15, [sp, #64] 5b0: a9407bfd ldp x29, x30, [sp] 5b4: d282500c mov x12, #0x1280 // #4736 5b8: 8b2c63ff add sp, sp, x12 5bc: d65f03c0 ret
9. Podpora SIMD operací NEON v programovacích jazycích
Nové instrukce zavedené v rámci technologie NEON, s nimiž jsme se setkali minule i dnes, je samozřejmě možné využívat především přímo v assembleru, což je sice pro vývojáře většinou ta nejobtížnější varianta, na druhou stranu však má programátor v tomto případě možnost přímo a do všech podrobností ovlivnit výslednou podobu programu. Ovšem naprostá většina programového kódu je v současnosti vytvářena ve vyšších programovacích jazycích. Z tohoto důvodu musí existovat nějaká možnost, jak tyto nové instrukce ve vyšších programovacích jazycích využívat – tedy jak je (i když nepřímo) vložit do nativního kódu.
Z hlediska programátora je nejjednodušší možností využít již existující odladěné a optimalizované knihovny implementované právě s pomocí SIMD instrukcí, což je většinou ideální řešení v případech, kdy tyto knihovny již obsahují implementaci časově nejnáročnějších částí programů (což ovšem zdaleka nemusí pokrývat všechny potřeby programátora). Mezi takové knihovny patří v případě mikroprocesorů ARM a technologie NEON například knihovna OpenMAX DL, v níž jsou implementovány různé zvukové i video kodeky: části algoritmů pro komprimaci a dekomprimaci pomocí JPEG (rastrové obrazy), MP3 (zvuk), H.264 (AV kodek), MPEG-4 (taktéž AV kodek) atd.
Kromě těchto algoritmů či jejich nejdůležitějších částí jsou v knihovně OpenMAX DL implementovány i funkce určené pro filtraci a zpracování signálů, především FIR, IIR (číslicové filtry s konečnou a nekonečnou impulsní odezvou) a FFT (rychlá Fourierova transformace). SIMD instrukce byly použity i při optimalizaci známé knihovny Cairo pro procesory ARM, kde se například podařilo zrychlit některé operace s rastrovým obrazem (alpha blending) až osmkrát v porovnání se „sekvenčním“ řešením (v případě Cairo se však podle mých informací veškeré optimalizace týkaly pouze úpravy některých funkcí pro práci s rastrovým obrazem; nešlo tedy o optimalizaci většiny funkcí, které jsou v této poměrně rozsáhlé knihovně implementovány).
Další možnost využití instrukcí typu SIMD i z vyšších programovacích jazyků spočívá v takzvané automatické „vektorizaci“. Překladače jazyků C a C++ totiž v některých případech dokážou rozpoznat, že je možné nějakou programovou smyčku provádět nikoli čistě sekvenčně, ale s využitím operací prováděných nad vektory. Programátor však musí v těchto případech překladači vhodným způsobem „napovědět“, například tak, že přímo v programu naznačí, že počet cyklů ve smyčce bude za všech okolností dělitelný čtyřmi či osmi atd. To nemusí být vždy úplně jednoduché, už jen z toho důvodu, že jazyky C a C++ nepodporují zápis metadat do programu (v Javě by to bylo umožněno s využitím anotací).
Automatická „vektorizace“ zmíněná v předchozím odstavci však stále nedokáže (alespoň v současnosti) využít celého potenciálu technologie NEON. Z tohoto důvodu mohou programátoři v případě potřeby zavolat přímo z programů psaných v C či C++ takzvané interní (intrinsic) funkce, tj. funkce, které jsou překladačem spravovány speciálním způsobem. Jejich použití se sice podobá volání běžné funkce, ve skutečnosti se však jedná o makro, které překladač vhodným způsobem expanduje do použití některé instrukce zavedené v technologii NEON. Příklad použití intrinsic funkce je ukázán níže na volání instrukce pro součet dvou vektorů:
#include <arm_neon.h> uint32x4_t double_elements(uint32x4_t input) { return(vaddq_u32(input, input)); }
10. Na pomezí mezi C a assemblerem – intrinsic v GCC
Intrinsic, které se taktéž v některých dokumentech označují možná přiléhavějším slovem built-ins, jsou z pohledu vývojáře (a taktéž z pohledu syntaxe jazyka C) funkce, které jsou rozeznávány a implementovány přímo překladačem (v našem konkrétním případě překladačem programovacího jazyka C), aniž by musely být deklarovány ve vyvíjeném programu nebo aniž by byly součástí nějakých knihoven. Překladač tedy nemusí generovat kód pro načtení runtime knihovny s těmito pseudofunkcemi, řešit volání těchto pseudofunkcí, ale naopak může využít všechny v dané situaci dostupné optimalizační strategie (typicky se intrinsic do kódu vkládá jako sekvence strojových instrukcí).
V předchozím odstavci je sice napsáno, že intrinsic (built-ins) vypadají z pohledu syntaxe programovacího jazyka C jako běžné funkce (a tak je tedy bude používat vývojář, popř. vývojové prostředí), ovšem ve skutečnosti se o plnohodnotné funkce nejedná, protože strojový kód pro ně generuje přímo překladač na základě interních pravidel. Do jisté míry se intrinsic podobají inline funkcím, protože i u nich lze zcela odstranit problematické předávání parametrů a pamatování návratových hodnot.
Proč však vlastně intrinsic vznikly a proč se používají zrovna v programovacím jazyku C, který je v ostatních ohledech navržen takovým způsobem, aby byl prakticky zcela abstrahován (na rozdíl od například Pascalu nebo i Go) od konkrétních knihoven a funkcí? V některých případech je nutné umožnit programátorům přístup ke specializovaným instrukcím, jejichž sémantiku není možné dobře vyjádřit přímo v C. A přesně toto je případ SIMD instrukcí, ať již na platformě x86–64, tak i na ARMech či na RISC-V. Z tohoto důvodu se budeme intrinsic zabývat v dalším textu; zaměříme se přitom opět na GCC, jehož vlastnosti jsou postupně přebírány i do Clangu.
11. Instrinsic pro SIMD operace na platformě x86–64
Na platformě x86–64 jsou (v oblasti SIMD operací) dostupné tři skupiny intrinsic, které jsou vypsány v následující tabulce:
Technologie | Hlavičkový soubor |
---|---|
MMX | mmintrin.h |
SSE1 | xmmintrin.h |
SSE2 | emmintrin.h |
Operace, které jsou přístupné přes intrinsic, prakticky 1:1 odpovídají strojovým instrukcím (jak ostatně uvidíme dále), ovšem programátor v C pochopitelně nemusí řešit předávání hodnot do registrů atd. (tedy věci, které by musel řešit v assembleru). Soustředit se musí jen na to důležité – použití SIMD operací.
12. Intrinsic pro využití technologie MMX
První SIMD technologií, která na platformě x86–64 vznikla, je technologie MMX. Ta se (stále) může hodit v situacích, kdy se provádí celočíselné operace s krátkými vektory.
Všech 57 instrukcí zavedených v instrukční sadě MMX lze rozdělit podle jejich funkce do několika skupin vypsaných v následující tabulce (ke jménu instrukce se ještě přidává typ prvků vektoru):
# | Skupina instrukcí | Příklady instrukcí |
---|---|---|
1 | Základní aritmetické operace | PADD, PADDS, PADDUS, PSUBS, PSUBUS, PMULHW, PMULLW |
2 | Logické (bitové) operace | PAND, PANDN, POR, PXOR |
3 | Bitové posuny | PSLL, PSRL, PSRA |
4 | Porovnávání | PCMPEQ, PCMGT |
5 | Konverze dat | PACKUSWB, PACKSS, PUNPCKH, PUNPCKL |
6 | Přenosy dat + práce s pamětí | MOV |
7 | Řízení jednotky MMX | EMMS |
Konkrétně se pracuje s těmito typy vektorů:
Typ v C | Význam | Deklarace |
---|---|---|
__v8qi | 8 prvků o velikosti char | typedef char __v8qi __attribute__ ((__vector_size__ (8))); |
__v4hi | 4 prvky o velikosti short | typedef short __v4hi __attribute__ ((__vector_size__ (8))); |
_v2si | 2 prvky o velikosti long | typedef int __v2si __attribute__ ((__vector_size__ (8))); |
__v1di | 1 prvek o velikosti long long | typedef long long __v1di __attribute__ ((__vector_size__ (8))); |
Pro prakticky každou MMX instrukci existuje příslušný intrinsic. Všechny tyto intrinsic jsou vypsány pod tímto odstavcem:
v8qi __builtin_ia32_paddb (v8qi, v8qi); v4hi __builtin_ia32_paddw (v4hi, v4hi); v2si __builtin_ia32_paddd (v2si, v2si); v8qi __builtin_ia32_psubb (v8qi, v8qi); v4hi __builtin_ia32_psubw (v4hi, v4hi); v2si __builtin_ia32_psubd (v2si, v2si); v8qi __builtin_ia32_paddsb (v8qi, v8qi); v4hi __builtin_ia32_paddsw (v4hi, v4hi); v8qi __builtin_ia32_psubsb (v8qi, v8qi); v4hi __builtin_ia32_psubsw (v4hi, v4hi); v8qi __builtin_ia32_paddusb (v8qi, v8qi); v4hi __builtin_ia32_paddusw (v4hi, v4hi); v8qi __builtin_ia32_psubusb (v8qi, v8qi); v4hi __builtin_ia32_psubusw (v4hi, v4hi); v4hi __builtin_ia32_pmullw (v4hi, v4hi); v4hi __builtin_ia32_pmulhw (v4hi, v4hi); di __builtin_ia32_pand (di, di); di __builtin_ia32_pandn (di,di); di __builtin_ia32_por (di, di); di __builtin_ia32_pxor (di, di); v8qi __builtin_ia32_pcmpeqb (v8qi, v8qi); v4hi __builtin_ia32_pcmpeqw (v4hi, v4hi); v2si __builtin_ia32_pcmpeqd (v2si, v2si); v8qi __builtin_ia32_pcmpgtb (v8qi, v8qi); v4hi __builtin_ia32_pcmpgtw (v4hi, v4hi); v2si __builtin_ia32_pcmpgtd (v2si, v2si); v8qi __builtin_ia32_punpckhbw (v8qi, v8qi); v4hi __builtin_ia32_punpckhwd (v4hi, v4hi); v2si __builtin_ia32_punpckhdq (v2si, v2si); v8qi __builtin_ia32_punpcklbw (v8qi, v8qi); v4hi __builtin_ia32_punpcklwd (v4hi, v4hi); v2si __builtin_ia32_punpckldq (v2si, v2si); v8qi __builtin_ia32_packsswb (v4hi, v4hi); v4hi __builtin_ia32_packssdw (v2si, v2si); v8qi __builtin_ia32_packuswb (v4hi, v4hi); v4hi __builtin_ia32_psllw (v4hi, v4hi); v2si __builtin_ia32_pslld (v2si, v2si); v1di __builtin_ia32_psllq (v1di, v1di); v4hi __builtin_ia32_psrlw (v4hi, v4hi); v2si __builtin_ia32_psrld (v2si, v2si); v1di __builtin_ia32_psrlq (v1di, v1di); v4hi __builtin_ia32_psraw (v4hi, v4hi); v2si __builtin_ia32_psrad (v2si, v2si); v4hi __builtin_ia32_psllwi (v4hi, int); v2si __builtin_ia32_pslldi (v2si, int); v1di __builtin_ia32_psllqi (v1di, int); v4hi __builtin_ia32_psrlwi (v4hi, int); v2si __builtin_ia32_psrldi (v2si, int); v1di __builtin_ia32_psrlqi (v1di, int); v4hi __builtin_ia32_psrawi (v4hi, int); v2si __builtin_ia32_psradi (v2si, int);
Jejich použití si ukážeme v následujícím textu.
13. Ukázka použití: součet vektorů s celočíselnými operandy
Podívejme se nyní na způsob využití některých základních MMX operací. Začneme instrukcí nazvanou paddb, která (jak již její název naznačuje) slouží k součtu dvou vektorů, z nichž každý obsahuje osm bajtových prvků; celý vektor je tedy představován datovým typem __v8qi. Tato instrukce je representována pomocí symbolu __builtin_ia32_paddb:
#include <stdio.h> #include <mmintrin.h> int main(void) { __v8qi x = { 1, 2, 3, 4, 5, 6, 7, 8 }; __v8qi y = { 1, 2, 3, 4, 5, 6, 7, 8 }; __v8qi z; int i; z = __builtin_ia32_paddb(x, y); for (i = 0; i < 8; i++) { printf("%d %d\n", i, z[i]); } }
Výsledek by měl vypadat následovně (ve druhém sloupci jsou prvky výsledného vektoru):
0 2 1 4 2 6 3 8 4 10 5 12 6 14 7 16
Podobně můžeme sečíst dva vektory, z nichž každý obsahuje čtyři prvky typu 16bitové celé číslo. Součet je interně realizován instrukcí paddw, takže příslušný intrinsic se jmenuje __builtin_ia32_paddw:
#include <stdio.h> #include <mmintrin.h> int main(void) { __v4hi x = { 1, 2, 3, 4 }; __v4hi y = { 1000, 1000, 1000, 1000 }; __v4hi z; int i; z = __builtin_ia32_paddw(x, y); for (i = 0; i < 4; i++) { printf("%d %d\n", i, z[i]); } }
Výsledek by měl v tomto případě vypadat následovně:
0 1001 1 1002 2 1003 3 1004
14. Způsob překladu obou demonstračních příkladů do assembleru
Zajímavé bude zjistit, jak se vlastně oba demonstrační příklady přeložily do assembleru. Nejprve si ukažme část přeloženého prvního příkladu:
z = __builtin_ia32_paddb(x, y); 31: 0f 6f 45 e8 movq mm0,QWORD PTR [rbp-0x18] 35: 0f fc 45 f0 paddb mm0,QWORD PTR [rbp-0x10] 39: 0f 7f 45 e0 movq QWORD PTR [rbp-0x20],mm0
Ve druhém příkladu je – podle očekávání – využita odlišná MMX instrukce, ovšem základ zůstává stejný:
z = __builtin_ia32_paddw(x, y); 31: 0f 6f 45 e8 movq mm0,QWORD PTR [rbp-0x18] 35: 0f fd 45 f0 paddw mm0,QWORD PTR [rbp-0x10] 39: 0f 7f 45 e0 movq QWORD PTR [rbp-0x20],mm0
15. Součet s přetečením a součet se saturací
V mnoha multimediálních aplikacích je nutné provádět aritmetické operace se saturací, tj. takovým způsobem, aby výsledná hodnota operace (například součtu) nepřetekla nebo nepodtekla, což by vedlo k nepříjemným artefaktům v obrazu, lupancům ve zvuku atd.. Ostatně si to můžeme ukázat na klasickém příkladu – zvýšení světlosti celého obrázku o zadanou konstantu:
Obrázek 1: Zdrojový rastrový obrázek (známá fotografie Lenny), který tvoří zdroj pro jednoduchý konvoluční (FIR) filtr, jenž zvyšuje hodnoty pixelů o pevně zadanou konstantu (offset).
Obrázek 2: Pokud je pro přičtení offsetu použita operace součtu se zanedbáním přenosu (carry), tj. když se počítá systémem „modulo N“ (viz též výše zmíněná instrukce PADDB), dochází při překročení maximální hodnoty pixelu (čistě bílá barva) k viditelným chybám.
Obrázek 3: Při použití operace součtu se saturací sice taktéž dojde ke ztrátě informace (vzniknou oblasti s pixely majícími hodnotu 255), ovšem viditelná chyba je mnohem menší, než na předchozím obrázku, kde docházelo k přetečení. Tento filtr by bylo možné realizovat s využitím instrukce PADDUSB s rychlostí výpočtu 8 pixelů/instrukci při bitové hloubce 8bpp.
V MMX jsou podporovány základní vektorové operace jak s přetečením (tj. tak, jak jsme z IT zvyklí), tak i se saturací. Každá z těchto operací je přitom realizována odlišnou instrukcí.
Nejprve si ukažme, jak vypadá součet dvou osmiprvkových vektorů s případným přetečením přes maximální 8bitovou hodnotu se znaménkem (signed char):
#include <stdio.h> #include <mmintrin.h> int main(void) { __v8qi x = { 0, 2, 4, 6, 8, 10, 12, 14 }; __v8qi y = { 120, 120, 120, 120, 120, 120, 120, 120 }; __v8qi z; int i; z = __builtin_ia32_paddb(x, y); for (i = 0; i < 8; i++) { printf("%d %d %d %d\n", i, x[i], y[i], z[i]); } }
Povšimněte si, že instrukce paddb bez problémů realizovala součet, který pro signed char přetekl:
0 0 120 120 1 2 120 122 2 4 120 124 3 6 120 126 4 8 120 -128 5 10 120 -126 6 12 120 -124 7 14 120 -122
Zkusme nyní vyměnit paddb za instrukci paddsb, tedy součet se saturací:
#include <stdio.h> #include <mmintrin.h> int main(void) { __v8qi x = { 0, 2, 4, 6, 8, 10, 12, 14 }; __v8qi y = { 120, 120, 120, 120, 120, 120, 120, 120 }; __v8qi z; int i; z = __builtin_ia32_paddsb(x, y); for (i = 0; i < 8; i++) { printf("%d %d %d %d\n", i, x[i], y[i], z[i]); } }
Z výsledků je patrné, že nedošlo k přetečení, ale k „zastropení“ na maximální hodnotě signed char:
0 0 120 120 1 2 120 122 2 4 120 124 3 6 120 126 4 8 120 127 5 10 120 127 6 12 120 127 7 14 120 127
16. Způsob překladu obou demonstračních příkladů do assembleru
Opět se alespoň v rychlosti podívejme na to, jakým způsobem je vektorový součet s případným přetečením, resp. se saturací přeložen do assembleru. Začneme prvním příkladem, v němž je realizován součet se saturací:
z = __builtin_ia32_paddb(x, y); 31: 0f 6f 45 e8 movq mm0,QWORD PTR [rbp-0x18] 35: 0f 6f 4d e0 movq mm1,QWORD PTR [rbp-0x20] 39: 0f fc c1 paddb mm0,mm1 3c: 0f 7f 45 f0 movq QWORD PTR [rbp-0x10],mm0
Naopak součet se saturací namísto instrukce paddb používá instrukci paddsb:
z = __builtin_ia32_paddsb(x, y); 31: 0f 6f 45 e8 movq mm0,QWORD PTR [rbp-0x18] 35: 0f 6f 4d e0 movq mm1,QWORD PTR [rbp-0x20] 39: 0f ec c1 paddsb mm0,mm1 3c: 0f 7f 45 f0 movq QWORD PTR [rbp-0x10],mm0
17. Převody mezi různými typy vektorů, kombinace dvou vektorů (pack, unpack)
V instrukční sadě MMX nalezneme několik instrukcí určených pro převody mezi různými typy vektorů, ale i instrukce, které dokážou zkombinovat prvky ze dvou vektorů do vektoru výsledného. Kombinací je myšleno například proložení prvků, což je velice užitečné například ve chvíli, kdy se konvertují různé typy rastrových obrázků atd.
Příkladem takové instrukce je instrukce nazvaná punpckhbw, která proloží prvky (typu byte) ze dvou vektorů do vektoru výsledného. Ovšem výsledný vektor má stále délku osmi prvků, takže se ve skutečnosti použijí čtyři poslední prvky z vektoru prvního a čtyři poslední prvky z vektoru druhého (to je význam znaku „h“ v názvu instrukce). Dokážete si představit, jak pomalá by byla implementace v čistém C? Naproti tomu s využitím intrinsic je tato operace naprosto triviální:
#include <stdio.h> #include <mmintrin.h> int main(void) { __v8qi x = { 1, 2, 3, 4, 5, 6, 7, 8 }; __v8qi y = { 99, 98, 97, 96, 95, 94, 93, 92 }; __v8qi z; int i; z = __builtin_ia32_punpckhbw(x, y); for (i = 0; i < 8; i++) { printf("%d %d %d %d\n", i, x[i], y[i], z[i]); } }
Výsledek získaný po překladu a spuštění tohoto programu je následující (povšimněte si proložení v posledním sloupci):
0 1 99 5 1 2 98 95 2 3 97 6 3 4 96 94 4 5 95 7 5 6 94 93 6 7 93 8 7 8 92 92
18. Způsob překladu demonstračního příkladu do assembleru
Samozřejmě se nezapomeneme podívat na to, jakým způsobem je tento demonstrační příklad přeložen do assembleru. Výsledný kód je (na rozdíl od ručně psaného kódu v C) triviální, rychlý a bezchybný:
z = __builtin_ia32_punpckhbw(x, y); 31: 0f 6f 4d e8 movq mm1,QWORD PTR [rbp-0x18] 35: 0f 6f 45 e0 movq mm0,QWORD PTR [rbp-0x20] 39: 0f 68 c1 punpckhbw mm0,mm1 3c: 0f 7f 45 f0 movq QWORD PTR [rbp-0x10],mm0
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ž velmi rozsáhlý) repositář:
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/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/simd04B1.lst |
4 | simd04B2.lst | překlad zdrojového kódu simd04B2.c s přepínači -O0 -g | https://github.com/tisnik/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/simd18_4.lst |
35 | intrinsic_mmx1.lst | překlad zdrojového kódu intrinsic_mmx1.c | https://github.com/tisnik/presentations/blob/master/SIMD/intrinsic_mmx1.lst |
36 | intrinsic_mmx2.lst | překlad zdrojového kódu intrinsic_mmx2.c | https://github.com/tisnik/presentations/blob/master/SIMD/intrinsic_mmx2.lst |
37 | intrinsic_mmx3.lst | překlad zdrojového kódu intrinsic_mmx3.c | https://github.com/tisnik/presentations/blob/master/SIMD/intrinsic_mmx3.lst |
39 | intrinsic_mmx5.lst | překlad zdrojového kódu intrinsic_mmx5.c | https://github.com/tisnik/presentations/blob/master/SIMD/intrinsic_mmx5.lst |
Soubory vzniklé překladem z jazyka C do assembleru procesorů ARMv8:
# | 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 -march=armv8-a+nosimd -g | https://github.com/tisnik/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/simd04_2.lst |
3 | simd04B1.lst | překlad zdrojového kódu simd04B1.c s přepínači -O0 -march=armv8-a+nosimd -g | https://github.com/tisnik/presentations/blob/master/SIMD/simd04B1.lst |
4 | simd04B2.lst | překlad zdrojového kódu simd04B2.c s přepínači -O0 -g | https://github.com/tisnik/presentations/blob/master/SIMD/simd04B2.lst |
5 | simd07_1.lst | překlad zdrojového kódu simd07_1.c s přepínači -march=armv8-a+nosimd -g | https://github.com/tisnik/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/simd07_2.lst |
7 | simd08_1.lst | překlad zdrojového kódu simd08_1.c s přepínači -march=armv8-a+nosimd -g | https://github.com/tisnik/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/simd08_2.lst |
9 | simd12_1.lst | překlad zdrojového kódu simd12_1.c s přepínači -O0 -march=armv8-a+nosimd -g | https://github.com/tisnik/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/simd12_2.lst |
11 | simd13_1.lst | překlad zdrojového kódu simd13_1.c s přepínači -O0 -march=armv8-a+nosimd -g | https://github.com/tisnik/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/simd13_2.lst |
13 | simd13_3.lst | překlad zdrojového kódu simd13_3.c s přepínači -O3 -march=armv8-a+nosimd -g | https://github.com/tisnik/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/simd13_4.lst |
15 | simd14_1.lst | překlad zdrojového kódu simd14_1.c s přepínači -O0 -march=armv8-a+nosimd -g | https://github.com/tisnik/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/simd14_2.lst |
17 | simd14_3.lst | překlad zdrojového kódu simd14_3.c s přepínači -O3 -march=armv8-a+nosimd -g | https://github.com/tisnik/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/simd14_4.lst |
19 | simd15_1.lst | překlad zdrojového kódu simd15_1.c s přepínači -O0 -march=armv8-a+nosimd -g | https://github.com/tisnik/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/simd15_2.lst |
21 | simd15_3.lst | překlad zdrojového kódu simd15_3.c s přepínači -O3 -march=armv8-a+nosimd -g | https://github.com/tisnik/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/simd15_4.lst |
23 | simd16_1.lst | překlad zdrojového kódu simd16_1.c s přepínači -O0 -march=armv8-a+nosimd -g | https://github.com/tisnik/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/simd16_2.lst |
25 | simd16_3.lst | překlad zdrojového kódu simd16_3.c s přepínači -O3 -march=armv8-a+nosimd -g | https://github.com/tisnik/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/simd16_4.lst |
27 | simd17_1.lst | překlad zdrojového kódu simd17_1.c s přepínači -O0 -march=armv8-a+nosimd -g | https://github.com/tisnik/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/simd17_2.lst |
29 | simd17_3.lst | překlad zdrojového kódu simd17_3.c s přepínači -O3 -march=armv8-a+nosimd -g | https://github.com/tisnik/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/simd17_4.lst |
31 | simd18_1.lst | překlad zdrojového kódu simd18_1.c s přepínači -O0 -march=armv8-a+nosimd -g | https://github.com/tisnik/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/simd18_2.lst |
33 | simd18_3.lst | překlad zdrojového kódu simd18_3.c s přepínači -O3 -march=armv8-a+nosimd -g | https://github.com/tisnik/presentations/blob/master/SIMD/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/presentations/blob/master/SIMD/simd18_4.lst |
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