是否有用于元素部分移位的 simd 指令/内在/内置?

Posted

技术标签:

【中文标题】是否有用于元素部分移位的 simd 指令/内在/内置?【英文标题】:Is there a simd instruction/intrinsic/builtin for partial shift of elements? 【发布时间】:2020-01-08 07:28:34 【问题描述】:

一个最小的例子会更有益:

假设我有一个排序的 8 个整数 = 10, 20, 30, 40, 50, 60, 70, 80(我的用例是排序整数,但考虑到向量指令作用于整个数据集,我不确定这些信息是否有价值)

需要的操作很少:

    插入和移位。

-> 在它的排序位置插入 25。 -> 在索引 2 处插入 25 并换档。

10, 20, 30, 40, 50, 60, 70, 80 变为:10, 20, 25, 30, 40, 50, 60, 70

    在背面拆卸和移动并插入。

-> 如果找到并删除了 20,则从数组中删除 20 并在后面插入 90。 10, 20, 30, 40, 50, 60, 70, 80 变为 10, 30, 40, 50, 60, 70, 80, 90

或者一组指令可以让它工作?

我正在尝试对降序排序数组进行多步插入和移位部分。 https://godbolt.org/z/_WCxkW

【问题讨论】:

有 AVX2 vpermd 用于 256 位向量,SSE2 pshufd 用于 128 位向量(双字元素),可以移动其他元素以设置 vpblendd 插入一。但是,没有什么特别有效的方法,也没有将插入位置转换为随机播放控制向量的好方法。如果您尝试构建 SIMD 排序,则 SIMD 向量中的插入排序并不好。 int 你的意思是int32?您是否将其中的 8 个存储在两个 SSE 寄存器中?或者你使用int16?另外:签名还是未签名?或者你有可用的 AVX2 吗?您如何确定要删除的对象(按值或按索引)?如果是由值决定,如果元素不存在怎么办?如果你在最后插入一个零,在删除之后,你的数组将不再被排序。 Int32。目前我不直接处理 SSE 寄存器,主要是通过 OpenMP 工作。 > Also: Signed or unsigned? 无论工作如何,值本质上都是无符号的。 > Or do you have AVX2 available? 我愿意。> How do you determine the to-be-removed object (by value or by index)? 按排序数组的值搜索。正如我所说,需要从示例中的数组中删除20> If it is determined by value, what shall happen if the element does not exist? 什么都没有。数组保持原样。 你的数组是否真的存储在内存中? (这将简化移位,只需从偏移地址加载)。如果一个待删除号码存在多次怎么办?此外,“什么都不做”,如果元素不存在会使这稍微复杂一些——我会有一个相对直接的解决方案来“删除不小于x的第一个元素”(或最后一个不大于 -如果x 只存在一次,则等效) SIMD 向量不能有效地支持您要求的各种操作;您真正想解决什么问题?您可能应该以不同的方式解决高级问题。 【参考方案1】:

做你想做的事情的一种通用方法是([u]int_8,16,32,64 甚至float/double 的总体思路是相同的):

x 插入input

// Shift your input array (e.g. "abcefghi") to the right:
out = ShiftRight(input); // out = 0abcefgh
// broadcast the to-be-inserted element (e.g., 'd')
insert = broadcast(x); // insert = dddddddd
// compute 
out = min(max(out,insert),input)
//  == min(max(0abcefgh,dddddddd),abcefghi)
//  == min(ddddefgh,abcefghi) == abcdefgh

input中删除不小于x的第一个元素:

// shift input (e.g., "abcdefgh") to the left (insert something at the end)
out = ShiftLeft(input); // out = bcdefghX
// determine elements smaller than `x` (e.g., "f") by broadcast and compare
mask = broadcast(x) < input; // mask = 11111000
// take masked elements from `input` and other values from `out` (using a blend instruction)
out = blend(mask, input, out); // == abcdeghX

如果要删除的元素数量不能保证为 1(即,它可能不存在或多次存在),这会更加困难,因为每个输出值都可能取决于每个输入值。一种想法可能是比较相等性并计算元素的数量(使用maskmovepopcount)。


你可以使用换档

SSE2 且只有一个 128 位寄存器:pslldqpsrldq SSSE3 和一系列 128 位寄存器:palignr AVX2 和一个 256 位寄存器:vpermd 带有一个预先确定的索引向量(没有 AVX2 等效于前面的指令,它适用于整个 256 位寄存器) 如果您的输入存储在内存中,请使用一个元素偏移量再次加载它(这需要一个“安全”元素超出数组的每一端 - 如果您多次执行这些操作,它可能会引入显着的写入读取延迟次)

对于广播,我建议只使用 _mm[256]_set1_epi32 内在函数,让编译器找出最有效的方法(如果没有 AVX2,这可能需要随机播放)

存在各种尺寸/类型的最小/最大运算符(取决于 SSE/AVX 版本)——只需搜索以 pmin/pmax 开头的指令即可。

据我所知,在 AVX512 之前没有无符号比较,但当然你可以使用有符号比较,如果没有值大于最大的有符号值。或者您可以通过在比较之前翻转高位来解决问题(我假设在 *** 上有一个相关的问题)。

最后,如果您有 SSE4.1,则由 pblendvb 完成混合。否则你需要做一些按位与/与非/或操作。

【讨论】:

以上是关于是否有用于元素部分移位的 simd 指令/内在/内置?的主要内容,如果未能解决你的问题,请参考以下文章

我可以将SIMD内在函数用于在云上运行的软件吗?

类似于 SIMD 指令的宏

有没有一种有效的方法来使用 SIMD 内在函数来获取 SIMD 寄存器中的第一个非零元素?

我可以将 SIMD 内在函数用于在云上运行的软件吗?

使用 ARM SIMD 指令优化掩码功能

SIMD 零向量测试