我想仅使用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混合操作(例如解压缩)都在128位通道上运行,并且不会跨越通道边界。
任何人都可以提高效率吗?非常感谢!
答案 0 :(得分:4)
我认为3条指令是你能做的最好的。 _mm256_blend_pd
非常便宜(如vblendps
和vpblendd
),在SnB / IvB中的2个端口上运行,以及Haswell及更高版本中的所有3个向量执行端口。 (即与矢量XOR或AND一样便宜。)其他两个都需要shuffle端口,这是不可避免的。
当vblendpd
将其数据从FP域转发为整数指令时,SnB系列CPU的旁路延迟为1个周期。虽然使用AVX1,但没有任何256b整数指令可以转发。
(来源:参见Agner Fog的insn表,链接自x86标签wiki。他的优化装配指南也有一些很好的洗牌表,但并不专注于 - AVX / AVX2的车道挑战。)
这种模式几乎可以通过两条指令实现,但并不完全。
vshufpd
(_mm256_shuffle_pd
)为您提供了一个内置2源shuffle,但数据移动受到限制。与原始SSE2版本一样,每个目标元素只能来自固定的源元素。 8位立即数有空间对来自四个源元素的两个选项进行编码,但它们保持硬件简单,并且每个dest元素仅使用1位选择器。对于每个128b通道,256b版本允许不同的随机播放,因此对于vpshufd ymm
,imm8的4位是重要的。
无论如何,由于上部通道需要从原始部分获取其高元素,但是低通道需要从perm128向量中获取其高元素,因此src1,src2排序的选择都不能满足我们的需要。
vshufpd
我认为编码比vpermilpd imm8
短一个字节。 vpermilps
/ vpermilpd
的直接形式的唯一用例似乎是一种加载和改变。 (vshufpd
仅在两个源操作数相同时作为完整的通道内洗牌工作)。 IDK如果vpermildp
可能会使用更少的能量或其他东西,因为它只有一个来源。
当然,编译器可以使用他们想要的任何指令来完成工作;他们允许使用内在函数优化代码,就像使用+
运算符(它不总是编译为add
指令)优化代码一样。 Clang实际上基本上忽略了使用内在函数进行指令选择的尝试,因为它以自己的内部格式表示shuffle,并对它们进行优化。