Hlavní navigace

Rozšíření instrukční sady F16C, FMA a AVX-512 na platformě x86–64

10. 11. 2022
Doba čtení: 51 minut

Sdílet

 Autor: Depositphotos
Dnes se seznámíme s rozšířeními instrukční sady, které se jmenují F16C, FMA a AVX-512. První dvě rozšíření jsou pojata minimalisticky, ovšem AVX-512 přidává velké množství instrukcí i registrů.

Obsah

1. Rozšíření instrukční sady F16C, FMA a AVX-512 na platformě x86–64, detekce podpory mikroprocesorem

2. Rozšíření F16C

3. Instrukce přidané v rámci rozšíření F16C

4. Převod vektoru s prvky float na vektor s prvky half a zpět

5. Konverze velkých hodnot, automatický převod na nekonečna

6. Konverze malých hodnot s volbou režimu zaokrouhlení

7. Způsob uložení hodnot typu half float

8. Využití 256bitových vektorů při převodech single/float na half float a zpět

9. Rozšíření FMA

10. Použití intrinsic pro instrukci VFMADD

11. Výpočet multiply-add s desetinnými hodnotami

12. Výpočty s maximálními hodnotami typu single/float

13. Použití intrinsic pro instrukci VFNMADD

14. Použití intrinsic pro instrukci VFMSUB

15. Rozšíření AVX-512

16. AVX-512 jakožto několik volitelných sad rozšíření instrukcí

17. Možné režimy SIMD s přihlédnutím k možnostem AVX-512

18. Obsah závěrečného článku o SIMD

19. Repositář s demonstračními příklady

20. Odkazy na Internetu

1. Rozšíření instrukční sady F16C, FMA a AVX-512 na platformě x86–64, detekce podpory mikroprocesorem

V sedmé a současně i předposlední části miniseriálu o podpoře SIMD operací na platformě x86–64 se seznámíme s rozšířeními instrukční sady, které se jmenují F16C, FMA a AVX-512. Zejména u instrukčních sad FMA (přesněji řečeno FMA4) a AVX-512 se již dostáváme k technologiím, která nemusí být dnes používanými mikroprocesory s architekturou x86–64 podporovány (a to buď proto, že se jedná o příliš nové instrukce, nebo naopak o instrukce již opuštěné či využívané jen konkurencí). Prozatím jsme totiž předpokládali, že MMX, SSE i AVX budou na většině CPU podporovány, ovšem ani u FMA4 ani u AVX-512 to není pravda.

Někdy se tedy nevyhneme runtime testu, zda je nějaké rozšíření podporováno či nikoli. Interně k tomuto účelu slouží instrukce CPUID, ovšem GCC tuto instrukci (a další logiku okolo ní) „obaluje“ v intrinsic nazvanou __builtin_cpu_supports. Této intrinsic je nutné předat řetězcovou konstantu (skutečně konstantu, ne například ukazatel do pole řetězců – takže v programu nepoužívám smyčku) se zkratkou rozšíření; návratovou hodnotou je pak kladná celočíselná hodnota v případě, že je rozšíření podporováno a nula, pokud podporováno není (takže se jedná o klasické céčkovské pravdivostní hodnoty):

#include <stdio.h<
 
int main(void)
{
    printf("Extension SSE    is %ssupported\n",
           __builtin_cpu_supports("sse") ? "" : "un");
    printf("Extension SSE2   is %ssupported\n",
           __builtin_cpu_supports("sse2") ? "" : "un");
    printf("Extension SSE3   is %ssupported\n",
           __builtin_cpu_supports("sse3") ? "" : "un");
    printf("Extension SSE4.1 is %ssupported\n",
           __builtin_cpu_supports("sse4.1") ? "" : "un");
    printf("Extension SSE4.2 is %ssupported\n",
           __builtin_cpu_supports("sse4.2") ? "" : "un");
    printf("Extension AVX    is %ssupported\n",
           __builtin_cpu_supports("avx") ? "" : "un");
    printf("Extension AVX2   is %ssupported\n",
           __builtin_cpu_supports("avx2") ? "" : "un");
    printf("Extension FMA    is %ssupported\n",
           __builtin_cpu_supports("fma") ? "" : "un");
    printf("Extension FMA4   is %ssupported\n",
           __builtin_cpu_supports("fma4") ? "" : "un");
    return 0;
}

V mém případě se po překladu a spuštění výše uvedeného programového kódu zobrazí tyto informace:

Extension SSE    is supported
Extension SSE2   is supported
Extension SSE3   is supported
Extension SSE4.1 is supported
Extension SSE4.2 is supported
Extension AVX    is supported
Extension AVX2   is supported
Extension FMA    is supported
Extension FMA4   is unsupported
Poznámka: jedná se o tento mikroprocesor:
$ cat /proc/cpuinfo |head -n 21
 
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         : 900.018
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

2. Rozšíření F16C

Rozšíření F16C obsahuje instrukce určené pro převody mezi vektory, jejichž prvky jsou typu single/float a half float (přesněji half precision floating point). Nejsou zde ovšem přítomny instrukce určené pro výpočty s typy half float, takže se toto rozšíření používá „jen“ pro načtení vektorů s prvky tohoto typu z paměti popř. naopak pro uložení hodnot tohoto typu do paměti. Výpočty budou probíhat s hodnotami single/float nebo double. Díky tomuto přístupu je rozšíření F16C velmi malé, protože obsahuje pouhé dvě instrukce VCVTPH2PS a VCVTPS2PH, které si popíšeme níže. K čemu se však typ half float používá?

Zatímco výše zmíněné formáty single a double jsou určeny pro běžné aritmetické výpočty a při správném použití mohou být využity v mnoha numerických algoritmech, začal být společně s rozšiřováním grafických akcelerátorů (a později s rozvojem neuronových sítí) vyvíjen tlak na standardizaci formátů s menší bitovou hloubkou. Je tomu tak z toho důvodu, že některé operace (již jsme se zmínili o paměti hloubky, ovšem i operace s barvami pixelů atd.) někdy vyžadují vyšší dynamický rozsah, ovšem přesnost nemusí být vysoká a více nám záleží na rychlosti provádění operací.

Dobrým příkladem je dnes již pochopitelně dávno překonaný, ovšem z hlediska vývoje IT velmi důležitý grafický akcelerátor Voodoo I, resp. přesněji řečeno způsob implementace jeho paměti hloubky. Do paměti hloubky (Z-bufferu) je možné ukládat vzdálenosti fragmentů od pozorovatele (kamery) ve dvou formátech, v obou případech je však každý údaj vždy uložen na šestnácti bitech. Při použití prvního způsobu se do Z-bufferu skutečně ukládají vzdálenosti fragmentů, přesněji řečeno celočíselná část vzdálenosti (výpočty vzdálenosti se provádí přesněji, ale výsledek je při ukládání zaokrouhlen).

Tento formát ovšem ve skutečnosti není příliš výhodný, protože po projekci 3D scény ze světových souřadnic do prostoru obrazovky není krok mezi jednotlivými vzdálenostmi konstantní, což vede k vizuálním chybám při vykreslování (rozlišení pouze 216 vzdáleností je v tomto případě nedostatečné). Z tohoto důvodu se preferuje alternativní způsob (nazývaný také w-buffer), při němž se do Z-bufferu ukládají převrácené hodnoty vzdálenosti, a to ve speciálním formátu čísel s pohyblivou řádovou tečkou (čárkou), který má následující strukturu připomínající formát definovaný v IEEE 754:

1.mantissa × 2exponent

V tomto formátu je pro mantisu vyhrazeno dvanáct bitů a pro exponent čtyři bity. Povšimněte si implicitní jedničky před desetinnou tečkou i toho, že žádný bit není vyhrazen pro uložení znaménka – vzdálenosti (a samozřejmě i jejich převrácené hodnoty) jsou vždy kladné. Minimální hodnota, kterou lze tímto způsobem uložit, je rovna jedničce (0×0000 ~ 1.0000000000002×20), maximální hodnota 65528.0 (0×ffff ~ 1.1111111111112×215).

Podobné „krátké“ formáty čísel s plovoucí řádovou tečkou jsou v oblasti grafických akcelerátorů velmi oblíbené. NVidia a firma Microsoft zavedla typ half do jazyka Cg (v roce 2002), ILM podporuje tento formát pro operace vyžadující velkou dynamiku (rozsah) hodnot atd.

Poznámka: v některých grafických akcelerátorech narazíme na formát fp24, který stojí na půl cesty mezi typem half a single. Ten však není na architektuře x86–64 přímo podporován.

Formát half float, jenž je dnes standardizován v IEEE 754–2008, používá pro ukládání hodnot s plovoucí řádovou čárkou pouhých šestnáct bitů, tj. dva byty. Maximální hodnota je rovna 65504, minimální hodnota (větší než nula) přibližně 5,9×10-8. Předností tohoto formátu je malá bitová šířka (umožňuje paralelní přenos po interních sběrnicích GPU) a také větší rychlost zpracování základních operací, protože pro tak malou bitovou šířku mantisy je možné některé operace „zadrátovat“ a nepočítat pomocí ALU. Také některé iterativní výpočty (sin, cos, sqrt) mohou být provedeny rychleji, než v případě plnohodnotných typů float a single.

Celkový počet bitů (bytů): 16 (2)
Bitů pro znaménko: 1
Bitů pro exponent: 5
Bitů pro mantisu: 10
BIAS (offset exponentu): 15
Přesnost: 5–6 číslic
Maximální hodnota: 65504
Minimální hodnota: –65504
Nejmenší kladná nenulová hodnota: 5,96×10-8
Nejmenší kladná normalizovaná hodnota: 6,104×10-5
Podpora záporné nuly: ano
Podpora +∞: ano
Podpora -∞: ano
Podpora NaN: ano
Poznámka: způsob kódování hodnot si dovysvětlíme v navazujících kapitolách.

3. Instrukce přidané v rámci rozšíření F16C

Jak jsme si již řekli v předchozí kapitole, byly v rámci rozšíření F16C přidány pouhé dvě instrukce, které existují ve dvou variantách – první varianta je určena pro vektory o šířce 128 bitů a druhá varianta pro vektory o šířce 256 bitů:

Instrukce Operandy Stručný popis
VCVTPH2PS xmm,xmm (nebo xmm,mem) konverze čtyř hodnot typu half na čtyři hodnoty typu single/float
VCVTPH2PS ymm,xmm (nebo ymm,mem) konverze osmi hodnot typu half na osm hodnot typu single/float
VCVTPS2PH xmm,xmm,imm8 (nebo mem,xmm,imm8) konverze čtyř hodnot typu single/float na čtyři hodnoty typu half
VCVTPS2PH xmm,ymm,imm8 (nebo mem,ymm,imm8) konverze osmi hodnot typu single/float na osm hodnot typu half
Poznámka: povšimněte si, že čtyři i osm hodnot typu half se vejde do 128bitových registrů XMMx, kdežto u typu float je nutné osm hodnot uložit do 256bitového registru YMMx.

Celočíselná konstanta imm8 u obou variant instrukce VCVTPS2PH dovoluje (kromě dalších věcí) specifikovat zaokrouhlovací režim, protože při konverzi single/float → half pochopitelně ztrácíme přesnost:

Hodnota (bity) Význam
00 zaokrouhlení na nejbližší hodnotu
01 zaokrouhlení směrem dolů
10 zaokrouhlení směrem nahoru
11 odříznutí nižších bitů

4. Převod vektoru s prvky float na vektor s prvky half a zpět

Vyzkoušejme si nyní převod 128bitového vektoru obsahujícího čtyři prvky typu float/single na vektor obsahující čtyři prvky typu half float. Tento převod lze realizovat intrinsic nazvanou __builtin_ia32_vcvtps2ph, které se předá vstupní vektor i celočíselná hodnota se zaokrouhlovacím režimem (zde nastaven na 0 – zaokrouhlení na nejbližší hodnotu). Dále provedeme zpětný převod vektoru s prvky typu half float zpět na vektor s prvky typu float/single. Tento zpětný převod je realizován intrinsic se jménem __builtin_ia32_vcvtph2ps. Povšimněte si, že vektor s prvky typu half je typu __v8hi (nebo obdobným vektorem bez znaménkových hodnot):

#include <stdio.h>
#include <immintrin.h>
 
int main(void)
{
    __v4sf x = { 0.0, 0.1, 1.0, 3.14 };
    __v8hi half;
    __v4sf y;
    int i;
 
    // konverze float -> half
    half = __builtin_ia32_vcvtps2ph(x, 0);
 
    // konverze half -> float
    y = __builtin_ia32_vcvtph2ps(half);
 
    for (i = 0; i < sizeof(x) / sizeof(float); i++) {
        printf("%2d   %f   %04x   %f\n", i, x[i], half[i], y[i]);
    }
    return 0;
}

Překlad obou konverzí do assembleru bude vypadat následovně:

    // konverze float -> half
    half = __builtin_ia32_vcvtps2ph(x, 0);
  28:   c5 f8 28 45 c0          vmovaps xmm0,XMMWORD PTR [rbp-0x40]
  2d:   c4 e3 79 1d c0 00       vcvtps2ph xmm0,xmm0,0x0
  33:   c5 f8 29 45 d0          vmovaps XMMWORD PTR [rbp-0x30],xmm0
 
    // konverze half -> float
    y = __builtin_ia32_vcvtph2ps(half);
  38:   c5 f9 6f 45 d0          vmovdqa xmm0,XMMWORD PTR [rbp-0x30]
  3d:   c4 e2 79 13 c0          vcvtph2ps xmm0,xmm0
  42:   c5 f8 29 45 e0          vmovaps XMMWORD PTR [rbp-0x20],xmm0

Zajímavější je však výstup z tohoto prográmku. Ukazuje totiž, že se při převodu ze single/float na half float skutečně ztrácí přesnost, což je vidět na zvýrazněných hodnotách:

 0   0.000000   0000   0.000000
 1   0.100000   2e66   0.099976
 2   1.000000   3c00   1.000000
 3   3.140000   4248   3.140625

5. Konverze velkých hodnot, automatický převod na nekonečna

Nyní se podívejme na způsob převodu „velkých“ hodnot typu single/float na typ half float a zpět. Slovo „velké“ jsem dal do uvozovek z toho důvodu, že ve skutečnosti je maximální reprezentovatelnou hodnotou pouze 65504. Ostatně se podívejme na následující demonstrační příklad:

#include <stdio.h>
#include <immintrin.h>
 
int main(void)
{
    __v4sf x = { 1e3, 1e4, 1e5, 1e6 };
    __v8hi half;
    __v4sf y;
    int i;
 
    // konverze float -> half
    half = __builtin_ia32_vcvtps2ph(x, 0);
 
    // konverze half -> float
    y = __builtin_ia32_vcvtph2ps(half);
 
    for (i = 0; i < sizeof(x) / sizeof(float); i++) {
        printf("%2d  %7.0f   %04x   %7.0f\n", i, x[i], half[i], y[i]);
    }
    return 0;
}

Velmi zajímavý je výsledek, který ukazuje, že hodnoty nad 65504 se korektně převedou na nekonečno a pochopitelně zůstávají nekonečnem i při zpětném převodu:

 0     1000   63d0      1000
 1    10000   70e2     10000
 2   100000   7c00       inf
 3  1000000   7c00       inf

6. Konverze malých hodnot s volbou režimu zaokrouhlení

V předchozí kapitole jsme si ukázali, jak se velké hodnoty, které se nemohou do rozsahu datového typu half float vejít, převedou na nekonečno. Jak je tomu u malých hodnot, přesněji řečeno u hodnot blízkých nule? V tomto případě se začne projevovat nastavení zaokrouhlovacích režimů, které je ukázáno v následujícím demonstračním příkladu:

#include <stdio.h>
#include <immintrin.h>
 
void print_vectors(__v4sf x, __v4sf y, __v8hi half)
{
    int i;
 
    for (i = 0; i < sizeof(x) / sizeof(float); i++) {
        printf("%2d   %9.8f   %04x   %9.8f\n", i, x[i], half[i], y[i]);
    }
 
    putchar('\n');
}
 
int main(void)
{
    __v4sf x = { 5e-4, 5e-5, 5e-6, 5e-7 };
    __v8hi half;
    __v4sf y;
 
    // round to nearest even
    half = __builtin_ia32_vcvtps2ph(x, 0);
    y = __builtin_ia32_vcvtph2ps(half);
    print_vectors(x, y, half);
 
    // round down
    half = __builtin_ia32_vcvtps2ph(x, 1);
    y = __builtin_ia32_vcvtph2ps(half);
    print_vectors(x, y, half);
 
    // round up
    half = __builtin_ia32_vcvtps2ph(x, 2);
    y = __builtin_ia32_vcvtph2ps(half);
    print_vectors(x, y, half);
 
    // truncate
    half = __builtin_ia32_vcvtps2ph(x, 3);
    y = __builtin_ia32_vcvtph2ps(half);
    print_vectors(x, y, half);
 
    return 0;
}

Z výsledků je patrné, jak se zaokrouhlovací režimy projevují. Nejvíce je to vidět ve druhém bloku (hodnoty jsou vždy shodné či nižší) a na bloku třetím (hodnoty jsou vždy shodné či vyšší). Nejmenší souhrnné odchylky jsou podle očekávání v prvním bloku. Mimochodem – druhý a čtvrtý blok mají stejné hodnoty, ovšem pokud změníte převáděné vstupy na záporná čísla, uvidíte podstatný rozdíl:

 0   0.00050000   1019   0.00050020
 1   0.00005000   0347   0.00005001
 2   0.00000500   0054   0.00000501
 3   0.00000050   0008   0.00000048
 
 0   0.00050000   1018   0.00049973
 1   0.00005000   0346   0.00004995
 2   0.00000500   0053   0.00000495
 3   0.00000050   0008   0.00000048
 
 0   0.00050000   1019   0.00050020
 1   0.00005000   0347   0.00005001
 2   0.00000500   0054   0.00000501
 3   0.00000050   0009   0.00000054
 
 0   0.00050000   1018   0.00049973
 1   0.00005000   0346   0.00004995
 2   0.00000500   0053   0.00000495
 3   0.00000050   0008   0.00000048

Pro zajímavost se podívejme i na způsob překladu, zejména na význam posledního operandu funkce vcvtps2ph:

    // round to nearest even
    half = __builtin_ia32_vcvtps2ph(x, 0);
  97:   c5 f8 28 45 d0          vmovaps xmm0,XMMWORD PTR [rbp-0x30]
  9c:   c4 e3 79 1d c0 00       vcvtps2ph xmm0,xmm0,0x0
  a2:   c5 f8 29 45 e0          vmovaps XMMWORD PTR [rbp-0x20],xmm0
 
    // round down
    half = __builtin_ia32_vcvtps2ph(x, 1);
  ca:   c5 f8 28 45 d0          vmovaps xmm0,XMMWORD PTR [rbp-0x30]
  cf:   c4 e3 79 1d c0 01       vcvtps2ph xmm0,xmm0,0x1
  d5:   c5 f8 29 45 e0          vmovaps XMMWORD PTR [rbp-0x20],xmm0
 
    // round up
    half = __builtin_ia32_vcvtps2ph(x, 2);
  fd:   c5 f8 28 45 d0          vmovaps xmm0,XMMWORD PTR [rbp-0x30]
 102:   c4 e3 79 1d c0 02       vcvtps2ph xmm0,xmm0,0x2
 108:   c5 f8 29 45 e0          vmovaps XMMWORD PTR [rbp-0x20],xmm0
 
    // truncate
    half = __builtin_ia32_vcvtps2ph(x, 3);
 130:   c5 f8 28 45 d0          vmovaps xmm0,XMMWORD PTR [rbp-0x30]
 135:   c4 e3 79 1d c0 03       vcvtps2ph xmm0,xmm0,0x3
 13b:   c5 f8 29 45 e0          vmovaps XMMWORD PTR [rbp-0x20],xmm0

7. Způsob uložení hodnot typu half float

Hodnoty typu half float používají stejný systém zakódování hodnot, jako je tomu v případě známých datových typů single a double; pouze se snižuje počet bitů rezervovaných pro uložení mantisy i exponentu. Pro mantisu je rezervováno deset bitů a pro exponent pět bitů, přičemž posun exponentu (exponent bias) je 15 (dekadicky). Nejvyšší hodnota exponentu je rezervována pro nekonečna a NaN. Se znalostí těchto vlastností datového typu half float je snadné si vytvořit program, který provede dekódování 16bitové hodnoty zpět na hodnotu s plovoucí řádovou čárkou.

#include <stdio.h>
#include <immintrin.h>
 
void decode_half(unsigned short int half)
{
    const int exponent_bias = 15;
    const int mantissa_base = 1024;
    const int max_exponent = 31;
 
    const int mantissa_bits = 10;
    const int exponent_bits = 5;
 
    unsigned int sign = 0x01 & (half >> (mantissa_bits + exponent_bits));
    unsigned int exponent = 0x1f & (half >> mantissa_bits);
    unsigned int mantissa = 0x03ff & half;
 
    if (exponent == max_exponent) {
        printf("%c infinity\n", sign ? '-' : '+');
    } else {
        printf("%c %8.6f x 2^%-2d\n", sign ? '-' : '+',
               1.0 + (float) mantissa / mantissa_base, exponent - exponent_bias);
    }
}
 
void convert_and_decode(__v4sf x)
{
    __v8hi half;
    int i;
 
    // konverze float -> half
    half = __builtin_ia32_vcvtps2ph(x, 0);
 
    for (i = 0; i < sizeof(x) / sizeof(float); i++) {
        printf("%12.6f    %04hx    ", x[i], half[i]);
        decode_half(half[i]);
    }
}
 
int main(void)
{
    __v4sf x = { 0.0, 0.1, 1.0, 2.0 };
    convert_and_decode(x);
 
    __v4sf y = { 10000, 65400, 65504, 65600 };
    convert_and_decode(y);
 
    __v4sf z = { 1.0, -1.0, 0.01, -0.01 };
    convert_and_decode(z);
 
    __v4sf w = {0.001, 0.0001, 0.00001, 0.000001 };
    convert_and_decode(w);
 
    return 0;
}

Z výsledků je patrné například i to, které hodnoty jsou již považovány za nekonečno atd.:

    0.000000    0000    + 1.000000 x 2^-15
    0.100000    2e66    + 1.599609 x 2^-4
    1.000000    3c00    + 1.000000 x 2^0
    2.000000    4000    + 1.000000 x 2^1
10000.000000    70e2    + 1.220703 x 2^13
65400.000000    7bfc    + 1.996094 x 2^15
65504.000000    7bff    + 1.999023 x 2^15
65600.000000    7c00    + infinity
    1.000000    3c00    + 1.000000 x 2^0
   -1.000000    bc00    - 1.000000 x 2^0
    0.010000    211f    + 1.280273 x 2^-7
   -0.010000    a11f    - 1.280273 x 2^-7
    0.001000    1419    + 1.024414 x 2^-10
    0.000100    068e    + 1.638672 x 2^-14
    0.000010    00a8    + 1.164062 x 2^-15
    0.000001    0011    + 1.016602 x 2^-15

8. Využití 256bitových vektorů při převodech single/float na half float a zpět

Jen pro úplnost se podívejme na dvě zbývající instrinsic nazvané __builtin_ia32_vcvtps2ph256 a __builtin_ia32_vcvtph2ps256. Ty jsou určeny pro konverzi prvků uložených do 256bitových vektorů, což umožňuje provádět paralelní konverze osmi prvků (oběma směry):

#include <stdio.h>
#include <immintrin.h>
 
int main(void)
{
    __v8sf x = { 0.0, 0.1, 1.0, 3.14, 1e5, 1e10, 1e15, -1e10 };
    __v8hi half;
    __v8sf y;
    int i;
 
    // konverze float -> half
    half = __builtin_ia32_vcvtps2ph256(x, 0);
 
    // konverze half -> float
    y = __builtin_ia32_vcvtph2ps256(half);
 
    for (i = 0; i < sizeof(x) / sizeof(float); i++) {
        printf("%2d   %8.6g   %04hx   %f\n", i, x[i], half[i], y[i]);
    }
    return 0;
}

Hodnoty zobrazené po překladu a spuštění tohoto demonstračního příkladu:

 0          0   0000   0.000000
 1        0.1   2e66   0.099976
 2          1   3c00   1.000000
 3       3.14   4248   3.140625
 4     100000   7c00   inf
 5      1e+10   7c00   inf
 6      1e+15   7c00   inf
 7     -1e+10   fc00   -inf

Překlad do bajtkódu vypadá následovně (povšimněte si, že jeden z operandů je vždy registr YMMx):

    // konverze float -> half
    half = __builtin_ia32_vcvtps2ph256(x, 0);
  3a:   c5 fc 28 45 90          vmovaps ymm0,YMMWORD PTR [rbp-0x70]
  3f:   c4 e3 7d 1d c0 00       vcvtps2ph xmm0,ymm0,0x0
  45:   c5 f8 29 45 80          vmovaps XMMWORD PTR [rbp-0x80],xmm0
 
    // konverze half -> float
    y = __builtin_ia32_vcvtph2ps256(half);
  4a:   c5 f9 6f 45 80          vmovdqa xmm0,XMMWORD PTR [rbp-0x80]
  4f:   c4 e2 7d 13 c0          vcvtph2ps ymm0,xmm0
  54:   c5 fc 29 45 b0          vmovaps YMMWORD PTR [rbp-0x50],ymm0

9. Rozšíření FMA

Druhé rozšíření instrukční sady, se kterým se v dnešním článku setkáme, se jmenuje FMA, což je zkratka odvozená z celého názvu Fused Multiply–Add. Z tohoto názvu je možné odvodit, že v tomto rozšíření nalezneme instrukci pro provedení operace a·b + c, ale i další varianty této instrukce, v nichž se navíc mění znaménka jednotlivých operandů popř. pořadí operandů (má význam především ve chvíli, kdy je jeden z operandů uložen v paměti a nikoli v pracovním registru). Účelem tohoto rozšíření je (relativně nepatrně) zvýšit výpočetní rychlost, ale především provést výpočet bez mezizaokrouhlení výsledků. Výpočty probíhají nad typem single či double (skalární varianta) popř. nad vektory s typy single a double.

Ve skutečnosti FMA existuje ve dvou variantách – FMA3 a FMA4. V dnešním článku se zaměříme na FMA3, protože toto rozšíření je v současnosti podporováno jak čipy od AMD, tak i od Intelu (na rozdíl od FMA4, které je prozatím podporováno jen čipy od AMD).

V rozšíření FMA3 lze nalézt jen malé množství nových instrukcí, k jejichž jménům je ještě přidán postfixový kód popsaný níže:

Instrukce Stručný popis instrukce Poznámka
VFMADD x = +a · b + c  
VFNMADD x = -a · b + c N – negate
VFMSUB x = +a · b – c  
VFMADDSUB x = +a · b + c nebo x = +a · b – c viz další text
VFMSUBADD x = +a · b − c nebo x = +a · b + c viz další text

U konkrétních instrukcí se, jak jsme si již řekli, uvádí i postfixový kód, který určuje pořadí operandů. Tento postfix uvidíme i v následujících kapitolách:

Postfixový kód Význam
132 a = a · c + b
213 a = b · a + c
231 a = b · c + a
Poznámka: z tabulky je patrné, že kód uvádí pořadí operandů oproti standardnímu pořadí, které by se zapsalo postfixem 123.

10. Použití intrinsic pro instrukci VFMADD

Podívejme se nyní na použití intrinsic, která v GCC umožňuje vložení instrukce VFMADD do cílového strojového kódu. Tato instrukce zajišťuje provedení výpočtu x = +a · b + c, tedy vynásobení dvou operandů s přičtením třetího operandu, a to pochopitelně opět pro všechny prvky vektoru:

#include <stdio.h>
#include <immintrin.h>
 
void print_results(const char *title, __v4sf * a, __v4sf * b, __v4sf * c,
                   __v4sf * result)
{
    int i;
 
    puts(title);
    for (i = 0; i < sizeof(*a) / sizeof(float); i++) {
        printf("%2d  %1.0f * %1.0f + %1.0f = %1.0f\n", i, (*a)[i], (*b)[i],
               (*c)[i], (*result)[i]);
    }
 
    putchar('\n');
}
 
int main(void)
{
    __v4sf a = { 1, 2, 3, 4 };
    __v4sf b = { 2, 2, 2, 2 };
    __v4sf c = { 1, 1, 1, 1 };;
    __v4sf result;
 
    result = __builtin_ia32_vfmaddps(a, b, c);
    print_results(" #  a   b   c   result", &a, &b, &c, &result);
}

Po překladu a spuštění tohoto demonstračního příkladu se zobrazí následující (očekávané) výsledky:

 #  a   b   c   result
 0  1 * 2 + 1 = 3
 1  2 * 2 + 1 = 5
 2  3 * 2 + 1 = 7
 3  4 * 2 + 1 = 9

Z přeloženého kódu můžeme vidět, že se použila instrukce VFMADD s postfixem 231 (pořadí operandů) a s určením datového typu ps, což značí vektory hodnot typu single:

    result = __builtin_ia32_vfmaddps(a, b, c);
  f2:   c5 f8 28 45 d0          vmovaps xmm0,XMMWORD PTR [rbp-0x30]
  f7:   c5 f8 28 4d c0          vmovaps xmm1,XMMWORD PTR [rbp-0x40]
  fc:   c5 f8 28 55 b0          vmovaps xmm2,XMMWORD PTR [rbp-0x50]
 101:   c4 e2 69 b8 c1          vfmadd231ps xmm0,xmm2,xmm1
 106:   c5 f8 29 45 e0          vmovaps XMMWORD PTR [rbp-0x20],xmm0

11. Výpočet multiply-add s desetinnými hodnotami

Pro jistotu je dobré si zkontrolovat, že výpočty typu multiply-add proběhnou korektně (tedy se správnými výsledky) i v těch případech, kdy jsou ve vstupních vektorech uložena desetinná čísla. Předchozí demonstrační příklad tedy upravíme do této podoby (liší se jen hodnoty ve vektorech):

#include <stdio.h>
#include <immintrin.h>
 
void print_results(const char *title, __v4sf * a, __v4sf * b, __v4sf * c,
                   __v4sf * result)
{
    int i;
 
    puts(title);
    for (i = 0; i < sizeof(*a) / sizeof(float); i++) {
        printf("%2d  %3.1f * %3.1f + %3.1f = %3.1f\n", i, (*a)[i], (*b)[i],
               (*c)[i], (*result)[i]);
    }
 
    putchar('\n');
}
 
int main(void)
{
    __v4sf a = { 1, 2, 3, 4 };
    __v4sf b = { 0.1, 0.1, 0.1, 0.1 };
    __v4sf c = { 1, 1, 1, 1 };;
    __v4sf result;
 
    result = __builtin_ia32_vfmaddps(a, b, c);
    print_results(" #   a     b     c     result", &a, &b, &c, &result);
}

Ze zobrazených zpráv je patrné, že i tyto výpočty proběhnou korektně:

 #   a     b     c     result
 0  1.0 * 0.1 + 1.0 = 1.1
 1  2.0 * 0.1 + 1.0 = 1.2
 2  3.0 * 0.1 + 1.0 = 1.3
 3  4.0 * 0.1 + 1.0 = 1.4
Poznámka: ve skutečnosti je hodnota 0,1 u datových typů single a double velmi problematická, neboť ji nelze vyjádřit zcela přesně – ostatně proto se tyto datové typy u některých typů výpočtů (účetnictví atd.) nepoužívají.

Přeložený kód:

    result = __builtin_ia32_vfmaddps(a, b, c);
  f2:   c5 f8 28 45 d0          vmovaps xmm0,XMMWORD PTR [rbp-0x30]
  f7:   c5 f8 28 4d c0          vmovaps xmm1,XMMWORD PTR [rbp-0x40]
  fc:   c5 f8 28 55 b0          vmovaps xmm2,XMMWORD PTR [rbp-0x50]
 101:   c4 e2 69 b8 c1          vfmadd231ps xmm0,xmm2,xmm1
 106:   c5 f8 29 45 e0          vmovaps XMMWORD PTR [rbp-0x20],xmm0

12. Výpočty s maximálními hodnotami typu single/float

Zajímavé bude zjistit, jak budou výpočty probíhat ve chvíli, kdy se budou nějaké (nenulové) hodnoty násobit konstantou FLT_MAX, což je konstanta reprezentující maximální možnou hodnotu typu single/float, která ještě není považovaná za nekonečno:

#include <stdio.h>
#include <immintrin.h>
#include <float.h>
 
void print_results(const char *title, __v4sf * a, __v4sf * b, __v4sf * c,
                   __v4sf * result)
{
    int i;
 
    puts(title);
    for (i = 0; i < sizeof(*a) / sizeof(float); i++) {
        printf("%2d  %5.2g * %5.2g + %5.2g = %5.2g\n", i, (*a)[i], (*b)[i],
               (*c)[i], (*result)[i]);
    }
 
    putchar('\n');
}
 
int main(void)
{
    __v4sf a = { 0.8, 0.9, 1.0, 1.1 };
    __v4sf b = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX };
    __v4sf c = { 0, 0, 0, 0 };;
    __v4sf result;
 
    result = __builtin_ia32_vfmaddps(a, b, c);
    print_results(" #     a      b       c       result", &a, &b, &c, &result);
}

Výsledky odpovídají očekávání – první dva výsledky jsou menší než FLT_MAX, další je přesně roven FLT_MAX a poslední je již (korektně) považován za nekonečno:

 #     a      b       c       result
 0    0.8 * 3.4e+38 +     0 = 2.7e+38
 1    0.9 * 3.4e+38 +     0 = 3.1e+38
 2      1 * 3.4e+38 +     0 = 3.4e+38
 3    1.1 * 3.4e+38 +     0 =   inf
Poznámka: to znamená, že překladače mohou instrukce z rozšíření FMA3 bez problémů používat pro ty části, které by jinak používaly standardní „skalární“ matematický koprocesor.

13. Použití intrinsic pro instrukci VFNMADD

V dalším demonstračním příkladu použijeme namísto instrukce VFMADD instrukci nazvanou VFNMADD, která navíc otáčí znaménko operandu a, což znamená, že se jedinou instrukcí provedou tři aritmetické operace, a to navíc nad vektory vstupních operandů:

#include <stdio.h>
#include <immintrin.h>
 
void print_results(const char *title, __v4sf * a, __v4sf * b, __v4sf * c,
                   __v4sf * result)
{
    int i;
 
    puts(title);
    for (i = 0; i < sizeof(*a) / sizeof(float); i++) {
        printf("%2d  -%1.0f * %1.0f + %1.0f = %1.0f\n", i, (*a)[i],
               (*b)[i], (*c)[i], (*result)[i]);
    }
 
    putchar('\n');
}
 
int main(void)
{
    __v4sf a = { 1, 2, 3, 4 };
    __v4sf b = { 2, 2, 2, 2 };
    __v4sf c = { 1, 1, 1, 1 };;
    __v4sf result;
 
    result = __builtin_ia32_vfnmaddps(a, b, c);
    print_results(" #   a   b   c   result", &a, &b, &c, &result);
}

Po překladu a spuštění tohoto demonstračního příkladu si můžeme zkontrolovat, že výpočty jsou opět korektní:

 #   a   b   c   result
 0  -1 * 2 + 1 = -1
 1  -2 * 2 + 1 = -3
 2  -3 * 2 + 1 = -5
 3  -4 * 2 + 1 = -7

Intrinsic __builtin_ia32_vfnmaddps se přeloží do instrukce vfnmadd231ps (opět si povšimněte postfixu u názvu instrukce):

    result = __builtin_ia32_vfnmaddps(a, b, c);
  f2:   c5 f8 28 45 d0          vmovaps xmm0,XMMWORD PTR [rbp-0x30]
  f7:   c5 f8 28 4d c0          vmovaps xmm1,XMMWORD PTR [rbp-0x40]
  fc:   c5 f8 28 55 b0          vmovaps xmm2,XMMWORD PTR [rbp-0x50]
 101:   c4 e2 69 bc c1          vfnmadd231ps xmm0,xmm2,xmm1
 106:   c5 f8 29 45 e0          vmovaps XMMWORD PTR [rbp-0x20],xmm0

14. Použití intrinsic pro instrukci VFMSUB

Posledním demonstračním příkladem, v němž použijeme instrukce z rozšíření FMA3, je příklad používající instrukci VFMSUB, která namísto výpočtu x = +a · b + c provádí výpočet x = +a · b – c. Ostatně si to můžeme velmi snadno ověřit:

#include <stdio.h>
#include <immintrin.h>
 
void print_results(const char *title, __v4sf * a, __v4sf * b, __v4sf * c,
                   __v4sf * result)
{
    int i;
 
    puts(title);
    for (i = 0; i < sizeof(*a) / sizeof(float); i++) {
        printf("%2d  %1.0f * %1.0f - %1.0f = %1.0f\n", i, (*a)[i], (*b)[i],
               (*c)[i], (*result)[i]);
    }
 
    putchar('\n');
}
 
int main(void)
{
    __v4sf a = { 1, 2, 3, 4 };
    __v4sf b = { 2, 2, 2, 2 };
    __v4sf c = { 1, 1, 1, 1 };;
    __v4sf result;
 
    result = __builtin_ia32_vfmsubps(a, b, c);
    print_results(" #  a   b   c   result", &a, &b, &c, &result);
}

Výsledky:

 #  a   b   c   result
 0  1 * 2 - 1 = 1
 1  2 * 2 - 1 = 3
 2  3 * 2 - 1 = 5
 3  4 * 2 - 1 = 7

Strojový kód získaný překladem vypadá takto:

    result = __builtin_ia32_vfmsubps(a, b, c);
  f2:   c5 f8 28 45 d0          vmovaps xmm0,XMMWORD PTR [rbp-0x30]
  f7:   c5 f8 28 4d c0          vmovaps xmm1,XMMWORD PTR [rbp-0x40]
  fc:   c5 f8 28 55 b0          vmovaps xmm2,XMMWORD PTR [rbp-0x50]
 101:   c4 e2 69 ba c1          vfmsub231ps xmm0,xmm2,xmm1
 106:   c5 f8 29 45 e0          vmovaps XMMWORD PTR [rbp-0x20],xmm0

15. Rozšíření AVX-512

V závěrečné části dnešního článku se zmíníme o rozšíření instrukční sady nazvané AVX-512. Jak již název tohoto rozšíření částečně napovídá, jedná se o vylepšení (či možná v tomto případě spíše „vylepšení“) rozšíření AVX a AVX2 (Advanced Vector Extensions), s nímž jsme se ve stručnosti seznámili v předchozím článku. Současně nám AVX-512 naznačuje, že se šířka zpracovávaných vektorů opět rozšířila, a to z 256 bitů na celých 512 bitů. To však není vše, protože došlo i k rozšíření počtu vektorových registrů, což může vést k urychlení výpočtů, ale současně se za toto rozšíření „platí“ zdroji na mikroprocesoru (obsazená plocha na čipu + počet použitých tranzistorů):

# 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 8 32 512 bitů ZMM0 .. ZMM31

Z výše uvedené tabulky, s níž jsme se již ostatně seznámili minule, 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

16. AVX-512 jakožto několik volitelných sad rozšíření instrukcí

Ve skutečnosti přináší AVX-512 tak rozsáhlou změnu, že je rozděleno do několika podmnožin, přičemž zdaleka na všechny mikroprocesory musí podporovat všechny podmnožiny. To má zajímavý důsledek – počet možných kombinací podporuje/nepodporuje je obrovský. Detailnější popis jednotlivých množin bude uveden příště, ovšem zajímavé bude si vypsat všechny tyto skupiny instrukcí:

Množina instrukcí Plné jméno První procesor s implementací
F AVX-512 Foundation Xeon Phi x200 (Knights Landing), Xeon Gold/Platinum
CD AVX-512 Conflict Detection Instructions Xeon Phi x200 (Knights Landing), Xeon Gold/Platinum
ER AVX-512 Exponential and Reciprocal Instructions Xeon Phi x200 (Knights Landing)
PF AVX-512 Prefetch Instructions Xeon Phi x200 (Knights Landing)
     
VL AVX-512 Vector Length Extensions Skylake X, Cannon Lake
DQ AVX-512 Doubleword and Quadword Instructions Skylake X, Cannon Lake
BW AVX-512 Byte and Word Instructions Skylake X, Cannon Lake
     
IFMA AVX-512 Integer Fused Multiply Add Cannon Lake
VBMI AVX-512 Vector Byte Manipulation Instructions Cannon Lake
     
4VNNIW AVX-512 Vector Neural Network Instructions Word variable precision Knights Mill
4FMAPS AVX-512 Fused Multiply Accumulation Packed Single precision Knights Mill
     
VPOPCNTDQ Vector population count instruction Knights Mill, Ice Lake
     
VNNI AVX-512 Vector Neural Network Instructions Ice Lake
VBMI2 AVX-512 Vector Byte Manipulation Instructions 2 Ice Lake
BITALG AVX-512 Bit Algorithms Ice Lake
     
VP2INTERSECT AVX-512 Vector Pair Intersection to a Pair of Mask Registers Tiger Lake

Z tohoto důvodu najdeme v GCC C hned několik hlavičkových souborů s hlavičkami intrinsic, které je možné v souvislosti s AVX-512 použít:

avx5124fmapsintrin.h
avx5124vnniwintrin.h
avx512bitalgintrin.h
avx512bwintrin.h
avx512cdintrin.h
avx512dqintrin.h
avx512erintrin.h
avx512fintrin.h
avx512ifmaintrin.h
avx512ifmavlintrin.h
avx512pfintrin.h
avx512vbmi2intrin.h
avx512vbmi2vlintrin.h
avx512vbmiintrin.h
avx512vbmivlintrin.h
avx512vlbwintrin.h
avx512vldqintrin.h
avx512vlintrin.h
avx512vnniintrin.h
avx512vnnivlintrin.h
avx512vpopcntdqintrin.h
avx512vpopcntdqvlintrin.h
Poznámka: ve skutečnosti se prakticky vždy pracuje přímo pouze s hlavičkovým souborem immintrin.h, který korektně ostatní hlavičkové soubory načte a tím pádem nabídne i v nich uložené datové typy a hlavičky intrinsic vývojáři.

Příklad z úvodní kapitoly, který zjistil a vypsal podporovaná rozšíření instrukční sady můžeme snadno rozšířit tak, aby se vypisovaly i informace o jednotlivých podmnožinách AVX-512. Upravená varianta příkladu bude vypadat následovně (opět je nutné používat řetězcové literály, takže příklad nelze jednoduše přepsat do podoby pole+smyčky):

#include <stdio.h>
 
int main(void) {
    printf("Extension SSE    is %ssupported\n", __builtin_cpu_supports("sse") ? "" : "un");
    printf("Extension SSE2   is %ssupported\n", __builtin_cpu_supports("sse2") ? "" : "un");
    printf("Extension SSE3   is %ssupported\n", __builtin_cpu_supports("sse3") ? "" : "un");
    printf("Extension SSE4.1 is %ssupported\n", __builtin_cpu_supports("sse4.1") ? "" : "un");
    printf("Extension SSE4.2 is %ssupported\n", __builtin_cpu_supports("sse4.2") ? "" : "un");
    printf("Extension AVX    is %ssupported\n", __builtin_cpu_supports("avx") ? "" : "un");
    printf("Extension AVX2   is %ssupported\n", __builtin_cpu_supports("avx2") ? "" : "un");
    printf("Extension FMA    is %ssupported\n", __builtin_cpu_supports("fma") ? "" : "un");
    printf("Extension FMA4   is %ssupported\n", __builtin_cpu_supports("fma4") ? "" : "un");
    putchar('\n');
 
    printf("Extension AVX512F         is %ssupported\n", __builtin_cpu_supports("avx512f") ? "" : "un");
    printf("Extension AVX512VL        is %ssupported\n", __builtin_cpu_supports("avx512vl") ? "" : "un");
    printf("Extension AVX512BW        is %ssupported\n", __builtin_cpu_supports("avx512bw") ? "" : "un");
    printf("Extension AVX512DQ        is %ssupported\n", __builtin_cpu_supports("avx512dq") ? "" : "un");
    printf("Extension AVX512CD        is %ssupported\n", __builtin_cpu_supports("avx512cd") ? "" : "un");
    printf("Extension AVX512ER        is %ssupported\n", __builtin_cpu_supports("avx512er") ? "" : "un");
    printf("Extension AVX512PF        is %ssupported\n", __builtin_cpu_supports("avx512pf") ? "" : "un");
    printf("Extension AVX512VBMI      is %ssupported\n", __builtin_cpu_supports("avx512vbmi") ? "" : "un");
    printf("Extension AVX512IFMA      is %ssupported\n", __builtin_cpu_supports("avx512ifma") ? "" : "un");
    printf("Extension AVX5124VNNIW    is %ssupported\n", __builtin_cpu_supports("avx5124vnniw") ? "" : "un");
    printf("Extension AVX5124FMAPS    is %ssupported\n", __builtin_cpu_supports("avx5124fmaps") ? "" : "un");
    printf("Extension AVX512VPOPCNTDQ is %ssupported\n", __builtin_cpu_supports("avx512vpopcntdq") ? "" : "un");
    printf("Extension AVX512VBMI2     is %ssupported\n", __builtin_cpu_supports("avx512vbmi2") ? "" : "un");
    printf("Extension AVX512BITALG    is %ssupported\n", __builtin_cpu_supports("avx512bitalg") ? "" : "un");
    return 0;
}

V mém konkrétním případě není ani jedna podmnožina AVX-512 podporována, na což se můžeme dívat ze dvou úhlů – jedná se o čip, který „neumí vše“ a „je z pravěku“, ovšem na druhou stranu jsem neplatil za relativně velkou plochu čipu (=tranzistory), které by stejně v naprosté většině aplikací nebyly použity (výjimkou mohou být snad jen kodeky – a i to je možná příliš optimistické očekávání, že budou masivně optimalizovány na všechny možné varianty rozšíření instrukčních sad):

Extension SSE    is supported
Extension SSE2   is supported
Extension SSE3   is supported
Extension SSE4.1 is supported
Extension SSE4.2 is supported
Extension AVX    is supported
Extension AVX2   is supported
Extension FMA    is supported
Extension FMA4   is unsupported
 
Extension AVX512F         is unsupported
Extension AVX512VL        is unsupported
Extension AVX512BW        is unsupported
Extension AVX512DQ        is unsupported
Extension AVX512CD        is unsupported
Extension AVX512ER        is unsupported
Extension AVX512PF        is unsupported
Extension AVX512VBMI      is unsupported
Extension AVX512IFMA      is unsupported
Extension AVX5124VNNIW    is unsupported
Extension AVX5124FMAPS    is unsupported
Extension AVX512VPOPCNTDQ is unsupported
Extension AVX512VBMI2     is unsupported
Extension AVX512BITALG    is unsupported

17. Možné režimy SIMD s přihlédnutím k možnostem AVX-512

Mikroprocesory, které podporují (i když pouze částečně) AVX-512, mohou zpracovávat hodnoty uložené ve vektorech ve více „SIMD režimech“. Pokud budeme na chvíli ignorovat MMX a 3DNow!, jedná se o tyto režimy:

Jméno Rozšíření Použité registry Datové typy
SSE SSE-SSE4.2 XMM0–XMM15 single/float, SSE2: byte, word, doubleword, quadword, double
AVX-128 (VEX) AVX, AVX2 XMM0–XMM15 byte, word, doubleword, quadword, single/float, double
AVX-256 (VEX) AVX, AVX2 YMM0–YMM15 single/float, double. From AVX2: byte, word, doubleword, quadword
AVX-128 (EVEX) AVX-512VL XMM0–XMM31 doubleword, quadword, single/float, double. AVX512BW: byte, word. AVX512-FP16: half float
AVX-256 (EVEX) AVX-512VL YMM0–YMM31 doubleword, quadword, single/float, double. AVX512BW: byte, word. AVX512-FP16: half float
AVX-512 (EVEX) AVX-512F ZMM0–ZMM31 doubleword, quadword, single/float, double. AVX512BW: byte, word. AVX512-FP16: half float
Poznámka: počty registrů jsou platné pro architekturu x86–64, nikoli nutně pro x86.

18. Obsah závěrečného článku o SIMD

V závěrečném článku o SIMD operacích podporovaných (i když nepřímo) překladačem GCC C se zaměříme přímo na ty instrukce, které lze nalézt v jednotlivých rozšířeních instrukčních sad AVX-512.

root_podpora

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ž 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 test_extensions.c test, které instrukční sady mikroprocesor podporuje https://github.com/tisnik/pre­sentations/blob/master/SIM­D/test_extensions.c
43 test_avx512_extensions.c test, které instrukční sady mikroprocesor podporuje, rozšíření o AVX-512 https://github.com/tisnik/pre­sentations/blob/master/SIM­D/test_avx512_extensions
       
44 intrinsic_f16c1.c převod 128bitového vektoru s prvky typu float na half a zpět https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_f16c1.c
45 intrinsic_f16c2.c převod 128bitového vektoru s prvky typu float na half a zpět, zpracování velkých hodnot https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_f16c2.c
46 intrinsic_f16c3.c převod 128bitového vektoru s prvky typu float na half s volbou režimu zaokrouhlení https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_f16c3.c
47 intrinsic_f16c4.c převod 256bitového vektoru s prvky typu float na half a zpět https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_f16c4.c
48 intrinsic_fma3_1.c využití instrukce pro provedení vektorové operace x = a*b+c https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_fma3_1.c
49 intrinsic_fma3_2.c dtto, ale pro odlišné hodnoty https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_fma3_2.c
50 intrinsic_fma3_3.c dtto, ale vynásobení maximálními možnými FP hodnotami https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_fma3_3.c
51 intrinsic_fma3_4.c využití instrukce pro provedení vektorové operace x = -a*b+c https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_fma3_4.c
52 intrinsic_fma3_5.c využití instrukce pro provedení vektorové operace x = a*b-c https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_fma3_5.c
       
53 decode_half_float.c dekódování hodnot s plovoucí řádovou čárkou uložených ve formátu half float https://github.com/tisnik/pre­sentations/blob/master/SIM­D/decode_half_float.c
       
54 Makefile Makefile pro překlad všech výše uvedených 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
       
60 intrinsic_f16c1.lst překlad zdrojového kódu intrinsic_f16c1.c s přepínačem https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_f16c1.lst
61 intrinsic_f16c2.lst překlad zdrojového kódu intrinsic_f16c2.c s přepínačem https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_f16c2.lst
62 intrinsic_f16c3.lst překlad zdrojového kódu intrinsic_f16c3.c s přepínačem https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_f16c3.lst
63 intrinsic_f16c4.lst překlad zdrojového kódu intrinsic_f16c4.c s přepínačem https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_f16c4.lst
64 intrinsic_fma3_1.lst překlad zdrojového kódu intrinsic_fma3_1.c s přepínačem https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_fma3_1.lst
65 intrinsic_fma3_2.lst překlad zdrojového kódu intrinsic_fma3_2.c s přepínačem https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_fma3_2.lst
66 intrinsic_fma3_3.lst překlad zdrojového kódu intrinsic_fma3_3.c s přepínačem https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_fma3_3.lst
67 intrinsic_fma3_4.lst překlad zdrojového kódu intrinsic_fma3_4.c s přepínačem https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_fma3_4.lst
68 intrinsic_fma3_5.lst překlad zdrojového kódu intrinsic_fma3_5.c s přepínačem https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_fma3_5.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
  62. Why Intel is betting on BFLOAT16 to be a game changer for deep learning training? Hint: Range trumps Precision
    https://hub.packtpub.com/why-intel-is-betting-on-bfloat16-to-be-a-game-changer-for-deep-learning-training-hint-range-trumps-precision/
  63. half-rs (pro Rust)
    https://github.com/starkat99/half-rs
  64. float16 (pro Go)
    https://github.com/x448/float16
  65. bfloat16 – Hardware Numerics Definition
    https://software.intel.com/en-us/download/bfloat16-hardware-numerics-definition
  66. Intel Prepares To Graft Google’s Bfloat16 Onto Processors
    https://www.nextplatform.com/2019/07/15/in­tel-prepares-to-graft-googles-bfloat16-onto-processors/
  67. A Study of BFLOAT16 for Deep Learning Training
    https://arxiv.org/pdf/1905.12322.pdf
  68. BFloat16s.jl
    https://github.com/JuliaCom­puting/BFloat16s.jl
  69. Half Precision Arithmetic: fp16 Versus bfloat16
    https://nhigham.com/2018/12/03/half-precision-arithmetic-fp16-versus-bfloat16/
  70. bfloat16 floating-point format (Wikipedia)
    https://en.wikipedia.org/wi­ki/Bfloat16_floating-point_format
  71. Unum (number format)
    https://en.wikipedia.org/wi­ki/Unum_(number_format)#Po­sit
  72. Performance Benefits of Half Precision Floats
    https://software.intel.com/en-us/articles/performance-benefits-of-half-precision-floats
  73. Norma IEEE 754 a příbuzní: formáty plovoucí řádové tečky
    https://www.root.cz/clanky/norma-ieee-754-a-pribuzni-formaty-plovouci-radove-tecky/
  74. IEEE-754 Floating-Point Conversion
    http://babbage.cs.qc.cuny.edu/IEEE-754.old/32bit.html
  75. Small Float Formats
    https://www.khronos.org/o­pengl/wiki/Small_Float_For­mats
  76. Binary-coded decimal
    https://en.wikipedia.org/wiki/Binary-coded_decimal
  77. Floating-Point Formats
    http://www.quadibloc.com/com­p/cp0201.htm
  78. Data types (SciPy)
    https://docs.scipy.org/doc/numpy-1.13.0/user/basics.types.html
  79. New 16-bit floating point type – NumPy 1.6.0 Release Notes
    https://github.com/numpy/num­py/blob/7cfec2403486456b52b525ec­cf7541e1562d9ab3/doc/rele­ase/1.6.0-notes.rst#new-16-bit-floating-point-type
  80. RFC pro Rust
    https://github.com/joshtri­plett/rfcs/blob/f16b/text/0000-f16b.md
  81. IEEE-754 Floating Point Converter
    https://www.h-schmidt.net/FloatConverter/I­EEE754.html
  82. Mediump float calculator
    https://oletus.github.io/float16-simulator.js/
  83. IEEE 754 Calculator
    http://weitz.de/ieee/
  84. BFloat16 (Swift for TensorFlow)
    https://www.tensorflow.or­g/swift/api_docs/Structs/BFlo­at16
  85. Using bfloat16 with TensorFlow models
    https://cloud.google.com/tpu/doc­s/bfloat16
  86. What is tf.bfloat16 “truncated 16-bit floating point”?
    https://stackoverflow.com/qu­estions/44873802/what-is-tf-bfloat16-truncated-16-bit-floating-point
  87. BFloat16 processing for Neural Networks on Armv8-A
    https://community.arm.com/developer/ip-products/processors/b/ml-ip-blog/posts/bfloat16-processing-for-neural-networks-on-armv8_2d00_a
  88. Mixed precision training
    https://arxiv.org/pdf/1710.03740.pdf
  89. [R] Mixed Precision Training
    https://www.reddit.com/r/Machi­neLearning/comments/75phd2/r_mi­xed_precision_training/
  90. Floating Point Numbers
    https://floating-point-gui.de/formats/fp/
  91. Float exposed
    https://float.exposed/0×40490000
  92. Float Toy
    http://evanw.github.io/float-toy/
  93. IEEE-754 visualization
    https://bartaz.github.io/ieee754-visualization/
  94. Advantages Of BFloat16 For AI Inference
    https://semiengineering.com/advantages-of-bfloat16-for-ai-inference/
  95. ARMv8-A bude podporovat nový formát čísel BFloat16
    https://www.root.cz/zpravicky/armv8-a-bude-podporovat-novy-format-cisle-bfloat16/
  96. Intel oznámil nový formát BFloat16 pro budoucí procesory
    https://www.root.cz/zpravicky/intel-oznamil-novy-format-bfloat16-pro-budouci-procesory/
  97. Nový formát čísel Intelu BFloat16 bude v GCC 10 a Clang 9
    https://www.root.cz/zpravicky/novy-format-cisel-intelu-bfloat16-bude-v-gcc-10-a-clang-9/
  98. Mixed precision
    https://www.tensorflow.or­g/guide/keras/mixed_preci­sion
  99. Training Performance: A user’s guide to converge faster (TensorFlow Dev Summit 2018)
    https://www.youtube.com/wat­ch?v=SxOsJPaxHME
  100. Programování GPU na Raspberry Pi: použití Quad Processor Unit(s)
    https://www.root.cz/clanky/pro­gramovani-gpu-na-raspberry-pi-pouziti-quad-processor-unit-s/
  101. “Half Precision” 16-bit Floating Point Arithmetic
    https://blogs.mathworks.com/cle­ve/2017/05/08/half-precision-16-bit-floating-point-arithmetic/
  102. Half Precision Arithmetic in Numerical Linear Algebra
    https://nla-group.org/2018/10/03/half-precision-arithmetic-in-numerical-linear-algebra/
  103. Enable BF16 support
    https://gcc.gnu.org/ml/gcc-patches/2019–04/msg00477.html
  104. Survey of Floating-Point Formats
    https://mrob.com/pub/math/flo­atformats.html
  105. VCVTPS2PH — Convert Single-Precision FP value to 16-bit FP value
    https://www.felixcloutier­.com/x86/vcvtps2ph
  106. VCVTPH2PS — Convert 16-bit FP values to Single-Precision FP values
    https://www.felixcloutier­.com/x86/vcvtph2ps

Byl pro vás článek přínosný?