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

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

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

需要的操作很少:

  1. 插入和移位。

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

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

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

-> 如果找到并删除了 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 向量不能有效地支持您要求的各种操作;您真正想解决什么问题?您可能应该以不同的方式解决高级问题。

标签: c++ gcc x86 sse 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 之前没有无符号比较,但当然你可以使用有符号比较,如果没有值大于最大的有符号值。或者您可以通过在比较之前翻转高位来解决问题(我假设在 stackoverflow 上有一个相关的问题)。

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

【讨论】:

    猜你喜欢
    • 2020-11-22
    • 2021-03-17
    • 1970-01-01
    • 2015-06-05
    • 1970-01-01
    • 2017-08-08
    • 2011-03-31
    • 2012-11-05
    • 2015-11-12
    相关资源
    最近更新 更多