SIMD types

Questa traduzione è incompleta. Collabora alla traduzione di questo articolo dall’originale in lingua inglese.

L'API sperimentale Javascript SIMD introduce il vettore oggetti che utilizza le istruzioni SIMD/SSE per le CPU che le supportano; SIMD è in sostanza per Istruzioni Singole / Dati Multipli. Le operazioni SIMD sono metodi che processano dati multipli con una singola istruzione. Al contrario, le operazioni scalari (SISD) processano solo un dato individuale con una singola istruzione.

Processando un data set con una singola istruzione puoi raggiungere grandi perfomance per la tua applicazione. Il quanto dipende dalla dimensione del data set (o vector, o packed data) tra le altre cose. Così, le operazioni SIMD vengono ampiamente usate per la grafica 3d ed il processamento audio/video, per simulazioni di fisica, per la crittografia, e per altri ambiti.

Lo svantaggio delle SIMD, che dipende dal fatto che gli algoritmi necessitano di essere progettati per le SIMD stesse, è che i data set impacchettati non possono essere processati differentemente mentre il tuo algoritmo spesso ha la necessità di processare differenti dati in modo diverso. Impareremo successivamente in questo articolo come possiamo lavorare con queste maschere e come re-allineare i nostri dati per instradarci nella risoluzione di questo problema.

La memoria condivisa o il parallelismo a livello di thread consente di ottenere percorsi (di calcolo) paralleli su dati multipli e con istruzioni multiple (MIMD). La programmazione in parallelo risulta più semplice con queste tecniche, ma questi concetti possono essere utilizzari anche assieme a SIMD. Per esempio, potresti immaginare di avere una vettorizzazione di tipo SIMD che viene utilizzata in ogni thread del tuo programma. La Mandelbrot demo, di Peter Jensen (Intel), dimostra l'incremento di prestazioni con SIMD e i Web Workers, per esempio.

SIMD in JavaScript

Le API JavaScript SIMD consistono in parecchi nuovi data types ed operations che ti consentono di usare le istruzioni SIMD da JavaScript. I browser forniscono implementazioni altamente ottimizzate di queste API le quali dipendono dall'hardware sottostante dell'utente. Attualmente, le API JS SIMD sono specificatamente modellate per le piattaforme ARMv7 con NEON e per x86 con SSE.

Diamo ora un'occhiata, per esempio, al data type SIMD SIMD.Float32x4. Un vettore SIMD è costituito da unità di dati multipli, che vengono chiamate lanes (corsie). Un registro SIMD per l'API corrente è grande 128-bit. Per un vettore di lunghezza 4 (x4), ci sono quindi 4 data type di tipo Float32 specifici per ogni "corsia" i quali vengono chiamati rispettivamente x, y, z, and w. Ora, invece di dover eseguire 4 operazioni separate, una per ogni corsia, SIMD permette di eseguire l'operazione su tutte e 4 le corsie simultaneamente.

Nella figura seguente, c'è solo una singola istruzione (addizione) così che il dato possa essere processato usando SIMD:

SISD SIMD

Figura 1 e 2: comparazione tra SISD e SIMD.

Il codice scalare SSID potrebbe essere questo (senza alcun loop, solo a titolo esemplificativo):

var a = [1, 2, 3, 4];
var b = [5, 6, 7, 8];
var c = [];

c[0] = a[0] + b[0];
c[1] = a[1] + b[1];
c[2] = a[2] + b[2];
c[3] = a[3] + b[3];
c; // Array[6, 8, 10, 12]

Ora, usando SIMD:

var a = SIMD.Float32x4(1, 2, 3, 4);
var b = SIMD.Float32x4(5, 6, 7, 8);
var c = SIMD.Float32x4.add(a, b); // Float32x4[6, 8, 10, 12]

Questo aggiunge i valori nelle 4 corsie simultaneamente e ritorna un nuovo SIMD con type Float32 con tutte le corsie aggiunte.

Come puoi vedere nelle 3 corsie di codice SIMD, un set di funzioni JavaScript consente di creare data types impacchettati e ti da accesso alle istruzioni vettorizzate (aggiunte qui). Nel momento in cui scriviamo questo articolo, non c'è nessun sovraccarico dell'operatore (es. a `+` segno) implementato per alleviare la scrittura di un codice SIMD come questo. Però, l'API JavaScript SIMD non è ancora finita ed è pianificato di includere l'overloading per l'operatore in uno dei prossimi "drafts". Puoi seguire lo sviluppo delle specifiche nell' ecmascript_simd GitHub repository.

Re-allineare i dati per migliorare i vettori SIMD

Spesso ci sono array che servono come dati di ingresso per vettori SIMD. Tuttavia, la struttura di questi array potrebbe non sempre essere adatta per operazioni SIMD. Diamo uno sguardo, per esempio, ai dati dei colori RGBA nelle immagini.

Grazie al metodo CanvasRenderingContext2D.getImageData() nel Canvas e alla proprietà ImageData.data puoi ottenere i dati dei pixel sottostanti espressi con un array ad una dimensione, che contiene i dati dell'immagine nello spazio RGBA con i valori interi espressi tra 0 e 255 (incluso).

[R, G, B, A, R, G, B, A, R, G, B, A, ...]

Se ora vogliamo processare questa immagine, per esempio calcolando il rapporto tra luminanza percepita /scala di grigi con la formula Y = 0.299r + 0.587g + 0.114b, dobbiamo ristrutturare i dati per SIMD. L'idea è di elaborare i differenti pesi per r, g, e in un formato adatto a SIMD con una sola istruzione di dati sui colori. Questo si potrebbe fare come:

[R, R, R, R, R, R, ...] * 0.299 +
[G, G, G, G, G, G, ...] * 0.587 +
[B, B, B, B, B, B, ...] * 0.114 =
[Y, Y, Y, Y, Y, Y, ...]

Parallelizzare rami condizionali

Nel codice scalare, i rami basati su condizioni sono utilizzati per controllare il flusso di elaborazione come nel seguente esempio:

var a = [1, 2, 3, 4];
var b = [5, 6, 7, 8];
var c = [];

for (var i = 0; i < 4; i++) {
  if (a[i] < 3) {
    c[i] = a[i] * b[i];
  } else {
    c[i] = b[i] + a[i];
  }
}

console.log(c); // [5, 12, 10, 12]

Non vogliamo comporre vettori SIMD per ogni ramo ed eseguirli in sequenza. SIMD fornisce un modo più efficiente utilizzando maschere di selezione.

Ramificare, mascherare, selezionare

Il metodo SIMD.%type%.select() seleziona le corsie da una maschera di selezione. Questo ti permette di creare i rami in modo da poter operare su una frazione di dati in tipi di dati SIMD. Nell'immagine seguente, una maschera personalizzata seleziona un risultato dai vettori SIMD A e B:

Le maschere booleane possono essere restituite da diverse funzioni di confronto, oppure è possibile utilizzare uno dei tipi booleani SIMD per creare la propria maschera.

Con questa tecnica si possono riscrivere i rami scalari dall'ultimo esempio di codice utilizzando la funzione select(), eseguendo la moltiplicazione e l'addizione in parallelo:

var a = SIMD.Float32x4(1, 2, 3, 4);
var b = SIMD.Float32x4(5, 6, 7, 8);

var mask = SIMD.Float32x4.lessThan(a, SIMD.Float32x4.splat(3));
// Bool32x4[true, true, false, false]

var result = SIMD.Float32x4.select(mask, 
                                   SIMD.Float32x4.mul(a, b),
                                   SIMD.Float32x4.add(a, b));

console.log(result); // Float32x4[5, 12, 10, 12]

In questa versione SIMD dell'esempio precedente, i dati vengono messi di nuovo in vettori SIMD. Quindi, al fine di creare un ramo base ad una condizione, viene usata la funzione SIMD.Float32x4.lessThan(). Questa restituisce una maschera di selezione con i valori booleani a seconda di quale corsia sia true o false in questo confronto. Il primo comparando è il vettore a ed il secondo comparando è creato dalla funzione splat , che setta tutte le 4 corsie a 3. Questo rende il confronto lo stesso come nella versione scalare (a[i] < 3).

Per ottenere il risultato reale dalla maschera di selezione, viene utilizzata la funzione select. Prende tre parametri: il primo è la maschera, il secondo parametro è TrueValue. Se la corsia delle maschera selettore è true, il valore corrispondente della corsia viene prelevato da TrueValue. In caso contrario, i valori di corsia si raccolgono dal parametro tre, il falseValue.

Di più sugli algoritmi SIMD ed i loro casi di utilizzo

In generale, i dati che possono essere elaborati con lo stesso insieme di istruzioni possono altamente beneficiare delle operazioni SIMD. I seguenti algoritmi e casi di utilizzo possono trarre notevoli benefici dalle operazioni di tipo SIMD:

Lo stato attuale di SIMD in JavaScript

L'API SIMD è disponibile nella versione più recente di Firefox Nightly. C'è in una Preview Build di Microsoft Edge e c'è un "intento di implementazione" in Blink/Chromium.

Le  specifiche SIMD sono in fase di sviluppo e ne è stata proposta l'inclusione in ECMAScript 2016.

Un'implementazione specifica per Polyfill basata su typed arrays è disponibile su GitHub all'indirizzo ecmascript_simd.

Vedere anche

Tag del documento e collaboratori

 Hanno collaborato alla realizzazione di questa pagina: giticon
 Ultima modifica di: giticon,