给定一个2D的4x8的nibble矩阵,用16字节的uint8_t数组表示。对于每一对nibbles i和j,字节计算如下:
例如,给定以下矩阵:
表示为:
所需的数组应为:
如何实现最有效率的函数?可以使用AVX2扩展。
这是我目前基于 Nibble shuffling with x64 SIMD的C实现。它将矩阵分成两个64位输入,解压半字节,重新排列并重新打包。
(j << 4) | i
。例如,给定以下矩阵:
0 1 2 3 3 7 1 9
4 5 6 7 4 1 6 15
8 9 10 11 3 14 6 11
12 13 14 15 8 10 7 4
表示为:
const uint8_t matrix[] = {
0x10, 0x32, 0x73, 0x91,
0x54, 0x76, 0x14, 0xf6,
0x98, 0xba, 0xe3, 0xb6,
0xdc, 0xfe, 0xa8, 0x47,
};
所需的数组应为:
const uint8_t result[] = {
0x40, 0xc8, 0x51, 0xd9,
0x62, 0xea, 0x73, 0xfb,
0x43, 0x83, 0x17, 0xae,
0x61, 0x76, 0xf9, 0x4b,
}
如何实现最有效率的函数?可以使用AVX2扩展。
这是我目前基于 Nibble shuffling with x64 SIMD的C实现。它将矩阵分成两个64位输入,解压半字节,重新排列并重新打包。
__m128i unpack_nibbles(__m128i src) {
__m128i nibbles_hi = _mm_srli_epi64(src, 4);
//Interlave high nibbles with full nibbles [0000 hi, hi lo, ...] and clear high
__m128i unpacked = _mm_unpacklo_epi8(src, nibbles_hi);
return _mm_and_si128(unpacked, _mm_set1_epi8(0xf));
}
void transpose_4x8_nibbles(uint8_t *src, uint8_t *dst) {
uint8_t *src_lo = src + 0x8;
__m128i data_hi = _mm_loadl_epi64((__m128i*)src);
__m128i data_lo = _mm_loadl_epi64((__m128i*)src_lo);
data_hi = unpack_nibbles(data_hi);
data_lo = unpack_nibbles(data_lo);
//Transpose
__m128i transpose_mask = _mm_setr_epi8(0, 0x8, 0x1, 0x9, 0x2, 0xa, 0x3, 0xb, 0x4, 0xc, 0x5, 0xd, 0x6, 0xe, 0x7, 0xf);
data_hi = _mm_shuffle_epi8(data_hi, transpose_mask);
data_lo = _mm_shuffle_epi8(data_lo, transpose_mask);
//Pack nibbles
__m128i pack_mask = _mm_set1_epi16(0x1001);
data_hi = _mm_maddubs_epi16(data_hi, pack_mask); //even bytes are multiplied by 0x10, odd bytes by 0x01
data_lo = _mm_maddubs_epi16(data_lo, pack_mask);
__m128i data = _mm_packus_epi16(data_hi, data_lo);
data = _mm_shuffle_epi8(data, transpose_mask);
_mm_store_si128((__m128i*) dst, data);
}
void*
指针数学运算是GNU扩展(类似于char*
),但问题已经被编辑为可移植的。我们可以删除这些评论。 - Peter Cordes0x40
,来自每个输入字节的低半字节(前两行的开头)0x10
;0x54
。也许将一个输入左移4位可以与2个混合配合使用? - Peter Cordespshufb
、1个pmaddwd
和4个位运算(不幸的是,pmaddwd
需要有符号输入)。我会在今天晚些时候写出答案。 - chtz