与 SIMD 内在函数进行比较和交换

Posted

技术标签:

【中文标题】与 SIMD 内在函数进行比较和交换【英文标题】:Compare and swap with SIMD intrinsics 【发布时间】:2015-08-27 12:29:45 【问题描述】:

如果发生某种情况,是否可以在 SIMD 指令中进行比较并交换值。换句话说,我有 4 个整数:

(100 5) (1 42)

我想收到:

(5 100) (1 42)

即我想成对比较(第一个值与第二个值,第三个与第四个值),如果左操作数更大 - 交换值。是否可以只使用 1 个 SIMD?

P.S.:这是我第一次尝试 SIMD,可能我使用了错误的术语 - 如果我错了,请纠正我。

【问题讨论】:

您正在研究 SIMD(或其他 wtv)的“明显方法”有什么问题? 是的,对不起,我忘了说我在做什么是为了学习。我正在尝试实现不同的排序算法并尽可能加快速度。 在不知道要谈论哪一组 SIMD(例如 SSE/AVX 或其他东西)的情况下,他们很难谈论 SIMD? 我不知道 - 有没有办法用 Visual Studio 进行检查?我知道我有 Intel Core i7 4930K - 它似乎支持 AVX,但我不知道是哪个版本。 @nikitablack AVX(1)(所以,不是 2)因为它是常春藤 【参考方案1】:

对于支持 AVX2 的系统,有一个使用最小/最大并与 imm 混合的解决方案(它有 1 个周期延迟,而变量 1 有 2 个周期)。

以下代码有 3 个周期的延迟,并且在 HSW+ 上的吞吐量应该少于 2 个周期

__m128i tmp = _mm_shuffle_epi32(in, _MM_SHUFFLE(2,3,0,1));
__m128i min = _mm_min_epi32(in, tmp);
__m128i max = _mm_max_epi32(in, tmp);
// __m128i res = _mm_blend_epi32(min, max, 0xA); // AVX2 only
__m128i res = _mm_blend_epi16(min, max, 0xCC);   // SSE4.1

我已经在我的 HSW 系统上对其进行了测试(处理 20000 对 100K 次),它的性能比 stgatilov 的代码高约 26%

CMP + VARIABLE BLEND    1.18sec
MIN/MAX + BLEND_32      0.87sec // AVX2 only code
MIN/MAX + BLEND_PS      0.86sec // SSE
MIN/MAX + PLEND_16      0.88sec // Preferred for SSE

更新:根据stgatilov'下面的评论。所有 MIN/MAX 实现几乎都具有相同的性能(很可能只是卡在内存中)

【讨论】:

是的,使用 min/max 提供了一个更简单的解决方案。我认为这个解决方案可以适应 SSE4.1。只需使用_mm_blend_epi16_mm_blend_ps 而不是_mm_blend_epi32(可能会造成一些延迟/吞吐量损失)。 @Elalfer 谢谢。你能解释一下循环延迟是什么意思吗?什么是HSW+ @nikitablack 指令延迟 - 执行一条指令需要多少个周期,HSW+ - 是 Haswell 微架构及更高版本(Broadwell,Skylake) .您可以找到有关指令延迟和吞吐量的更多信息@software.intel.com/sites/landingpage/IntrinsicsGuide【参考方案2】:

您似乎想在单个 XMM 寄存器中对成对的 32 位整数进行排序。当然没有现成的指令,但您可以使用 SSE4.1 的一些指令来完成(注意:代码未测试):

//input = [100, 5, 1, 42]
__m128i swapped = _mm_shuffle_epi32(input, _MM_SHUFFLE(2,3,0,1)); // [5, 100, 42, 1]
__m128i comp = _mm_cmplt_epi32(input, swapped);                   // [0, -1, -1, 0]
comp = _mm_xor_si128(comp, _mm_set_epi32(-1, 0, -1, 0));          // [0, 0, -1, -1]
input = _mm_blendv_epi8(swapped, input, comp);                    // [5, 100, 1, 42]

在 Ivy Bridge 上似乎是 7 微指令,需要 2 个 CPU 周期(吞吐量)。

如果需要,它可以轻松移植到 AVX2。

【讨论】:

@nikitablack:转到英特尔内部指南并一一检查每个命令。这并不难理解。 BTW xor 指令可以用另一个 shuffle 替换。 谢谢,已经这样做了。你能提示一下如何用洗牌替换 xor - 我自己想不出来。我怎么知道要洗牌的元素? @nikitablack:比较结果会产生四个 32 位掩码。但是,第一个和第二个结果必须是互补的:(a (除非 a == b,在这种情况下任何掩码西装)。第三和第四个结果也是如此。现在我反转第二个和第四个蒙版,但您可以将第一个蒙版复制到第二个蒙版,将第三个蒙版复制到第四个蒙版。只需一次调用 _mm_shuffle_epi32 即可完成此元素的注册内复制。

以上是关于与 SIMD 内在函数进行比较和交换的主要内容,如果未能解决你的问题,请参考以下文章

为啥 gcc -O3 处理 avx256 的内在比较与 gcc -O0 和 clang 不同?

如何使用 SIMD 比较两个字符向量并将结果存储为浮点数?

与霓虹内在函数中的浮点数比较

基于 SIMD 的算法实现的复杂性比较

SIMD/SSE:短点积和短最大值

如何在有或没有 SIMD 内在函数的情况下从 Zig 构建和链接到 CGLM