Sto imparando a utilizzare le funzionalità di SIMD riscrivendo la mia libreria di elaborazione di immagini personali utilizzando gli elementi intrinseci del vettore. Una funzione di base è un semplice "matrice +=
", cioèSIMD array add per lunghezze arbitrarie di array
void arrayAdd(unsigned char* A, unsigned char* B, size_t n) {
for(size_t i=0; i < n; i++) { B[i] += A[i] };
}
Per lunghezze degli array arbitrario, il codice SIMD ovvio (supponendo allineato di 16) è qualcosa di simile:
size_t i = 0;
__m128i xmm0, xmm1;
n16 = n - (n % 16);
for (; i < n16; i+=16) {
xmm0 = _mm_load_si128((__m128i*) (A + i));
xmm1 = _mm_load_si128((__m128i*) (B + i));
xmm1 = _mm_add_epi8(xmm0, xmm1);
_mm_store_si128((__m128i*) (B + i), xmm1);
}
for (; i < n; i++) { B[i] += A[i]; }
Ma è possibile fare all le aggiunte con le istruzioni SIMD? Ho pensato di provare questo:
__m128i mask = (0x100<<8*(n - n16))-1;
_mm_maskmoveu_si128(xmm1, mask, (__m128i*) (B + i));
per gli elementi aggiuntivi, ma questo si tradurrà in un comportamento non definito? Lo mask
dovrebbe garantire che nessun accesso venga effettivamente effettuato oltre i limiti dell'array (credo). L'alternativa è fare prima gli elementi extra, ma poi l'array deve essere allineato da n-n16
, che non sembra giusto.
Esiste un altro schema più ottimale di tali cicli vettoriali?
si potrebbe garantire che nel codice le lunghezze degli array sono sempre multipli di 16 byte (anche se forse meno elementi sono effettivamente utilizzati), quindi questo epilogo non viene in su. Ma l'epilogo non è davvero importante in termini di velocità. – Walter