仅使用 avx 而不是 avx2 转置 64 位元素

transpose of 64bit elements using only avx, not avx2

我想仅使用 avx 而不是 avx2 实现 64 位转置操作。它应该这样做:

// in  = Hh Hl Lh Ll
//        |   X   |
// out = Hh Lh Hl Ll

这是 with avx2:

的样子
#define SIMD_INLINE inline __attribute__ ((always_inline))

static SIMD_INLINE __m256i
x_mm256_transpose4x64_epi64(__m256i a)
{
  return _mm256_permute4x64_epi64(a, _MM_SHUFFLE(3,1,2,0));
}

这是最有效的解决方法没有我能想到的 avx2(使用 3 条 avx 指令):

static SIMD_INLINE __m256i
x_mm256_transpose4x64_epi64(__m256i a)
{
  __m256d in, x1, x2;
  // in = Hh Hl Lh Ll
  in = _mm256_castsi256_pd(a);
  // only lower 4 bit are used
  // in = Hh Hl Lh Ll
  //       0  1  0  1  = (0,0,1,1)
  // x1 = Hl Hh Ll Lh
  x1 = _mm256_permute_pd(in, _MM_SHUFFLE(0,0,1,1));
  // all 8 bit are used
  // x1 = Hl Hh Ll Lh
  //       0  0  1  1
  // x2 = Ll Lh Hl Hh
  x2 = _mm256_permute2f128_pd(x1, x1, _MM_SHUFFLE(0,0,1,1));
  // only lower 4 bit are used
  // in = Hh Hl Lh Ll
  // x2 = Ll Lh Hl Hh
  //       0  1  1  0 = (0,0,1,2)
  // ret: Hh Lh Hl Ll
  return _mm256_castpd_si256(_mm256_blend_pd(in, x2, _MM_SHUFFLE(0,0,1,2)));
}

问题是大多数 avx swizzle 操作(例如解包)都在 128 位通道上运行并且不跨越通道边界。

谁能提供更高效的实施方案?非常感谢!

我认为 3 条指令是你能做的最好的。 _mm256_blend_pd 非常便宜(如 vblendpsvpblendd),运行 在 SnB/IvB 的 2 个端口上,以及 Haswell 和更高版本中的所有 3 个矢量执行端口。 (即与向量 XOR 或 AND 一样便宜。)其他两个都需要随机端口,这是不可避免的。

vblendpd 将其数据从 FP 域转发到整数指令时,您将在 SnB 系列 CPU 上有 1 个周期的旁路延迟。尽管使用 AVX1,但没有任何 256b 整数指令可以转发到。

(来源:参见 Agner Fog 的 insn 表,链接自 标签 wiki。他的优化装配指南也有一些不错的洗牌表,但没有关注 AVX/AVX2.)


这种模式几乎可以用两条指令实现,但不完全是。

vshufpd (_mm256_shuffle_pd) 为您提供通道内 2 源随机播放,但对数据移动有限制。与最初的 SSE2 版本一样,每个目标元素只能来自一个固定的源元素。 8 位立即数有空间对来自四个源元素的两个选择进行编码,但它们使硬件保持简单并且只为每个目标元素使用一个 1 位选择器。 256b 版本确实允许对每个 128b 通道进行不同的洗牌,因此 imm8 的 4 位对于 vpshufd ymm.

很重要

总之,由于上路需要从原始向量中取其高位元素,而低路需要从perm128向量中取其高位元素,因此无论选择src1,src2顺序都不能满足我们的需要。


vshufpd 我认为编码比 vpermilpd imm8 短一个字节。 vpermilps / vpermilpd 的直接形式的唯一用例似乎是加载和随机播放。 (vshufpd 仅在两个源操作数相同时用作完整的通道内洗牌)。 IDK if vpermildp 可能会使用更少的能源或其他东西,因为它只有一个来源。

当然,编译器可以使用他们想要完成工作的任何指令;他们被允许使用内在函数优化代码,就像使用 + 运算符(并不总是编译成 add 指令)优化代码一样。 Clang 实际上确实基本上忽略了使用内在函数进行指令选择的尝试,因为它以自己的内部格式表示随机播放,并对其进行了优化。