对整个 YMM 寄存器执行旋转操作的最快方法是什么,只有在运行时才知道数量?
已知旋转是 64 位的倍数。
使用 AVX2,您可以使用_mm256_permutevar8x32_epi32
. 伪代码(未经测试,常量可能是错误的):
static inline __m256i rotate(__m256i x, unsigned n) {
static const __m256i rotspec[4] = {
_mm256_set_epi32(0, 1, 2, 3, 4, 5, 6, 7),
_mm256_set_epi32(6, 7, 0, 1, 2, 3, 4, 5),
_mm256_set_epi32(4, 5, 6, 7, 0, 1, 2, 3),
_mm256_set_epi32(2, 3, 4, 5, 6, 7, 0, 1)
};
return _mm256_permutevar8x32_epi32(x, rotspec[n]);
}
如果您仅限于 AVX 指令,您仍然可以使用条件混合指令 ( VBLENDVPD
) 来选择正确的旋转,而无需使用开关。这可能更快,尤其是在无法轻松预测条件的情况下。
正确旋转的完整实现(已测试):
// rotate packed double vector right by n
__m256d rotate_pd_right(__m256d x, int n) {
__m128i c = _mm_cvtsi32_si128(n);
__m128i cc = _mm_unpacklo_epi64(c,c);
// create blend masks (highest bit)
__m128d half_low = _mm_castsi128_pd(_mm_slli_epi64(cc, 63));
__m128d swap_low = _mm_castsi128_pd(_mm_slli_epi64(cc, 62));
__m256d half = _mm256_insertf128_pd(_mm256_castpd128_pd256(half_low), half_low, 1);
__m256d swap = _mm256_insertf128_pd(_mm256_castpd128_pd256(swap_low), swap_low, 1);
// compute rotations
__m256d t0 = _mm256_permute_pd(x, 0x05); // [2 3 0 1]
__m256d t1 = _mm256_permute2f128_pd(t0, t0, 0x01); // [1 0 2 3]
__m256d y0 = x; // [3 2 1 0]
__m256d y1 = _mm256_blend_pd(t0, t1, 0x0a); // [0 3 2 1]
__m256d y2 = _mm256_permute2f128_pd(x, x, 0x01); // [1 0 3 2]
__m256d y3 = _mm256_blend_pd(t0, t1, 0x05); // [2 1 0 3]
// select correct rotation
__m256d y01 = _mm256_blendv_pd(y0, y1, half);
__m256d y23 = _mm256_blendv_pd(y2, y3, half);
__m256d yn = _mm256_blendv_pd(y01, y23, swap);
return yn;
}
左旋转可以简单地完成
__m256d rotate_pd_left(__m256d x, int n) {
return rotate_pd_right(x, -n);
}
您可以使用 AVX 向右旋转,如下所示。假设您的输入是x
:
__m256d t0 = _mm256_permute_pd(x, 0x05); // [x2 x3 x0 x1]
__m256d t1 = _mm256_permute2f128_pd(t0, t0, 0x01); // [x0 x1 x2 x3]
__m256d y = _mm256_blend_pd(t0, t1, 0x0a); // [x0 x3 x2 x1]
结果在y
. 通过反转混合蒙版,您可以向左旋转:
__m256d t0 = _mm256_permute_pd(x, 0x05); // [x2 x3 x0 x1]
__m256d t1 = _mm256_permute2f128_pd(t0, t0, 0x01); // [x0 x1 x2 x3]
__m256d y = _mm256_blend_pd(t0, t1, 0x05); // [x2 x1 x0 x3]
有四种循环:0 位、64 位、128 位和 192 位。0 位是微不足道的。Felix Whyss 的解决方案适用于 64 位和 192 位的 AVX。但是对于 128 位旋转,您可以简单地交换高 128 位字和低 128 位字。这是 AVX 和 AVX2 的最佳解决方案。
_mm256_permute2f128_pd(x, x, 0x01)