2016-01-28 15 views
6

Sviluppo algoritmi di elaborazione immagini (utilizzando GCC, targeting per ARMv7 (Raspberry Pi 2B)).Ricerca rapida/sostituzione di singoli byte corrispondenti in un array a 8 bit, su ARM

In particolare utilizzare un semplice algoritmo, che cambia indice in una maschera:

void ChangeIndex(uint8_t * mask, size_t size, uint8_t oldIndex, uint8_t newIndex) 
{ 
    for(size_t i = 0; i < size; ++i) 
    { 
     if(mask[i] == oldIndex) 
      mask[i] = newIndex; 
    } 
} 

Sfortunatamente ha scarse prestazioni per la piattaforma di destinazione.

C'è un modo per ottimizzarlo?

+1

immediatamente evidente come fare che più veloce - ci possono essere trucchi, se si sa di più sui dati - per esempio, si potrebbe avere un elenco di celle che contengono valore 'X' - ma questo è davvero utile solo se il numero di" colpi "è piuttosto basso - se stai colpendo la maggior parte delle voci in' mask' che corrisponde a 'oldIndex', allora è improbabile che acceleri. Quale valore è 'size' e quale percentuale della tabella ha valore' oldIndex' in media? –

+0

Quali opzioni del compilatore stai usando? Assicurati di averlo istruito a usare le istruzioni NEON ('-mfpu = neon-vfpv4', credo), altrimenti potrebbe generare codice compatibile con le vecchie CPU che non hanno NEON. – Gilles

+0

Si dovrebbe anche ottenere un po 'di velocità usando l'operatore ternario: 'mask [i] = (mask [i] == oldIndex)? newIndex: mask [i]; ' – Miki

risposta

13

La piattaforma ARMv7 supporta le istruzioni SIMD denominate NEON. Con l'uso di essi è possibile farvi un codice più veloce:

#include <arm_neon.h> 

void ChangeIndex(uint8_t * mask, size_t size, uint8_t oldIndex, uint8_t newIndex) 
{ 
    size_t alignedSize = size/16*16, i = 0; 

    uint8x16_t _oldIndex = vdupq_n_u8(oldIndex); 
    uint8x16_t _newIndex = vdupq_n_u8(newIndex); 

    for(; i < alignedSize; i += 16) 
    { 
     uint8x16_t oldMask = vld1q_u8(mask + i); // loading of 128-bit vector 
     uint8x16_t condition = vceqq_u8(oldMask, _oldIndex); // compare two 128-bit vectors 
     uint8x16_t newMask = vbslq_u8(condition, _newIndex, oldMask); // selective copying of 128-bit vector 
     vst1q_u8(mask + i, newMask); // saving of 128-bit vector 
    } 

    for(; i < size; ++i) 
    { 
     if(mask[i] == oldIndex) 
      mask[i] = newIndex; 
    } 
} 
Non
+0

Ho controllato la tua versione dell'algoritmo. Funziona in 5 volte più velocemente rispetto alla versione originale. È ottimo! –

+0

Si potrebbe ottenere un ulteriore miglioramento minore, lavorando direttamente con il puntatore 'maschera', piuttosto che con' maschera + i'. Prima precalcola il tuo endpoint 'uint8_t * maskEnd = mask + i;' quindi modifica i cicli for per lavorare direttamente con il puntatore, ad es. 'for (; mask

+0

Probabilmente potresti renderlo ancora più veloce scrivendo direttamente l'assemblea Neon. Gli intrinseci al neon di IMC GCC non sono molto veloci perché continuano a spostare materiale tra i registri Neon e principale, che blocca la conduttura. (Forse hanno risolto il problema dall'ultima volta che li ho usati, però.) –