Die Rotation um 8x8 wird effizienter durch SIMD- oder SWAR-Techniken durchgeführt, die mindestens 64 Bits gleichzeitig lesen können.
Rot90Left(X) = flip_vertical(transpose(X))
Rot90Right(X) = transpose(flip_vertical(X))
Die vertikale Umkehrung ist eine Null-Kosten-Operation, da sie nur das Speichern/Lesen vom anderen Ende der temporären Variablen bedeutet. Wenn die SSE/SIMD-Implementierung der Transponierung nicht verwendet werden kann, hat sich dieser Kernel auf x64 und arm64-v8 als recht schnell erwiesen.
inline void transpose_u8(uint64_t *a, uint64_t *b) {
uint64_t A = *a, B = *b, C = B ^ (A>>8)) & 0x00ff00ff00ff00ffull;
*a = A ^ (C << 8);
*b = B ^ C;
}
inline void transpose_u16(uint64_t *a, uint64_t *b) {
uint64_t A = *a, B = *b, C = B ^ (A>>16)) & 0x0000ffff0000ffffull;
*a = A ^ (C << 16);
*b = B ^ C;
}
inline void transpose_u32(uint64_t *a, uint64_t *b) {
uint64_t A = *a, B = *b, C = B ^ (A>>32)) & 0x00000000ffffffffull;
*a = A ^ (C << 32);
*b = B ^ C;
}
void transpose8x8(uint8_t *src, int skip0, uint8_t *dst, int skip1) {
uint64_t d[8];
for (int x = 0; x < 8; x++)
memcpy(d+(x ^ LEFT), src + x * skip0);
transpose_u8(d+0, d+1);
transpose_u8(d+2, d+3);
transpose_u8(d+4, d+5);
transpose_u8(d+6, d+7);
transpose_u16(d+0, d+2);
transpose_u16(d+1, d+3);
transpose_u16(d+4, d+6);
transpose_u16(d+5, d+7);
transpose_u32(d+0, d+4);
transpose_u32(d+1, d+5);
transpose_u32(d+2, d+6);
transpose_u32(d+3, d+7);
for (int x = 0; x < 8; x++)
memcpy(dst + x * skip1, d + (x ^ RIGHT));
}
-
Hier erfolgt die Rechtsdrehung durch die Einstellung LEFT=0, RIGHT=7
-
Linksdrehung == LEFT=7, RIGHT = 0
-
Transponieren = LINKS=0, RECHTS=0
Meine Hypothese ist, dass jeder anständige Compiler alle internen Speicherlesungen in der transpose_uXX
Funktionen durch direkte Änderung der in Registern gespeicherten Variablen und ersetzt die memcpy
durch einzelnes 64-Bit-Lesen oder -Schreiben in den Speicher - dies sollte zumindest bei 64-Bit-Architekturen geschehen. Auf pre-x86-Architekturen gibt es nicht genügend Register, und die praktische Alternative besteht darin, jeden verfügbaren SIMD-Register- und Befehlssatz zu verwenden.