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

Posted

技术标签:

【中文标题】仅使用 avx 而不是 avx2 转置 64 位元素【英文标题】:transpose of 64bit elements using only avx, not avx2 【发布时间】:2016-06-14 09:08:12 【问题描述】:

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

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

这是 使用 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 位通道上运行,并且不跨越通道边界。

任何人都可以产生更有效的实现吗?非常感谢!

【问题讨论】:

codereview.stackexchange.com - 我认为这是解决此类问题的更合适的网站。 @Gluttton:我不同意 - 优化问题在这里完全是主题 - codereview 更适合可以在惯用或风格上改进的工作代码。 @Ralf:考虑到 AVX 的所有限制,我怀疑你会改进当前的 3 指令解决方案,但也许有人会证明我错了。 @PaulR 非特定优化问题是代码审查的主题。不过,这个问题的标题中有一个具体的问题,所以我认为这是一个很好的 Stack Overflow 问题。 ***.com/questions/19516585/… 【参考方案1】:

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

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

(来源:参见 Agner Fog 的 insn 表,链接自 x86 标签 wiki。他的 Optimizing Assembly 指南也有一些不错的 shuffle 表,但没有关注 AVX/AVX2 的车道内挑战。)


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

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

无论如何,由于上面的lane需要从原始向量中获取其高元素,而low lane需要从perm128向量中获取其高元素,因此src1、src2排序的选择都不能满足我们的需要。


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

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

【讨论】:

以上是关于仅使用 avx 而不是 avx2 转置 64 位元素的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Ubuntu 20.04 64 位在 VirtualBox 6.1.16 中启用 AVX / AVX2?

AVX2 浮点比较并得到 0.0 或 1.0 而不是全 0 或全 1 位

AVX2 上的 256 位 CRC 计算

AVX/AVX2 是不是“存在”在每个内核上?

英特尔 AVX2 组装开发

AVX2中的按位非/补码[重复]