2

对整个 YMM 寄存器执行旋转操作的最快方法是什么,只有在运行时才知道数量?

已知旋转是 64 位的倍数。

4

4 回答 4

3

使用 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]);
}
于 2013-10-06T03:46:01.517 回答
1

如果您仅限于 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);
}
于 2013-10-28T01:20:47.413 回答
1

您可以使用 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]
于 2013-10-23T04:40:07.893 回答
0

有四种循环:0 位、64 位、128 位和 192 位。0 位是微不足道的。Felix Whyss 的解决方案适用于 64 位和 192 位的 AVX。但是对于 128 位旋转,您可以简单地交换高 128 位字和低 128 位字。这是 AVX 和 AVX2 的最佳解决方案。

_mm256_permute2f128_pd(x, x, 0x01)

于 2013-10-24T13:31:26.643 回答