Hlavní navigace

Instrukční sada AArch64: technologie NEON

11. 7. 2017
Doba čtení: 29 minut

Sdílet

Jednou z nejdůležitějších a potenciálně nejužitečnějších vlastností mikroprocesorových jader ARMv8-A s instrukční sadou AArch64 je upravená a v několika směrech vylepšená technologie NEON umožňující provádění SIMD operací.

Obsah

1. Instrukční sada AArch64 (3.část – technologie NEON)

2. Úspěšná a užitečná kombinace RISC + SIMD

3. Od technologie VFP k Advanced SIMD (NEON)

4. Podpora technologie NEON v jádrech AArch64

5. Registry používané instrukcemi NEON

6. Použitá terminologie: vector, lane, element

7. Podporované formáty prvků vektorů

8. Práce se skalárními daty

9. Formát instrukcí NEON, prefixy a suffixy u instrukcí

10. Konverze operandů (rozšíření, zmenšení), operace se saturací

11. Příklad různých variant instrukce ADD

12. Typy podporovaných vektorových instrukcí

13. Aritmetické a logické instrukce

14. Instrukce pro provedení jednoho kroku delší operace

15. Podpora SIMD instrukcí ve vyšších programovacích jazycích

16. Odkazy na Internetu

1. Instrukční sada AArch64 (3.část – technologie NEON)

Prakticky každá významnější společnost (v případě mikroprocesorů řady PowerPC pak dokonce aliance) navrhující mikroprocesory s architekturou RISC přišla dříve či později na trh s instrukční sadou obsahující „vektorové“ instrukce, které jsou dnes souhrnně označovány zkratkou SIMD (původní vektorové instrukce používané na superpočítačích jsou v některých ohledech flexibilnější, proto budeme v dnešním článku používat spíše poněkud přesnější zkratku SIMD znamenající „single instruction – multiple data“). Rozšiřující instrukční sady byly pojmenovávány nejrůznějšími názvy a zkratkami a nikdy vlastně nedošlo – na rozdíl od platformy x86 – ke sjednocení těchto instrukcí do jediné skupiny „SIMD pro RISC“, což je vlastně logické, protože procesory RISC jsou mnohdy určeny pro specializované oblasti použití, od vestavných (embedded) systémů přes smartphony a tablety až po superpočítače.

Nejvýznamnější implementace instrukcí typu SIMD na mikroprocesorech s architekturou RISC, ať již se jedná o instrukce určené pro operace s celými čísly či s čísly reálnými (přesněji řečeno s plovoucí řádovou čárkou), jsou vypsány v následující tabulce:

# Zkratka/název Plný název Rodina procesorů
1 MAX-1 Multimedia Acceleration eXtensions v1 HP-PA RISC
2 MAX-2 Multimedia Acceleration eXtensions v2 HP-PA RISC
3 VIS 1 Visual Instruction v1 Set SPARC V9
4 VIS 2 Visual Instruction v2 Set SPARC V9
5 AltiVec (obchodní názvy Velocity Engine, VMX) PowerPC
6 MDMX MIPS Digital Media eXtension (MaDMaX) MIPS
7 MIPS-3D MIPS-3D MIPS
8 MVI Motion Video Instructions DEC Alpha
9 NEON Advanced SIMD Cortex (ARMv7, ARMv8)
10 Packed SIMD Packed SIMD (není finalizováno) RISC-V
11 Vector Set Vector Set (není finalizováno) RISC-V

2. Úspěšná a užitečná kombinace RISC + SIMD

Důvodů, proč se instrukce typu SIMD na RISCových procesorech vůbec objevily, je větší množství. Jedním z nich je to, že se tyto procesory začaly používat v grafických pracovních stanicích, mj. i pro zpracování videa, provádění rastrových operací a v některých případech i 3D operací, což je přesně ta oblast, v níž je možné informace zpracovávat nikoli jen ve formě skalárních dat, ale i jako vektory pevné délky (u 3D operací se typicky jedná o vektory a matice). Dalším důvodem byla snaha výrobců RISCových procesorů o průnik na trh s počítači určenými pro náročné výpočty (jedná se o určitý mezistupeň mezi výkonnými pracovními stanicemi a superpočítači, v oblasti superpočítačů se však většinou používají XEONy, s výjimkami projektů typu Flagship2020, které sází právě na nová ARMovská jádra).

V tomto oboru se mnoho algoritmů provádí nad maticemi a vektory obsahujícími numerické hodnoty reprezentované v systému plovoucí řádové čárky (FP: Floating Point). Třetím důvodem je samozřejmě snaha o zvýšení výpočetního výkonu a právě SIMD instrukce k němu mohou vést, aniž by bylo nutné radikálně měnit používanou výrobní technologii čipů (zvyšovat úroveň integrace, snižovat napěťové úrovně či zvyšovat frekvenci, popř. přidávat drahé vyrovnávací paměti/cache).

Poznámka: instrukce SIMD představují pouze jednou z možností zvýšení výpočetního výkonu mikroprocesorů. Další variantou jsou – podle používané Flynnovy klasifikace sekvenčních a paralelních systémů – instrukce/architektura MISD využívaná ve specializovaných aplikacích či nejobecnější architektura MIMD. Ve více než padesátileté historii vývoje výpočetní techniky se již objevily všechny čtyři možné kombinace instrukčního a datového paralelismu. Viz též následující tabulka:

Zkratka klasifikace Anglický význam zkratky Využití systémů s danou klasifikací
SISD Single Instruction Stream, Single Data klasická architektura pro procesory CISC a RISC, stále používána, nejjednodušší pro programátory i překladače
SIMD Single Instruction Stream, Multiple Data vektorové procesory, GPU, procesory s instrukční sadou SSE/MMX…
MISD Multiple Instructions Stream, Single Data Stream poměrně speciální případy, řídicí počítače raketoplánů (Space Shuttle)
MIMD Multiple Instructions Stream, Multiple Data Stream Connection Machine, transputery, symetrické multiprocesory

3. Od technologie VFP k Advanced SIMD (NEON)

Pokud se podíváme na historii mikroprocesorů ARM, zjistíme, že cesta k technologii NEON na jádrech AArch64 vlastně nebyla vůbec přímočará. První implementace „vektorových“ operací pro procesory ARM používaly rozhraní pro koprocesory, takže se vlastně používala paralelní/doplňková instrukční sada. Konkrétně se jednalo o technologii nazvanou VFP neboli Vector Floating Point. Touto technologií, která je stále na některých ARMovských jádrech podporována, jsme se již v seriálu o architekturách počítačů zabývali. Na tomto místě je vhodné zdůraznit, že i přesto, že se v názvu VFP používá termín „vector“, nejednalo se o implementaci skutečných SIMD operací, protože se prvky vektorů zpracovávaly postupně, tedy sekvenčně (stále se však jednalo o vylepšení, protože se ušetřilo načtení instrukce a její dekódování). Z tohoto důvodu byl „vektorový režim“ VFP poměrně rychle nahrazen novou technologií nazvanou NEON označovanou též Advanced SIMD. Dnes se s VFP můžeme na některých ARMovských jádrech setkat, další jádra pak podporují jen VFPLite, kde však každá operace trvá zhruba deset strojových cyklů!

Technologie NEON již podporovala plnohodnotné SIMD operace, při použití vektorů s nejmenšími prvky o velikosti jednoho bajtu až šestnáct operací (například součtu) paralelně. Díky tomu bylo možné implementovat například dekodér pro známý formát MP3 na mikroprocesoru s taktem pouhých 10 MHz, popř. implementovat AMR kodek na podobném čipu, ovšem s hodinovým taktem 13 MHz. Při použití klasických výpočtů se skalárními hodnotami by bylo nutné použít čip s vyšší hodinovou frekvencí či naopak – tento výkonný čip by již neměl dostatek výkonu pro provádění dalších činností. Pro ukázku: ještě mikroprocesory 486DX2 s hodinovou frekvencí 66 MHz měly s dekódováním MP3 velké problémy a zvládaly jen menší bitrate a monofonní výstup). Právě díky NEONu se začaly čipy ARM používat i v některých oblastech, které byly dříve vyhrazeny digitálním signálovým procesorům.

Mimo VFP a NEON bylo pro některá (dnes již notně stará) jádra ARM vyvinuto rozšíření pro DSP operace. Konkrétně se jednalo o jádra ARMv5TE. Nové instrukce byly využitelné například při kódování a dekódování videa, při zpracování zvukového signálu (včetně zvukové syntézy), práci s rastrovými obrazy (image processing) atd. Instrukce typu Load & Store mohly pracovat s registrovými páry, zavedeny byly nové adresovací režimy, aritmetika se saturací (tj. bez přetečení) a taktéž instrukce typu „multiply and accumulate (MAC)“ 16×16 bitů a 32×16 bitů, které mohly být v instrukční pipeline vykonány v rozmezí jednoho taktu (v jejich průběhu se tedy mohly začít zpracovávat další instrukce). Uvádí se, že při zpracování signálů byla výkonnost nových instrukcí v jádrech ARMv5TE dvakrát až třikrát vyšší, než při použití „běžných“ jader ARMv5 (samozřejmě za předpokladu ruční optimalizace kódu, což ostatně až na některé výjimky platí dodnes, protože intrinsic nejsou samospasitelné).

Další odbočkou byla technologie SIMD extensions for multimedia pro jádra ARMv6, která byla později nahrazena Advanced SIMD (NEONem).

Poznámka: skutečná míra paralelnosti SIMD operací se na různých ARM jádrech lišila. Například na Cortex-A8 se sice stále daly provádět operace se 128bitovými registry obsahujícími prvky vektorů (viz navazující kapitoly), ovšem ve skutečnosti se v daný okamžik zpracovávalo jen 64 bitů, tj. každá operace se musela provádět dvakrát. Naproti tomu na Cortex-15 se již zpracovávaly celé 128bitové registry.

4. Podpora technologie NEON v jádrech AArch64

Podle specifikace by všechny standardní implementace jader ARMv8 (s instrukční sadou AArch64) měly technologii NEON podporovat, stejně jako operace s FP hodnotami (jinými slovy – už není nutné řešit například problém „hardfp versus softfp“). Ovšem v budoucnosti se pravděpodobně setkáme i se specializovanými jádry nakonfigurovanými odlišným způsobem, například:

  • NEON nebude vůbec podporován.
  • FP operace nebudou vůbec podporovány.
  • NEON+FP budou podporovány, ale bez zpracování výjimek u FP operací.

5. Registry používané instrukcemi NEON

Původní technologie NEON, která byla implementovaná na 32bitových jádrech ARM, používala sadu třiceti dvou nových pracovních registrů, přičemž každý registr měl šířku 64 bitů. Tato sada byla oddělena od klasických celočíselných pracovních registrů, což samozřejmě zvýšilo možnosti překladače při optimalizacích kódu. Nové registry byly pojmenovány D0D31 (D = double), popř. mohly být vždy dva sousední registry spojeny do jednoho 128bitového registru (ty byly pojmenovány Q0Q15, Q = quad). Pokud jádro kromě NEONu podporovalo i výše zmíněnou VFP (konkrétně VFPv3 nebo VFPv4), byly registry D0D31 sdíleny mezi oběma jednotkami.

U jader ARMv8-A s instrukční sadou AArch64 došlo v této oblasti k vylepšení, protože programátoři nově mají k dispozici 32 pracovních registrů, ovšem nyní se jedná o plnohodnotné 128bitové registry. Tyto registry jsou současně používány i při běžných matematických operacích s hodnotami uloženými v systému plovoucí řádové čárky a taktéž kryptografickým modulem. Došlo ještě k další změně – již nedochází k rozdělení jednoho registru pro typ double do dvou registrů pro hodnotu typu single/float, jako tomu bylo u VFP. Nově je možné každý registr použít jako 128bitový vektor, pro uložení hodnoty double (spodních 64 bitů), uložení hodnoty typu single/float (spodních 32 bitů) popř. pro uložení hodnoty typu half float (spodních 16 bitů). Horní bity jsou při čtení ignorovány, při zápisu nulovány:

Jméno Význam
v0..v31 128bitové registry
d0..d31 spodních 64 bitů registrů v0..v31, použito pro hodnoty typu double
s0..s31 spodních 32 bitů registrů v0..v31, použito pro hodnoty typu single/float
h0..h31 spodních 16 bitů registrů v0..v31, použito pro hodnoty typu half float

Poznámka: datový typ half float o šířce pouhých šestnácti bitů je podporován jen několika instrukcemi. Nejedná se totiž o formát určený primárně pro zpracování hodnot, ale pouze o formát pro načtení či naopak uložení výsledků. S tímto formátem se můžeme setkat u grafických akcelerátorů či v OpenCL. Zde má použití half float svůj význam, protože u některých algoritmů se úzkým hrdlem výpočtu stává přenosová rychlost sběrnice, rychlost přístupu do operační paměti či výpadky cache (a u pole prvků typu half float bude výpadků méně, než u prvků typu single/float či dokonce double).

6. Použitá terminologie: vector, lane, element

U technologie NEON se používá následující terminologie:

  1. Vector vždy značí 64bitovou či 128bitovou část pracovního registru Vn, která je rozdělena na prvky.
  2. Element je prvek vektoru.
  3. Lane označuje index prvku vektoru. U mnoha operací se kombinují prvky z různých vektorů, které mají shodný index.

Indexy jednotlivých lanes se zvyšují směrem od nejnižšího bitu k bitu nejvyššímu. Pro pracovní registry V0V31 a použitý typ prvků mohou indexy nabývat těchto hodnot:

Vektor Indexy jednotlivých lanes
128bitový registr ×
2×64 bitů 1, 0
4×32 bitů 3, 2, 1, 0
8×16 bitů 7, 6, ..0
16×8 bitů 15..0

Víme již, že je možné namísto 128bitových vektorů používat i vektory 64bitové, tj. registrové aliasy D0D31. Zde samozřejmě bude k dispozici jen polovina indexů:

Vektor Indexy jednotlivých lanes
64bitový registr ×
1×64 bitů 0
2×32 bitů 1, 0
4×16 bitů 3, 2, 1, 0
8×8 bitů 7, 6, ..0

7. Podporované formáty prvků vektorů

Instrukce NEON na 64bitových mikroprocesorech AArch64 podporují vektory s elementy (prvky) těchto typů:

Typ Šířka Poznámka
float 32 bitů hodnota s plovoucí řádovou čárkou, jednoduchá přesnost
double 64 bitů hodnota s plovoucí řádovou čárkou, dvojitá přesnost
half 32 bitů hodnota s plovoucí řádovou čárkou, poloviční přesnost (jen převody)
     
int8 8 bitů osmibitové celé číslo se znaménkem, obecné použití
int16 16 bitů 16bitové celé číslo se znaménkem, obecné použití
int32 32 bitů 32bitové celé číslo se znaménkem, obecné použití
int64 64 bitů 64bitové celé číslo se znaménkem, obecné použití
     
uint8 8 bitů osmibitové celé číslo bez znaménka, typicky barvová složka pixelu
uint16 16 bitů 16bitové celé číslo bez znaménka, typicky zvukový vzorek
uint32 32 bitů 32bitové celé číslo bez znaménka, obecné použití
uint64 64 bitů 64bitové celé číslo bez znaménka, obecné použití
     
polynomial 8 bitů používán pro výpočty korekcí chyb atd.
polynomial 16 bitů používán pro výpočty korekcí chyb atd.

Poznámka: některé typy (formáty) jsou dostupné jen pro NEON implementovaný na mikroprocesorech s architekturou AArch64. Týká se to zejména použití vektorů s elementy typu double.

8. Práce se skalárními daty

I přesto, že je technologie SIMD primárně určena pro provádění operací s prvky vektorů, nabízí NEON i mnoho instrukcí pracujících se skalárními daty. U těchto instrukcí se pracuje buď s celými 128bitovými hodnotami či s hodnotami 64bitovými popř. „pouze“ 32bitovými. V prvním případě jsou zdrojové a cílové registry pojmenovány Vn, v případě druhém pak Dn (zde se tedy využije spodních 64 bitů původně 128bitového registru, u 32bitových operací jen spodních 32 bitů). Příkladem může být instrukce ADD, která může být prováděna na běžné ALU, kde bude pracovat s registry Wn či Xn. NEON nabízí stejnou instrukci, ovšem pro použití s registry Dn, které obsahují celé číslo (pro FP operace je určena instrukce FADD). Některé instrukce pro zpracování skalárních dat jsou vypsány v následující tabulce:

Instrukce Poznámka
ABS
ADDP
CMEQ
CMEQ
CMGE
CMGE
CMGT
CMGT
CMHI
CMHS
CMLE
CMLT
CMTST
DUP
FABD
FACGE
FACGT
FADDP
FCMEQ
FCMEQ
FCMGE
FCMGE
FCMGT
FCMGT
FCMLE
FCMLT
FCVTAS
FCVTAU
FCVTMS
FCVTMU
FCVTNS
FCVTNU
FCVTPS
FCVTPU
FCVTXN
FCVTZS
FCVTZS
FCVTZU
FCVTZU
FMAXNMP
FMAXP
FMINNMP
FMINP
FMLA
FMLS
FMUL
FMULX
FMULX
FRECPE
FRECPS
FRECPX
FRSQRTE
FRSQRTS
MOV
NEG
SCVTF
SCVTF
SHL
SLI
SQABS výpočet se saturací, operand je celé číslo se znaménkem
SQADD výpočet se saturací, operand je celé číslo se znaménkem
SQDMLAL výpočet se saturací, operand je celé číslo se znaménkem
SQDMLAL výpočet se saturací, operand je celé číslo se znaménkem
SQDMLSL výpočet se saturací, operand je celé číslo se znaménkem
SQDMLSL výpočet se saturací, operand je celé číslo se znaménkem
SQDMULH výpočet se saturací, operand je celé číslo se znaménkem
SQDMULH výpočet se saturací, operand je celé číslo se znaménkem
SQDMULL výpočet se saturací, operand je celé číslo se znaménkem
SQDMULL výpočet se saturací, operand je celé číslo se znaménkem
SQNEG výpočet se saturací, operand je celé číslo se znaménkem
SQRDMULH výpočet se saturací, operand je celé číslo se znaménkem
SQRDMULH výpočet se saturací, operand je celé číslo se znaménkem
SQRSHL výpočet se saturací, operand je celé číslo se znaménkem
SQRSHRN výpočet se saturací, operand je celé číslo se znaménkem
SQRSHRUN výpočet se saturací, operand je celé číslo se znaménkem
SQSHL výpočet se saturací, operand je celé číslo se znaménkem
SQSHL výpočet se saturací, operand je celé číslo se znaménkem
SQSHLU výpočet se saturací, operand je celé číslo se znaménkem
SQSHRN výpočet se saturací, operand je celé číslo se znaménkem
SQSHRUN výpočet se saturací, operand je celé číslo se znaménkem
SQSUB výpočet se saturací, operand je celé číslo se znaménkem
SQXTN výpočet se saturací, operand je celé číslo se znaménkem
SQXTUN výpočet se saturací, operand je celé číslo se znaménkem
SRI
SRSHL
SRSHR
SRSRA
SSHL
SSHR
SSRA
SUB
SUQADD
UCVTF
UCVTF
UQADD výpočet se saturací, operand je celé číslo bez znaménka
UQRSHL výpočet se saturací, operand je celé číslo bez znaménka
UQRSHRN výpočet se saturací, operand je celé číslo bez znaménka
UQSHL výpočet se saturací, operand je celé číslo bez znaménka
UQSHL výpočet se saturací, operand je celé číslo bez znaménka
UQSHRN výpočet se saturací, operand je celé číslo bez znaménka
UQSUB výpočet se saturací, operand je celé číslo bez znaménka
UQXTN výpočet se saturací, operand je celé číslo bez znaménka
URSHL
URSHR
URSRA
USHL
USHR
USQADD
USRA

9. Formát instrukcí NEON, prefixy a suffixy u instrukcí

Pojmenování instrukcí technologie NEON je u AArch64 v porovnání s původní 32bitovou architekturou odlišné, s čímž se setkají především programátoři pracující s assemblerem nebo s debuggerem. Zejména došlo k odstranění prefixového znaku V, protože způsob provádění instrukce je odvozen z typů operandů. Namísto toho se používají prefixy S, U, F a P s následujícím významem:

Prefix Význam
S signed
U unsigned
F floating point
P polynomial

Přesná konfigurace vektorů, které se používají jako vstupní a výstupní operandy instrukcí, je určena suffixem uvedeným za jméno příslušného registru, ať již se jedná o registr zdrojový či cílový. Suffix se od jména registru odděluje tečkou:

Suffix Význam Šířka vektoru
Vn.8B 8 bitů × 8 64 bitů
Vn.16B 8 bitů × 16 128 bitů
Vn.4H 16 bitů či half × 4 64 bitů
Vn.8H 16 bitů či half × 8 128 bitů
Vn.2S 32 bitů či single/float × 2 64 bitů
Vn.4S 32 bitů či single/float × 4 128 bitů
Vn.1D 64 bitů či double × 1 64 bitů
Vn.2D 64 bitů či double × 2 128 bitů

Zde můžeme vidět, že je skutečně nutné použít kombinaci prefixu a suffixu, aby bylo možné rozlišit například součet vektorů čtyř 32bitových celých čísel se znaménkem od součtu 32bitových celých čísel bez znaménka či čtyř hodnot typu single/float – ve všech případech mají vektory stejný počet prvků a prvky mají stejnou bitovou šířku, ale prováděná operace může být diametrálně odlišná.

Poznámka: u intrinsic se prefix V stále používá.

10. Konverze operandů (rozšíření, zmenšení), operace se saturací

Některé instrukce, typicky základní aritmetické instrukce prováděné nad prvky vektorů, existují v několika variantách:

  • normální variantě se paralelně provádí operace nad jednotlivými prvky bez dalších úprav či konverzí. U této varianty je typ výsledného vektoru shodný s vektory zdrojovými.
  • U varianty označované slovem long (lengtening) se nejprve prvky vstupních vektorů zkonvertují na dvojnásobnou šířku (osm bitů → 16 bitů, 16 bitů → 32 bitů či 32 bitů → 64 bitů), provede se zvolená operace a výsledky se uloží do cílového vektoru. Vzhledem k tomu, že došlo k rozšíření prvků vektorů, je možné zpracovat vždy jen polovinu prvků, protože druhou polovinu by nebylo možné nikam uložit. Z tohoto důvodu vždy existují dvě varianty každé instrukce, přičemž druhá varianta používá suffix 2 pro odlišení, zda se má pracovat s horní polovinou či dolní polovinou vektoru.
  • Další varianta se jmenuje wide (widening) a dochází u ní ke kombinaci dvou zdrojových vektorů různých typů. Druhý zdrojový vektor obsahuje prvky s poloviční šířkou, které jsou opět zkonvertovány a následně je provedena zvolená operace. Opět platí, že každá instrukce existuje ve dvou variantách podle toho, kterou polovinu druhého zdrojového vektoru zpracovává.
  • Opačná situace nastává u varianty označované narrow (narrowing), kde se nejprve provede zvolená operace a následně se prvky sníží na poloviční šířku.
  • Další varianty rozlišují provedení operace se saturací. V tomto případě se používají prefixy SQ a UQ podle toho, zda prvky vstupních vektorů jsou hodnoty se znaménkem či bez znaménka.
  • Posledním suffixem je znak P značící, že se zvolená operace nemá provést vždy mezi korespondujícími prvky dvou vstupních vektorů, ale vždy mezi dvěma sousedními prvky prvního zdrojového vektoru a posléze mezi stejnými páry druhého zdrojového vektoru.

11. Příklad různých variant instrukce ADD

Vzhledem k tomu, že u technologie NEON je možné používat vektory s prvky různých typů a navíc se při výpočtech může provádět konverze operandů, je celkový počet kombinací instrukcí velmi vysoký. Můžeme si to ukázat na instrukci ADD, tj. instrukci, která má provést součet. Přitom se může jednat o součet dvou skalárů (různých typů) nebo o součet prvků vektorů, přičemž počet prvků a jejich typ se může lišit (připomeňme si, že můžeme použít „jen“ 64bitové vektory či naopak vektory 128bitové):

Instrukce Význam/provedená operace
ADD Wd, Wn, Wm skalární součet s 32bitovými operandy typu celé číslo
ADD Xd, Xn, Xm skalární součet se 64bitovými operandy typu celé číslo
ADD Dd, Dn, Dm skalární součet se 64bitovými operandy typu celé číslo, tentokrát s registry NEONu
   
FADD Sd, Sn, Sm skalární součet s FP operandy typu float/single
FADD Dd, Dn, Dm skalární součet s FP operandy typu double
   
SQDD Vd.typ, Vn.typ, Vm.typ skalární součet se saturací pro celá čísla se znaménkem
UQDD Vd.typ, Vn.typ, Vm.typ skalární součet se saturací pro celá čísla bez znaménka
   
ADD Vd.8B, Vn.8B, Vm.8B „vektorový“ součet osmi prvků typu byte
ADD Vd.16B, Vn.16B, Vm.16B „vektorový“ součet šestnácti prvků typu byte
ADD Vd.4H, Vn.4H, Vm.4H „vektorový“ součet čtyř prvků typu 16bit integer
ADD Vd.8H, Vn.8H, Vm.8H „vektorový“ součet osmi prvků typu 16bit integer
ADD Vd.2S, Vn.2S, Vm.2S „vektorový“ součet dvou prvků typu 32bit integer
ADD Vd.4S, Vn.4S, Vm.4S „vektorový“ součet čtyř prvků typu 32bit integer
ADD Vd.2D, Vn.2D, Vm.2D „vektorový“ součet dvou prvků typu 64bit integer
   
FADD Vd.2S, Vn.2S, Vm.2S „vektorový“ součet dvou prvků typu float/single
FADD Vd.4S, Vn.4S, Vm.4S „vektorový“ součet čtyř prvků typu float/single
FADD Vd.2D, Vn.2D, Vm.2D „vektorový“ součet dvou prvků typu double
   
UADDL Vd.8H, Vn.8B, Vm.8B „vektorový“ součet s rozšířením operandů bez znaménka (byte → 16bit integer)
UADDL Vd.4S, Vn.4H, Vm.4H „vektorový“ součet s rozšířením operandů bez znaménka (16bit integer → 32bit integer)
UADDL Vd.2D, Vn.2S, Vm.2S „vektorový“ součet s rozšířením operandů bez znaménka (32bit integer → 64bit integer)
UADDL2 Vd.8H, Vn.8B, Vm.8B jako první UADDL, ale pro druhou polovinu vektoru
UADDL2 Vd.4S, Vn.4H, Vm.4H jako první UADDL, ale pro druhou polovinu vektoru
UADDL2 Vd.2D, Vn.2S, Vm.2S jako první UADDL, ale pro druhou polovinu vektoru
   
SADDL Vd.8H, Vn.8B, Vm.8B „vektorový“ součet s rozšířením operandů se znaménkem (byte → 16bit integer)
SADDL Vd.4S, Vn.4H, Vm.4H „vektorový“ součet s rozšířením operandů se znaménkem (16bit integer → 32bit integer)
SADDL Vd.2D, Vn.2S, Vm.2S „vektorový“ součet s rozšířením operandů se znaménkem (32bit integer → 64bit integer)
SADDL2 Vd.8H, Vn.8B, Vm.8B jako první SADDL, ale pro druhou polovinu vektoru
SADDL2 Vd.4S, Vn.4H, Vm.4H jako první SADDL, ale pro druhou polovinu vektoru
SADDL2 Vd.2D, Vn.2S, Vm.2S jako první SADDL, ale pro druhou polovinu vektoru
   
UADDW Vd.8H, Vn.8H, Vm.8B „vektorový“ součet s rozšířením operandu bez znaménka (byte → 16bit integer)
UADDW Vd.4S, Vn.4S, Vm.4H „vektorový“ součet s rozšířením operandu bez znaménka (16bit integer → 32bit integer)
UADDW Vd.2D, Vn.2D, Vm.2S „vektorový“ součet s rozšířením operandu bez znaménka (32bit integer → 64bit integer)
UADDW2 Vd.8H, Vn.8H, Vm.8B jako první UADDW, ale pro druhou polovinu vektoru
UADDW2 Vd.4S, Vn.4S, Vm.4H jako první UADDW, ale pro druhou polovinu vektoru
UADDW2 Vd.2D, Vn.2D, Vm.2S jako první UADDW, ale pro druhou polovinu vektoru
   
SADDW Vd.8H, Vn.8H, Vm.8B „vektorový“ součet s rozšířením operandu se znaménkem (byte → 16bit integer)
SADDW Vd.4S, Vn.4S, Vm.4H „vektorový“ součet s rozšířením operandu se znaménkem (16bit integer → 32bit integer)
SADDW Vd.2D, Vn.2D, Vm.2S „vektorový“ součet s rozšířením operandu se znaménkem (32bit integer → 64bit integer)
SADDW2 Vd.8H, Vn.8H, Vm.8B jako první SADDW, ale pro druhou polovinu vektoru
SADDW2 Vd.4S, Vn.4S, Vm.4H jako první SADDW, ale pro druhou polovinu vektoru
SADDW2 Vd.2D, Vn.2D, Vm.2S jako první SADDW, ale pro druhou polovinu vektoru
   
ADDP Vd.typ, Vn.typ, Vm.typ součet sousedních párů prvků obou zdrojových vektorů
FADDP Vd.typ, Vn.typ, Vm.typ dtto, ale pro typy single a double

12. Typy podporovaných vektorových instrukcí

Instrukce v technologii NEON jsou navrženy takovým způsobem, aby je bylo možné použít při zpracování 1D signálů (filtry s konečnou a nekonečnou odezvou, FFT, DFT, DCT), při image processingu, zpracování videa (motion vektory atd.) popř. v 3D grafice:

Instrukce Zpracování signálu Image/video processing Další použití
vektorový součet obecně použitelný změna velikosti, změna jasu detekce kolizí
maticový součet FFT rotace obrazu  
vektorový rozdíl obecně použitelný, FFT změna jasu  
maticový rozdíl FIR    
násobení prvků vektorů FIR změna kontrastu  
vektorový MAC FIR    
násobení matic FIR    
dělení prvků vektorů IIR    
výpočet délky vektoru      
normalizace vektoru     3D grafika
absolutní hodnota      
skalární součin     3D grafika
vektorový součin     3D grafika
determinant matice      
inverzní matice      
transpozice matice      

13. Aritmetické a logické instrukce

Základní aritmetické a logické instrukce prováděné s prvky vektorů jsou vypsány v následující tabulce. Povšimněte si, že některé instrukce existují ve variantách pro celočíselné i FP operandy, u dalších instrukcí se navíc rozlišuje režim se saturací a instrukce pro násobení pracuje i pro typ polynomial:

Instrukce Poznámka
ADD/FADD součet
SQADD/UQADD/ součet se saturací
SUB/FSUB rozdíl
SQSUB/UQSUB/ rozdíl se saturací
   
MUL/FMUL/PMUL součin
MLA/FMLA multiply-accumulate (MAC)
MLS/FMLS multiply-subtract (podobné předchozímu, ale výsledek se odečte od akumulátoru)
FDIV podíl (jen pro single a double)
   
UABD/SABD/FABD absolutní hodnota rozdílu
UABA/SABA absolutní hodnota rozdílu se přičte s cílovému vektoru (akumulace)
UMAX/SMAX/FMAX maximální hodnota
UMIN/SMIN/FMIN minimální hodnota
   
AND logický součin bit po bitu
EOR logická operace XOR
ORR logický součet bit po bitu
ORN logický součet + negace

14. Instrukce pro provedení jednoho kroku delší operace

Některé delší operace se rozdělují do série několika instrukcí a provádí se tak iterativně, přičemž v každé iteraci dochází ke zpřesnění výsledku. U těchto instrukcí se mezivýsledky ukládají do speciálních registrů (konkrétně se jmenují FRECPS a FRSQRTS):

První takovou instrukcí je FRECPS. Tato instrukce vynásobí příslušné dvojice prvků vektorů, odečte tento výsledek od konstanty 2 a výsledek uloží do příslušných prvků cílového vektoru. Jedná se o jeden krok iterativního výpočtu:

xn+1 = xn (2-dxn)

který konverguje k hodnotě 1/d, ovšem jen ve chvíli, pokud je prvotní odhad výsledku x0 proveden instrukcí FRECPE.

Druhá instrukce se jmenuje FRSQRTS a provádí jeden krok iterativního výpočtu:

xn+1 = xn (3-dxn2)/2

Tento výpočet konverguje k hodnotě 1/√d, ovšem původní odhad mezivýsledku je nutné spočítat instrukcí FRSQRTE (E znamená „estimate“ zatímco S v předchozí instrukci znamená „step“).

15. Podpora SIMD instrukcí ve vyšších programovacích jazycích

Nové instrukce zavedené v rámci technologie NEON lze samozřejmě využívat především přímo v assembleru, což je sice 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. 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ů. 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 čá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).

CS24_early

Další možnost využití instrukcí typu SIMD i z vyšších programovacích jazyků spočívá v takzvané automatické „vektorizaci“. Překladače jazyků C a C++ totiž v některých případech dokážou rozpoznat, že je možné nějakou programovou smyčku provádět nikoli čistě sekvenčně, ale s využitím operací prováděných nad vektory. Programátor však musí v těchto případech překladači vhodným způsobem „napovědět“, například tak, že přímo v programu naznačí, že počet cyklů ve smyčce bude za všech okolností dělitelný čtyřmi či osmi atd. To nemusí být vždy úplně jednoduché, už jen z toho důvodu, že jazyky C a C++ nepodporují zápis metadat do programu (v Javě by to bylo umožněno s využitím anotací).

Automatická „vektorizace“ zmíněná v předchozím odstavci však stále nedokáže (alespoň v současnosti) využít celého potenciálu technologie NEON. Z tohoto důvodu mohou programátoři v případě potřeby zavolat přímo z programů psaných v C či C++ takzvané interní (intrinsic) funkce, tj. funkce, které jsou překladačem spravovány speciálním způsobem. Jejich použití se sice podobá volání běžné funkce, ve skutečnosti se však jedná o makro, které překladač vhodným způsobem expanduje do použití některé instrukce zavedené v technologii NEON. Příklad použití intrinsic funkce je ukázán níže na volání instrukce pro součet dvou vektorů:

 
#include <arm_neon.h>
 
uint32x4_t double_elements(uint32x4_t input)
{
    return(vaddq_u32(input, input));
}
 

16. Odkazy na Internetu

  1. NEON Technology (stránky ARM)
    https://developer.arm.com/techno­logies/neon
  2. SIMD Assembly Tutorial: ARM NEON – Xiph.org
    https://people.xiph.org/~tte­rribe/daala/neon_tutorial­.pdf
  3. Ne10
    http://projectne10.github.io/Ne10/
  4. NEON and Floating-Point architecture
    http://infocenter.arm.com/hel­p/index.jsp?topic=/com.ar­m.doc.den0024a/BABIGHEB.html
  5. An Introduction to ARM NEON
    http://peterdn.com/post/an-introduction-to-ARM-NEON.aspx
  6. ARM NEON Intrinsics Reference
    http://infocenter.arm.com/hel­p/topic/com.arm.doc.ihi0073a/I­HI0073A_arm_neon_intrinsic­s_ref.pdf
  7. Arm Neon Intrinsics vs hand assembly
    https://stackoverflow.com/qu­estions/9828567/arm-neon-intrinsics-vs-hand-assembly
  8. ARM NEON Optimization. An Example
    http://hilbert-space.de/?p=22
  9. AArch64 NEON instruction format
    https://developer.arm.com/doc­s/den0024/latest/7-aarch64-floating-point-and-neon/73-aarch64-neon-instruction-format
  10. Vektorové procesory aneb další pokus o zvýšení výpočetního výkonu počítačů
    http://www.root.cz/clanky/vektorove-procesory-aneb-dalsi-pokus-o-zvyseni-vypocetniho-vykonu-pocitacu/
  11. SIMD instrukce využívané v moderních mikroprocesorech řady x86
    http://www.root.cz/clanky/simd-instrukce-vyuzivane-v-modernich-mikroprocesorech-rady-x86/
  12. SIMD instrukce v moderních mikroprocesorech řady x86 (2.část: SSE)
    http://www.root.cz/clanky/simd-instrukce-v-modernich-mikroprocesorech-rady-x86–2-cast-sse/
  13. SIMD instrukce v moderních mikroprocesorech řady x86 (3.část: SSE2)
    http://www.root.cz/clanky/simd-instrukce-v-modernich-mikroprocesorech-rady-x86–3-cast-sse2/
  14. Instrukce typu SIMD na mikroprocesorech RISC
    http://www.root.cz/clanky/instrukce-typu-simd-na-mikroprocesorech-risc/
  15. Instrukce typu SIMD na mikroprocesorech RISC (2. část)
    http://www.root.cz/clanky/instrukce-typu-simd-na-mikroprocesorech-risc-2-cast/
  16. Instrukce typu SIMD na mikroprocesorech RISC (3.část – MIPS-3D a VIS)
    http://www.root.cz/clanky/instrukce-typu-simd-na-mikroprocesorech-risc-3-cast-mips-3d-a-vis/
  17. Trasování a ladění nativních aplikací v Linuxu
    https://www.root.cz/clanky/trasovani-a-ladeni-nativnich-aplikaci-v-linuxu/
  18. Trasování a ladění nativních aplikací v Linuxu: použití GDB a jeho nadstaveb
    https://www.root.cz/clanky/trasovani-a-ladeni-nativnich-aplikaci-v-linuxu-pouziti-gdb-a-jeho-nadstaveb/
  19. Debuggery a jejich nadstavby v Linuxu (3): Nemiver
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-3-nemiver/
  20. Debuggery a jejich nadstavby v Linuxu (4): KDbg
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-4-kdbg/
  21. Debuggery a jejich nadstavby v Linuxu (5): ladění aplikací v editorech Emacs a Vim
    http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-5-ladeni-aplikaci-v-editorech-emacs-a-vim/
  22. Tracing (software)
    https://en.wikipedia.org/wi­ki/Tracing_%28software%29
  23. cgdb: the curses debugger
    https://cgdb.github.io/
  24. cgdb: dokumentace
    https://cgdb.github.io/docs/cgdb-split.html
  25. strace(1) – Linux man page
    http://linux.die.net/man/1/strace
  26. strace (stránka projektu na SourceForge)
    https://sourceforge.net/pro­jects/strace/
  27. strace (Wikipedia)
    https://en.wikipedia.org/wiki/Strace
  28. GDB – Dokumentace
    http://sourceware.org/gdb/cu­rrent/onlinedocs/gdb/
  29. GDB – Supported Languages
    http://sourceware.org/gdb/cu­rrent/onlinedocs/gdb/Suppor­ted-Languages.html#Supported-Languages
  30. GNU Debugger (Wikipedia)
    https://en.wikipedia.org/wi­ki/GNU_Debugger
  31. The LLDB Debugger
    http://lldb.llvm.org/
  32. Debugger (Wikipedia)
    https://en.wikipedia.org/wi­ki/Debugger
  33. Comparison of ARMv8-A cores
    https://en.wikipedia.org/wi­ki/Comparison_of_ARMv8-A_cores
  34. A64 General Instructions
    http://www.keil.com/suppor­t/man/docs/armclang_asm/ar­mclang_asm_pge1427898258836­.htm
  35. ARMv8 (AArch64) Instruction Encoding
    http://kitoslab-eng.blogspot.cz/2012/10/armv8-aarch64-instruction-encoding.html
  36. Cortex-A32 Processor
    https://www.arm.com/produc­ts/processors/cortex-a/cortex-a32-processor.php
  37. Cortex-A35 Processor
    https://www.arm.com/produc­ts/processors/cortex-a/cortex-a35-processor.php
  38. Cortex-A53 Processor
    https://www.arm.com/produc­ts/processors/cortex-a/cortex-a53-processor.php
  39. Cortex-A57 Processor
    https://www.arm.com/produc­ts/processors/cortex-a/cortex-a57-processor.php
  40. Cortex-A72 Processor
    https://www.arm.com/produc­ts/processors/cortex-a/cortex-a72-processor.php
  41. Cortex-A73 Processor
    https://www.arm.com/produc­ts/processors/cortex-a/cortex-a73-processor.php
  42. Apple A7 (SoC založen na CPU Cyclone)
    https://en.wikipedia.org/wi­ki/Apple_A7
  43. System cally pro AArch64 na Linuxu
    https://github.com/torval­ds/linux/blob/master/inclu­de/uapi/asm-generic/unistd.h
  44. Architectures/AArch64 (FedoraProject.org)
    https://fedoraproject.org/wi­ki/Architectures/AArch64
  45. SIG pro AArch64 (CentOS)
    https://wiki.centos.org/Spe­cialInterestGroup/AltArch/A­Arch64
  46. The ARMv8 instruction sets
    http://infocenter.arm.com/hel­p/index.jsp?topic=/com.ar­m.doc.den0024a/ch05s01.html
  47. A64 Instruction Set
    https://developer.arm.com/pro­ducts/architecture/instruc­tion-sets/a64-instruction-set
  48. Switching between the instruction sets
    http://infocenter.arm.com/hel­p/index.jsp?topic=/com.ar­m.doc.den0024a/ch05s01.html
  49. The A64 instruction set
    http://infocenter.arm.com/hel­p/index.jsp?topic=/com.ar­m.doc.den0024a/ch05s01.html
  50. Introduction to ARMv8 64-bit Architecture
    https://quequero.org/2014/04/in­troduction-to-arm-architecture/
  51. MCU market turns to 32-bits and ARM
    http://www.eetimes.com/do­cument.asp?doc_id=1280803
  52. Cortex-M0 Processor (ARM Holdings)
    http://www.arm.com/produc­ts/processors/cortex-m/cortex-m0.php
  53. Cortex-M0+ Processor (ARM Holdings)
    http://www.arm.com/produc­ts/processors/cortex-m/cortex-m0plus.php
  54. ARM Processors in a Mixed Signal World
    http://www.eeweb.com/blog/arm/arm-processors-in-a-mixed-signal-world
  55. ARM Architecture (Wikipedia)
    https://en.wikipedia.org/wi­ki/ARM_architecture
  56. DSP for Cortex-M
    https://developer.arm.com/techno­logies/dsp/dsp-for-cortex-m
  57. Cortex-M processors in DSP applications? Why not?!
    https://community.arm.com/pro­cessors/b/blog/posts/cortex-m-processors-in-dsp-applications-why-not
  58. White Paper – DSP capabilities of Cortex-M4 and Cortex-M7
    https://community.arm.com/pro­cessors/b/blog/posts/white-paper-dsp-capabilities-of-cortex-m4-and-cortex-m7
  59. Q (number format)
    https://en.wikipedia.org/wi­ki/Q_%28number_format%29
  60. TriCore Architecture & Core
    http://www.infineon.com/cms/en/pro­duct/microcontroller/32-bit-tricore-tm-microcontroller/tricore-tm-architecture-and-core/channel.html?channel=ff80808112ab681d0112­ab6b73d40837
  61. TriCoreTM V1.6 Instruction Set: 32-bit Unified Processor Core
    http://www.infineon.com/dgdl/tc_v131_in­structionset_v138.pdf?file­Id=db3a304412b407950112b409b6dd0352
  62. TriCore v2.2 C Compiler, Assembler, Linker Reference Manual
    http://tasking.com/suppor­t/tricore/tc_reference_gu­ide_v2.2.pdf
  63. Infineon TriCore (Wikipedia)
    https://en.wikipedia.org/wi­ki/Infineon_TriCore
  64. C166®S V2 Architecture & Core
    http://www.infineon.com/cms/en/pro­duct/microcontroller/16-bit-c166-microcontroller/c166-s-v2-architecture-and-core/channel.html?channel=db3a304312bef5660112­c3011c7d01ae
  65. Comparing four 32-bit soft processor cores
    http://www.eetimes.com/au­thor.asp?section_id=14&doc_id=1286116
  66. RISC-V Instruction Set
    http://riscv.org/download­.html#spec_compressed_isa
  67. RISC-V Spike (ISA Simulator)
    http://riscv.org/download.html#isa-sim
  68. RISC-V (Wikipedia)
    https://en.wikipedia.org/wiki/RISC-V
  69. David Patterson (Wikipedia)
    https://en.wikipedia.org/wi­ki/David_Patterson_(compu­ter_scientist)
  70. OpenRISC (oficiální stránky projektu)
    http://openrisc.io/
  71. OpenRISC architecture
    http://openrisc.io/architecture.html
  72. Emulátor OpenRISC CPU v JavaScriptu
    http://s-macke.github.io/jor1k/demos/main.html
  73. OpenRISC (Wikipedia)
    https://en.wikipedia.org/wi­ki/OpenRISC
  74. OpenRISC – instrukce
    http://sourceware.org/cgen/gen-doc/openrisc-insn.html
  75. OpenRISC – slajdy z přednášky o projektu
    https://iis.ee.ethz.ch/~gmichi/a­socd/lecturenotes/Lecture6­.pdf
  76. Berkeley RISC
    http://en.wikipedia.org/wi­ki/Berkeley_RISC
  77. Great moments in microprocessor history
    http://www.ibm.com/develo­perworks/library/pa-microhist.html
  78. Microprogram-Based Processors
    http://research.microsoft.com/en-us/um/people/gbell/Computer_Struc­tures_Principles_and_Exam­ples/csp0167.htm
  79. Great Microprocessors of the Past and Present
    http://www.cpushack.com/CPU/cpu1.html
  80. A Brief History of Microprogramming
    http://www.cs.clemson.edu/~mar­k/uprog.html
  81. What is RISC?
    http://www-cs-faculty.stanford.edu/~ero­berts/courses/soco/projec­ts/2000–01/risc/whatis/
  82. RISC vs. CISC
    http://www-cs-faculty.stanford.edu/~ero­berts/courses/soco/projec­ts/2000–01/risc/risccisc/
  83. RISC and CISC definitions:
    http://www.cpushack.com/CPU/cpu­AppendA.html
  84. FPGA
    https://cs.wikipedia.org/wi­ki/Programovateln%C3%A9_hra­dlov%C3%A9_pole
  85. The Evolution of RISC
    http://www.ibm.com/develo­perworks/library/pa-microhist.html#sidebar1

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

Autor článku

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