【问题标题】:SIMD rotate by variableSIMD按变量旋转
【发布时间】:2013-10-06 03:11:45
【问题描述】:

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

已知旋转是 64 位的倍数。

【问题讨论】:

    标签: assembly x86 x86-64 sse avx


    【解决方案1】:

    使用 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]);
    }
    

    【讨论】:

    • 澄清一下,这是rotate_right(__m256i x, unsigned n)
    • 仅使用 AVX 怎么样?我的电脑只有一个常春藤桥处理器。
    • 您可以使用带 4 个案例的开关。我认为 AVX 没有一种有效的方法。
    【解决方案2】:

    您可以使用 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]
    

    【讨论】:

      【解决方案3】:

      如果您仅限于 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);
      }
      

      【讨论】:

        【解决方案4】:

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

        _mm256_permute2f128_pd(x, x, 0x01)

        【讨论】:

        • 我其实不需要这个——只需要其他 3 个案例
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-11-26
        • 1970-01-01
        • 2015-01-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多