英特尔位操作指令集2(BMI2)中的并行存款指令(PDEP
)的文档描述了该指令的以下串行实现(类C伪代码):
U64 _pdep_u64(U64 val, U64 mask) {
U64 res = 0;
for (U64 bb = 1; mask; bb += bb) {
if (val & bb)
res |= mask & -mask;
mask &= mask - 1;
}
return res;
}
字符串
See also Intel's pdep
insn ref manual entry的数据。
这个算法是O(n)的,其中n是mask
中的设置位数,显然最坏情况是O(k),其中k是mask
中的总位数。
更有效的最坏情况算法是否可能?
有没有可能做一个更快的版本,假设val
最多有一个位设置,即等于0或等于1<<r
的一些值r
从0到63?
2条答案
按热度按时间xpszyzbs1#
问题的第二部分,关于1位存款的特殊情况,需要两个步骤。在第一步中,我们需要确定
val
中单个1位的位索引r
,在val
为零的情况下具有合适的响应。这可以很容易地通过POSIX函数ffs
来实现,或者如果r
是已知的,就像提问者在评论中提到的那样。在第二步中,我们需要识别mask
中第r
个1位的位索引i
(如果存在)。然后,我们可以将val
的第r
位存放在位i
处。找到
mask
中第r
个1位的索引的一种方法是使用基于二进制分区的经典population count算法来计算1位,并记录所有中间组的位计数。然后,我们对记录的位计数数据执行二进制搜索,以识别所需位的位置。下面的
C
代码使用64位数据演示了这一点。这实际上是否比迭代方法更快将在很大程度上取决于mask
和val
的典型值。字符串
mnemlml82#
作为一个额外的注意,因为这再次出现在我身上,我发现Sebastiano Vigna's method用于查找第n个设置位在实践中更快。它不包含分支或条件移动。
请注意,
leq_bytes
和gt_zero_bytes
可能可以使用SSE指令实现,但这个版本的优点是完全可移植。字符串