2016-01-28 15 views
6

Ich entwickle Bildverarbeitungsalgorithmen (mit GCC, Targeting ARMv7 (Raspberry Pi 2B)).Schnelles Suchen/Ersetzen von übereinstimmenden einzelnen Bytes in einem 8-Bit-Array, auf ARM

Insbesondere verwende ich einen einfachen Algorithmus, der Index in einer Maske ändert:

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; 
    } 
} 

Leider für die Zielplattform hat schlechte Leistung.

Gibt es eine Möglichkeit, es zu optimieren?

+1

nicht sofort ersichtlich, wie man die schneller - es Tricks sein, wenn Sie mehr über die Daten wissen - zum Beispiel, könnten Sie eine Liste von Zellen enthalten, Wert 'X' - aber das ist nur dann nützlich, wenn die Anzahl der" Hits "ziemlich niedrig ist - wenn Sie die meisten Einträge in' mask' mit 'oldIndex' treffen, ist es unwahrscheinlich, dass sie schneller werden. Welchen Wert hat "Größe" und wie viele Prozent der Tabelle hat im Durchschnitt den Wert "oldIndex"? –

+0

Welche Compiler-Optionen verwenden Sie? Stellen Sie sicher, dass Sie es angewiesen haben, NEON-Anweisungen zu verwenden ('-mfpu = neon-vfpv4', denke ich), andernfalls erzeugt es Code, der mit älteren CPUs kompatibel ist, die keinen NEON haben. – Gilles

+0

Sie sollten auch eine Beschleunigung mit dem ternären Operator bekommen: 'mask [i] = (mask [i] == oldIndex)? newIndex: mask [i]; ' – Miki

Antwort

13

Die ARMv7-Plattform unterstützt SIMD-Anweisungen mit der Bezeichnung NEON. Mit Gebrauch von ihnen können Sie schneller Code:

#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; 
    } 
} 
+0

Ich habe deine Version des Algorithmus überprüft. Es funktioniert in 5 mal schneller als die ursprüngliche Version. Es ist großartig! –

+0

Sie könnten eine weitere, kleinere Geschwindigkeitsverbesserung erzielen, indem Sie direkt mit dem 'mask'-Zeiger anstatt mit 'mask + i' arbeiten. Zuerst berechnen Sie Ihren Endpunkt 'uint8_t * maskEnd = mask + i; 'und ändern dann die for-Schleifen so, dass sie direkt mit Ihrem Zeiger arbeiten, z. 'for (; mask

+0

Sie könnten dies wahrscheinlich noch schneller machen, indem Sie die Neon-Baugruppe direkt schreiben. Die Neon-Intrinsics von IME GCC sind nicht sehr schnell, weil sie immer wieder zwischen Neon und Hauptregistern hin- und herfahren, was die Pipeline blockiert. (Vielleicht haben sie das behoben, seit ich sie zum letzten Mal benutzt habe.) –