Hlavní navigace

Podpora SIMD operací v GCC s využitím intrinsic pro nízkoúrovňové optimalizace

20. 10. 2022
Doba čtení: 52 minut

Sdílet

 Autor: Depositphotos
V první části článku dokončíme popis problematiky SIMD technologie NEON na architektuře ARM. Část druhá bude věnována takzvaným intrinsic, které programátorům umožňují přímo v C provádět i nízkoúrovňové optimalizace.

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 floatdouble

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

20. Odkazy na Internetu

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ů

Poznámka na úvod: všechny příklady pro AArch64 byly otestovány na počítači s tímto čtyřjádrovým čipem:
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]
    }
Poznámka: povšimněte si, že se vždy pracuje se shodnými pracovními registry V0 a V1 a dokonce i operační kódy instrukcí se odlišují pouze v několika bitech (a to díky poměrně promyšlenému návrhu instrukční sady mikroprocesorů AArch64):
  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 floatdouble

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í).

Poznámka: v tomto ohledu jsou na tom lépe překladače Fortranu a taktéž programovacího jazyka Julia.

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.

Poznámka: některé intrinsic pro GCC jsou zmíněny zde.

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]);
    }
}
Poznámka: při překladu není nutné specifikovat žádné speciální přepínače a už vůbec ne linkovat nějakou knihovnu – intrinsic jsou skutečně součástí překladače.

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
Poznámka: zde jsou vypnuty optimalizace, takže součet je realizován třemi instrukcemi a nevyužívá se zde faktu, že některé hodnoty jsou již umístěny v pracovních registrech.

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
Poznámka: pro operace bez znaménka existuje podobná instrukce paddusb.

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
Poznámka: mimochodem si povšimněte, že se jedná o operace s dvojicí pracovních registrů MMX (indexovaných od nuly do sedmi) a samotná instrukce v takovém případě zabírá pouze tři bajty v paměti. To – zvláště když si uvědomíme, jak chaotický byl vývoj instrukční sady x86/x86–64 – není mnoho a za použití MMX instrukcí tedy neplatíme příliš velkou cenu v podobě rozsáhlého objektového kódu (který klade větší nároky na cache).

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/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ář:

root_podpora

# 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 Makefile Makefile pro překlad demonstračních příkladů https://github.com/tisnik/pre­sentations/blob/master/SIM­D/Makefile

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

# Příklad Stručný popis Adresa
1 simd04_1.lst překlad zdrojového kódu simd04_1.c s přepínači -O0 -mno-sse -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd04_1.lst
2 simd04_2.lst překlad zdrojového kódu simd04_2.c s přepínači -O0 -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd04_2.lst
3 simd04B1.lst překlad zdrojového kódu simd04B1.c s přepínači -O0 -mno-sse -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd04B1.lst
4 simd04B2.lst překlad zdrojového kódu simd04B2.c s přepínači -O0 -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd04B2.lst
5 simd07_1.lst překlad zdrojového kódu simd07_1.c s přepínači -mno-sse -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd07_1.lst
6 simd07_2.lst překlad zdrojového kódu simd07_2.c s přepínači -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd07_2.lst
7 simd08_1.lst překlad zdrojového kódu simd08_1.c s přepínači -mno-sse -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd08_1.lst
8 simd08_2.lst překlad zdrojového kódu simd08_2.c s přepínači -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd08_2.lst
9 simd12_1.lst překlad zdrojového kódu simd12_1.c s přepínači -O0 -mno-sse -g  https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd12_1.lst
10 simd12_2.lst překlad zdrojového kódu simd12_2.c s přepínači -O0 -g  https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd12_2.lst
11 simd13_1.lst překlad zdrojového kódu simd13_1.c s přepínači -O0 -mno-sse -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd13_1.lst
12 simd13_2.lst překlad zdrojového kódu simd13_2.c s přepínači -O0 -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd13_2.lst
13 simd13_3.lst překlad zdrojového kódu simd13_3.c s přepínači -O3 -mno-sse -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd13_3.lst
14 simd13_4.lst překlad zdrojového kódu simd13_4.c s přepínači -O3 -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd13_4.lst
15 simd14_1.lst překlad zdrojového kódu simd14_1.c s přepínači -O0 -mno-sse -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd14_1.lst
16 simd14_2.lst překlad zdrojového kódu simd14_2.c s přepínači -O0 -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd14_2.lst
17 simd14_3.lst překlad zdrojového kódu simd14_3.c s přepínači -O3 -mno-sse -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd14_3.lst
18 simd14_4.lst překlad zdrojového kódu simd14_4.c s přepínači -O3 -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd14_4.lst
19 simd15_1.lst překlad zdrojového kódu simd15_1.c s přepínači -O0 -mno-sse -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd15_1.lst
20 simd15_2.lst překlad zdrojového kódu simd15_2.c s přepínači -O0 -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd15_2.lst
21 simd15_3.lst překlad zdrojového kódu simd15_3.c s přepínači -O3 -mno-sse -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd15_3.lst
22 simd15_4.lst překlad zdrojového kódu simd15_4.c s přepínači -O3 -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd15_4.lst
23 simd16_1.lst překlad zdrojového kódu simd16_1.c s přepínači -O0 -mno-sse -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd16_1.lst
24 simd16_2.lst překlad zdrojového kódu simd16_2.c s přepínači -O0 -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd16_2.lst
25 simd16_3.lst překlad zdrojového kódu simd16_3.c s přepínači -O3 -mno-sse -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd16_3.lst
26 simd16_4.lst překlad zdrojového kódu simd16_4.c s přepínači -O3 -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd16_4.lst
27 simd17_1.lst překlad zdrojového kódu simd17_1.c s přepínači -O0 -mno-sse -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd17_1.lst
28 simd17_2.lst překlad zdrojového kódu simd17_2.c s přepínači -O0 -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd17_2.lst
29 simd17_3.lst překlad zdrojového kódu simd17_3.c s přepínači -O3 -mno-sse -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd17_3.lst
30 simd17_4.lst překlad zdrojového kódu simd17_4.c s přepínači -O3 -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd17_4.lst
31 simd18_1.lst překlad zdrojového kódu simd18_1.c s přepínači -O0 -mno-sse -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd18_1.lst
32 simd18_2.lst překlad zdrojového kódu simd18_2.c s přepínači -O0 -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd18_2.lst
33 simd18_3.lst překlad zdrojového kódu simd18_3.c s přepínači -O3 -mno-sse -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd18_3.lst
34 simd18_4.lst překlad zdrojového kódu simd18_4.c s přepínači -O3 -g https://github.com/tisnik/pre­sentations/blob/master/SIM­D/simd18_4.lst
       
35 intrinsic_mmx1.lst překlad zdrojového kódu intrinsic_mmx1.c https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_mmx1.lst
36 intrinsic_mmx2.lst překlad zdrojového kódu intrinsic_mmx2.c https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_mmx2.lst
37 intrinsic_mmx3.lst překlad zdrojového kódu intrinsic_mmx3.c https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_mmx3.lst
39 intrinsic_mmx5.lst překlad zdrojového kódu intrinsic_mmx5.c https://github.com/tisnik/pre­sentations/blob/master/SIM­D/intrinsic_mmx5.lst

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/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 -march=armv8-a+nosimd -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 -march=armv8-a+nosimd -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 -march=armv8-a+nosimd -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 -march=armv8-a+nosimd -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 -march=armv8-a+nosimd -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 -march=armv8-a+nosimd -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 -march=armv8-a+nosimd -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 -march=armv8-a+nosimd -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 -march=armv8-a+nosimd -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 -march=armv8-a+nosimd -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 -march=armv8-a+nosimd -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 -march=armv8-a+nosimd -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 -march=armv8-a+nosimd -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 -march=armv8-a+nosimd -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 -march=armv8-a+nosimd -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 -march=armv8-a+nosimd -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

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

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