Ich lerne SIMD-Fähigkeiten zu nutzen, indem ich meine persönliche Bildverarbeitungsbibliothek mit Vektor-Intrinsics umschreibe. Eine Grundfunktion ist eine einfache "array +=
", dhSIMD-Array für beliebige Array-Längen hinzufügen
void arrayAdd(unsigned char* A, unsigned char* B, size_t n) {
for(size_t i=0; i < n; i++) { B[i] += A[i] };
}
Für beliebige Array Längen, die offensichtliche Code SIMD (um 16 ausgerichtet angenommen wird) ist so etwas wie:
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]; }
Aber ist es möglich, tun alle die Ergänzungen mit SIMD Anweisungen? Ich dachte daran, dies zu versuchen:
__m128i mask = (0x100<<8*(n - n16))-1;
_mm_maskmoveu_si128(xmm1, mask, (__m128i*) (B + i));
für die zusätzlichen Elemente, aber wird das zu undefiniertem Verhalten führen? Die mask
sollte garantieren, dass kein Zugriff über die Array-Grenzen hinaus erfolgt (glaube ich). Die Alternative besteht darin, die zusätzlichen Elemente zuerst auszuführen, aber dann muss das Array um n-n16
ausgerichtet werden, was nicht richtig erscheint.
Gibt es ein anderes, optimaleres Muster wie vektorisierte Schleifen?
Sie, dass die Feldlängen immer ein Vielfaches von 16 Bytes im Code sicherstellen könnten (obwohl möglicherweise weniger Elemente tatsächlich verwendet werden), so dass dieser Epilog nie kommt. Aber der Epilog ist in Bezug auf die Geschwindigkeit wirklich nicht wichtig. – Walter