gpt4 book ai didi

c++ - 使用 SSE 的任意位置 2 输入混洗

转载 作者:行者123 更新时间:2023-11-30 01:02:01 26 4
gpt4 key购买 nike

我有两个 4 分量 vector ,我将它们加载到两个 __m128 变量中。然后我需要将它们打乱顺序,以便结果如下所示:

给定:

__m128 mmMin = _mm_load_ps(&glm::vec4(-1.0f,-2.0f,-3.0f,-4.0f)[0]);
__m128 mmMax = _mm_load_ps(&glm::vec4(1.0f,2.0f,3.0f,4.0f)[0]);

我希望洗牌的结果如下所示:

 //    {mmMin.x,mmMax.x,mmMin.x,mmMax.x}

但我发现用 _mm_shuffle_ps 是不可能的。

来自 SSE docs我总是看到 _mm_shuffle_ps 面具首先从 __m128 的低 2 个组件插入结果 2 个值,然后从高 2 个组件插入 2 个值。

SPU 内在函数有 si_shufb 方法,它允许定义基于 qword 的掩码并随机播放我想要的任何位置。 SSE有类似的方法吗?

我正在使用 SSE2,但也很高兴看到它如何与其他版本(包括 AVX)一起完成。

最佳答案

只有 SSE2 我认为你需要 2 次随机播放:unpcklps 交错然后 unpcklpd same,sameshufps same,same 广播低 64 位。

使用 AVX512F,vpermt2ps可以在一次洗牌中做到这一点(使用控制 vector );我认为 AVX2 或更早版本中没有任何 2 源混洗具有足够精细的粒度和灵活的源位置。并且没有重复元素和交错的固定随机播放。

在 AVX512 之前,2 源随机播放很少见:主要是固定的随机播放,如 unpckl/h*palignr。在那之前,它主要只是 [v]shufps/[v]shufpd。可变控制洗牌也很少见:在 AVX 之前,唯一的是 pshufb。 AVX1/2 添加了一些可变控制双字元素随机播放,但仅适用于 1 个源。在 AVX512 之前,没有可变控制 2 源随机播放。

立即洗牌需要超过 4 组 2 位索引来处理对两个 4 元素 vector 的串联的任意索引。但是 x86 SIMD 指令始终最多有一个 8 位立即数操作数。不幸的是,没有像 ARM 这样的广播立即数可以有效地创建 1.0f 或其他 vector 。


AVX

因为您只需要每个 vector 中的 1 个元素,而不是加载整个 vector ,您可以使用 AVX 广播加载,然后使用 vblendps

广播负载与 Intel CPU 上的正常负载的成本相同(不要为洗牌端口花费 uop,纯粹在加载端口处理)。在 AVX512F 之前,它们无法折叠到 ALU 指令的内存操作数中,但它们确实避免了洗牌端口瓶颈。 AMD CPU 可能仍然需要一个 ALU uop,但它们有更多的 shuffle ALU,因此 shuffle 吞吐量几乎不是瓶颈。 ( https://agner.org/optimize/ )

Ryzen vbroadcastss xmm, [mem] 不幸的是前端有 2 个独立的 uops,但它仍然有 2 个每时钟的吞吐量。

在 dword 和更高版本的元素上 blend-immediate 非常高效,可以在 Haswell 和更高版本的任何端口上运行,或者在 SnB/IvB 和 Ryzen 上运行 2 个端口。但即使在 Nehalem 上仍然是单 uop/1c 延迟。

#include <immintrin.h>
__m128 broadcast_interleave_scalars_avx(const float *min, const float *max) {
__m128 minx = _mm_broadcast_ss(min);
__m128 maxx = _mm_broadcast_ss(max);
return _mm_blend_ps(minx, maxx, 0b1010);
}

On Godbolt , clang 的 asm 评论确认我得到了正确的混合常量:

    vbroadcastss    xmm0, dword ptr [rdi]
vbroadcastss xmm1, dword ptr [rsi]
vblendps xmm0, xmm0, xmm1, 10 # xmm0 = xmm0[0],xmm1[1],xmm0[2],xmm1[3]

如果您的数据已经在寄存器中,而不是新加载的,您可能只想使用 2 次随机播放。


使用 SSE4.1 您可能能够执行 2x movddup加载以从内存广播 64 位(包括您关心的 32 位),然后 blendps。第一次加载将加载您关心的 float 之后的 32 位,第二次加载将加载您关心的float 之前的 32 位

要让 C++ 编译器为您发出此消息,您必须将指针转换为 double* 以获得 __m128d _mm_loaddup_pd (double const* mem_addr)加载,然后使用 _mm_castpd_ps__m128d 获取 __m128

https://www.felixcloutier.com/x86/movsldup也可用于设置 unpcklps

关于c++ - 使用 SSE 的任意位置 2 输入混洗,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57055756/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com