英特尔比特操作指令集2(BMI2)中并行存放指令(PDEP)的文档描述了该指令的以下串行实现(类似C的伪代码):
PDEP
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; }
另请参阅英特尔的pdepinsn ref手册条目。
pdep
此算法为O(n),其中n是in中的设置位数mask,这显然是O(k)的最坏情况,其中k是in中的总位数mask。
mask
有可能有效率更高的最坏情况算法吗?
是否可以做一个更快的版本,假设它val最多设置一位,即等于0还是等于0到63之间的1<<r某个值r?
val
1<<r
r
问题的第二部分,关于1位寄存的特殊情况,需要两个步骤。第一步,我们需要确定rin中单个1位的位索引val,并在情况val为零的情况下做出适当的响应。这可以很容易通过POSIX函数来完成ffs,或者,如果r由其他方式已知的,如通过在注释中的提问者提到。第二步,我们需要确定i中的r第-1个位的位索引(mask如果存在)。然后,我们可以存放at位的r第th val位i。
ffs
i
找到r第一个1位索引的一种方法mask是,使用基于二进制分区的经典填充计数算法对1位进行计数,并记录所有中间的逐组位计数。然后,我们对记录的位计数数据执行二进制搜索,以识别所需位的位置。
以下C-code使用64位数据对此进行了演示。无论这实际上比迭代法快将在很大程度上取决于典型值mask和val。
C
#include <stdint.h> /* Find the index of the n-th 1-bit in mask, n >= 0 The index of the least significant bit is 0 Return -1 if there is no such bit */ int find_nth_set_bit (uint64_t mask, int n) { int t, i = n, r = 0; const uint64_t m1 = 0x5555555555555555ULL; // even bits const uint64_t m2 = 0x3333333333333333ULL; // even 2-bit groups const uint64_t m4 = 0x0f0f0f0f0f0f0f0fULL; // even nibbles const uint64_t m8 = 0x00ff00ff00ff00ffULL; // even bytes uint64_t c1 = mask; uint64_t c2 = c1 - ((c1 >> 1) & m1); uint64_t c4 = ((c2 >> 2) & m2) + (c2 & m2); uint64_t c8 = ((c4 >> 4) + c4) & m4; uint64_t c16 = ((c8 >> 8) + c8) & m8; uint64_t c32 = (c16 >> 16) + c16; int c64 = (int)(((c32 >> 32) + c32) & 0x7f); t = (c32 ) & 0x3f; if (i >= t) { r += 32; i -= t; } t = (c16>> r) & 0x1f; if (i >= t) { r += 16; i -= t; } t = (c8 >> r) & 0x0f; if (i >= t) { r += 8; i -= t; } t = (c4 >> r) & 0x07; if (i >= t) { r += 4; i -= t; } t = (c2 >> r) & 0x03; if (i >= t) { r += 2; i -= t; } t = (c1 >> r) & 0x01; if (i >= t) { r += 1; } if (n >= c64) r = -1; return r; } /* val is either zero or has a single 1-bit. Return -1 if val is zero, otherwise the index of the 1-bit The index of the least significant bit is 0 */ int find_bit_index (uint64_t val) { return ffsll (val) - 1; } uint64_t deposit_single_bit (uint64_t val, uint64_t mask) { uint64_t res = (uint64_t)0; int r = find_bit_index (val); if (r >= 0) { int i = find_nth_set_bit (mask, r); if (i >= 0) res = (uint64_t)1 << i; } return res; }