是否有用于元素部分移位的 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
【问题讨论】:
有 AVX2vpermd
用于 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(即,它可能不存在或多次存在),这会更加困难,因为每个输出值都可能取决于每个输入值。一种想法可能是比较相等性并计算元素的数量(使用maskmove
和popcount
)。
你可以使用换档
SSE2 且只有一个 128 位寄存器:pslldq
、psrldq
SSSE3 和一系列 128 位寄存器:palignr
AVX2 和一个 256 位寄存器:vpermd
带有一个预先确定的索引向量(没有 AVX2 等效于前面的指令,它适用于整个 256 位寄存器)
如果您的输入存储在内存中,请使用一个元素偏移量再次加载它(这需要一个“安全”元素超出数组的每一端 - 如果您多次执行这些操作,它可能会引入显着的写入读取延迟次)
对于广播,我建议只使用 _mm[256]_set1_epi32
内在函数,让编译器找出最有效的方法(如果没有 AVX2,这可能需要随机播放)
存在各种尺寸/类型的最小/最大运算符(取决于 SSE/AVX 版本)——只需搜索以 pmin
/pmax
开头的指令即可。
据我所知,在 AVX512 之前没有无符号比较,但当然你可以使用有符号比较,如果没有值大于最大的有符号值。或者您可以通过在比较之前翻转高位来解决问题(我假设在 *** 上有一个相关的问题)。
最后,如果您有 SSE4.1,则由 pblendvb
完成混合。否则你需要做一些按位与/与非/或操作。
【讨论】:
以上是关于是否有用于元素部分移位的 simd 指令/内在/内置?的主要内容,如果未能解决你的问题,请参考以下文章