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ů
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
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 D0 až D31 (D = double), popř. mohly být vždy dva sousední registry spojeny do jednoho 128bitového registru (ty byly pojmenovány Q0 až Q15, Q = quad). Pokud jádro kromě NEONu podporovalo i výše zmíněnou VFP (konkrétně VFPv3 nebo VFPv4), byly registry D0 až D31 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:
- Vector vždy značí 64bitovou či 128bitovou část pracovního registru Vn, která je rozdělena na prvky.
- Element je prvek vektoru.
- 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 V0 až V31 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 D0 až D31. 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:
- V 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).
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
- NEON Technology (stránky ARM)
https://developer.arm.com/technologies/neon - SIMD Assembly Tutorial: ARM NEON – Xiph.org
https://people.xiph.org/~tterribe/daala/neon_tutorial.pdf - Ne10
http://projectne10.github.io/Ne10/ - NEON and Floating-Point architecture
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0024a/BABIGHEB.html - An Introduction to ARM NEON
http://peterdn.com/post/an-introduction-to-ARM-NEON.aspx - ARM NEON Intrinsics Reference
http://infocenter.arm.com/help/topic/com.arm.doc.ihi0073a/IHI0073A_arm_neon_intrinsics_ref.pdf - Arm Neon Intrinsics vs hand assembly
https://stackoverflow.com/questions/9828567/arm-neon-intrinsics-vs-hand-assembly - ARM NEON Optimization. An Example
http://hilbert-space.de/?p=22 - AArch64 NEON instruction format
https://developer.arm.com/docs/den0024/latest/7-aarch64-floating-point-and-neon/73-aarch64-neon-instruction-format - 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/ - SIMD instrukce využívané v moderních mikroprocesorech řady x86
http://www.root.cz/clanky/simd-instrukce-vyuzivane-v-modernich-mikroprocesorech-rady-x86/ - 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/ - 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/ - Instrukce typu SIMD na mikroprocesorech RISC
http://www.root.cz/clanky/instrukce-typu-simd-na-mikroprocesorech-risc/ - Instrukce typu SIMD na mikroprocesorech RISC (2. část)
http://www.root.cz/clanky/instrukce-typu-simd-na-mikroprocesorech-risc-2-cast/ - 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/ - Trasování a ladění nativních aplikací v Linuxu
https://www.root.cz/clanky/trasovani-a-ladeni-nativnich-aplikaci-v-linuxu/ - 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/ - Debuggery a jejich nadstavby v Linuxu (3): Nemiver
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-3-nemiver/ - Debuggery a jejich nadstavby v Linuxu (4): KDbg
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-4-kdbg/ - 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/ - Tracing (software)
https://en.wikipedia.org/wiki/Tracing_%28software%29 - cgdb: the curses debugger
https://cgdb.github.io/ - cgdb: dokumentace
https://cgdb.github.io/docs/cgdb-split.html - strace(1) – Linux man page
http://linux.die.net/man/1/strace - strace (stránka projektu na SourceForge)
https://sourceforge.net/projects/strace/ - strace (Wikipedia)
https://en.wikipedia.org/wiki/Strace - GDB – Dokumentace
http://sourceware.org/gdb/current/onlinedocs/gdb/ - GDB – Supported Languages
http://sourceware.org/gdb/current/onlinedocs/gdb/Supported-Languages.html#Supported-Languages - GNU Debugger (Wikipedia)
https://en.wikipedia.org/wiki/GNU_Debugger - The LLDB Debugger
http://lldb.llvm.org/ - Debugger (Wikipedia)
https://en.wikipedia.org/wiki/Debugger - Comparison of ARMv8-A cores
https://en.wikipedia.org/wiki/Comparison_of_ARMv8-A_cores - A64 General Instructions
http://www.keil.com/support/man/docs/armclang_asm/armclang_asm_pge1427898258836.htm - ARMv8 (AArch64) Instruction Encoding
http://kitoslab-eng.blogspot.cz/2012/10/armv8-aarch64-instruction-encoding.html - Cortex-A32 Processor
https://www.arm.com/products/processors/cortex-a/cortex-a32-processor.php - Cortex-A35 Processor
https://www.arm.com/products/processors/cortex-a/cortex-a35-processor.php - Cortex-A53 Processor
https://www.arm.com/products/processors/cortex-a/cortex-a53-processor.php - Cortex-A57 Processor
https://www.arm.com/products/processors/cortex-a/cortex-a57-processor.php - Cortex-A72 Processor
https://www.arm.com/products/processors/cortex-a/cortex-a72-processor.php - Cortex-A73 Processor
https://www.arm.com/products/processors/cortex-a/cortex-a73-processor.php - Apple A7 (SoC založen na CPU Cyclone)
https://en.wikipedia.org/wiki/Apple_A7 - System cally pro AArch64 na Linuxu
https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/unistd.h - Architectures/AArch64 (FedoraProject.org)
https://fedoraproject.org/wiki/Architectures/AArch64 - SIG pro AArch64 (CentOS)
https://wiki.centos.org/SpecialInterestGroup/AltArch/AArch64 - The ARMv8 instruction sets
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0024a/ch05s01.html - A64 Instruction Set
https://developer.arm.com/products/architecture/instruction-sets/a64-instruction-set - Switching between the instruction sets
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0024a/ch05s01.html - The A64 instruction set
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0024a/ch05s01.html - Introduction to ARMv8 64-bit Architecture
https://quequero.org/2014/04/introduction-to-arm-architecture/ - MCU market turns to 32-bits and ARM
http://www.eetimes.com/document.asp?doc_id=1280803 - Cortex-M0 Processor (ARM Holdings)
http://www.arm.com/products/processors/cortex-m/cortex-m0.php - Cortex-M0+ Processor (ARM Holdings)
http://www.arm.com/products/processors/cortex-m/cortex-m0plus.php - ARM Processors in a Mixed Signal World
http://www.eeweb.com/blog/arm/arm-processors-in-a-mixed-signal-world - ARM Architecture (Wikipedia)
https://en.wikipedia.org/wiki/ARM_architecture - DSP for Cortex-M
https://developer.arm.com/technologies/dsp/dsp-for-cortex-m - Cortex-M processors in DSP applications? Why not?!
https://community.arm.com/processors/b/blog/posts/cortex-m-processors-in-dsp-applications-why-not - White Paper – DSP capabilities of Cortex-M4 and Cortex-M7
https://community.arm.com/processors/b/blog/posts/white-paper-dsp-capabilities-of-cortex-m4-and-cortex-m7 - Q (number format)
https://en.wikipedia.org/wiki/Q_%28number_format%29 - TriCore Architecture & Core
http://www.infineon.com/cms/en/product/microcontroller/32-bit-tricore-tm-microcontroller/tricore-tm-architecture-and-core/channel.html?channel=ff80808112ab681d0112ab6b73d40837 - TriCoreTM V1.6 Instruction Set: 32-bit Unified Processor Core
http://www.infineon.com/dgdl/tc_v131_instructionset_v138.pdf?fileId=db3a304412b407950112b409b6dd0352 - TriCore v2.2 C Compiler, Assembler, Linker Reference Manual
http://tasking.com/support/tricore/tc_reference_guide_v2.2.pdf - Infineon TriCore (Wikipedia)
https://en.wikipedia.org/wiki/Infineon_TriCore - C166®S V2 Architecture & Core
http://www.infineon.com/cms/en/product/microcontroller/16-bit-c166-microcontroller/c166-s-v2-architecture-and-core/channel.html?channel=db3a304312bef5660112c3011c7d01ae - Comparing four 32-bit soft processor cores
http://www.eetimes.com/author.asp?section_id=14&doc_id=1286116 - RISC-V Instruction Set
http://riscv.org/download.html#spec_compressed_isa - RISC-V Spike (ISA Simulator)
http://riscv.org/download.html#isa-sim - RISC-V (Wikipedia)
https://en.wikipedia.org/wiki/RISC-V - David Patterson (Wikipedia)
https://en.wikipedia.org/wiki/David_Patterson_(computer_scientist) - OpenRISC (oficiální stránky projektu)
http://openrisc.io/ - OpenRISC architecture
http://openrisc.io/architecture.html - Emulátor OpenRISC CPU v JavaScriptu
http://s-macke.github.io/jor1k/demos/main.html - OpenRISC (Wikipedia)
https://en.wikipedia.org/wiki/OpenRISC - OpenRISC – instrukce
http://sourceware.org/cgen/gen-doc/openrisc-insn.html - OpenRISC – slajdy z přednášky o projektu
https://iis.ee.ethz.ch/~gmichi/asocd/lecturenotes/Lecture6.pdf - Berkeley RISC
http://en.wikipedia.org/wiki/Berkeley_RISC - Great moments in microprocessor history
http://www.ibm.com/developerworks/library/pa-microhist.html - Microprogram-Based Processors
http://research.microsoft.com/en-us/um/people/gbell/Computer_Structures_Principles_and_Examples/csp0167.htm - Great Microprocessors of the Past and Present
http://www.cpushack.com/CPU/cpu1.html - A Brief History of Microprogramming
http://www.cs.clemson.edu/~mark/uprog.html - What is RISC?
http://www-cs-faculty.stanford.edu/~eroberts/courses/soco/projects/2000–01/risc/whatis/ - RISC vs. CISC
http://www-cs-faculty.stanford.edu/~eroberts/courses/soco/projects/2000–01/risc/risccisc/ - RISC and CISC definitions:
http://www.cpushack.com/CPU/cpuAppendA.html - FPGA
https://cs.wikipedia.org/wiki/Programovateln%C3%A9_hradlov%C3%A9_pole - The Evolution of RISC
http://www.ibm.com/developerworks/library/pa-microhist.html#sidebar1