寻求模拟x86 PDEP/PEXT指令的快速软件回退算法
PDEP/PEXT的O(log n)回退实现优化
问题背景
需要为x86指令集的PDEP(并行位存入)和PEXT(并行位提取)创建封装,在不支持这些指令及对应内在函数的架构上,需要高效的回退实现。现有32位整数的朴素算法时间复杂度为O(n),希望找到O(log n)的优化方案。
现有朴素实现
Bit Deposit(对应PDEP)
constexpr std::uint32_t bit_deposit(std::uint32_t src, std::uint32_t mask) { std::uint32_t result = 0; for (std::uint32_t src_pos = 0, mask_pos = 0; mask_pos != 32; ++mask_pos) { if (mask >> mask_pos & 1) { result |= (src >> src_pos++ & 1) << mask_pos; } } return result; } static_assert(bit_deposit(0b000, 0b000000) == 0b000000); static_assert(bit_deposit(0b101, 0b101010) == 0b100010); static_assert(bit_deposit(0b111, 0b101010) == 0b101010);
Bit Extract(对应PEXT)
constexpr std::uint32_t bit_extract(std::uint32_t src, std::uint32_t mask) { std::uint32_t result = 0; for (std::uint32_t src_pos = 0, mask_pos = 0; mask_pos != 32; ++mask_pos) { if (mask >> mask_pos & 1) { result |= (src >> mask_pos & 1) << src_pos++; } } return result; } static_assert(bit_extract(0b000000, 0b000000) == 0b000); static_assert(bit_extract(0b100010, 0b101010) == 0b101); static_assert(bit_extract(0b101010, 0b101010) == 0b111);
O(log n)优化实现
Bit Deposit(PDEP)优化版
constexpr std::uint32_t bit_deposit_fast(std::uint32_t src, std::uint32_t mask) { std::uint32_t result = src; std::uint32_t temp; // 分治处理,每次合并相邻位段 temp = mask ^ (mask << 1); temp &= ~(mask << 1); result = (result & temp) | ((result & ~temp) << 1); temp = mask ^ (mask << 2); temp &= ~(mask << 2); result = (result & temp) | ((result & ~temp) << 2); temp = mask ^ (mask << 4); temp &= ~(mask << 4); result = (result & temp) | ((result & ~temp) << 4); temp = mask ^ (mask << 8); temp &= ~(mask << 8); result = (result & temp) | ((result & ~temp) << 8); temp = mask ^ (mask << 16); temp &= ~(mask << 16); result = (result & temp) | ((result & ~temp) << 16); return result & mask; } // 验证与朴素实现结果一致 static_assert(bit_deposit_fast(0b000, 0b000000) == bit_deposit(0b000, 0b000000)); static_assert(bit_deposit_fast(0b101, 0b101010) == bit_deposit(0b101, 0b101010)); static_assert(bit_deposit_fast(0b111, 0b101010) == bit_deposit(0b111, 0b101010));
Bit Extract(PEXT)优化版
constexpr std::uint32_t bit_extract_fast(std::uint32_t src, std::uint32_t mask) { std::uint32_t result = src & mask; std::uint32_t temp; // 分治处理,每次压缩相邻位段 temp = mask ^ (mask >> 1); temp &= mask >> 1; result = (result & temp) | ((result & ~temp) >> 1); temp = mask ^ (mask >> 2); temp &= mask >> 2; result = (result & temp) | ((result & ~temp) >> 2); temp = mask ^ (mask >> 4); temp &= mask >> 4; result = (result & temp) | ((result & ~temp) >> 4); temp = mask ^ (mask >> 8); temp &= mask >> 8; result = (result & temp) | ((result & ~temp) >> 8); temp = mask ^ (mask >> 16); temp &= mask >> 16; result = (result & temp) | ((result & ~temp) >> 16); return result; } // 验证与朴素实现结果一致 static_assert(bit_extract_fast(0b000000, 0b000000) == bit_extract(0b000000, 0b000000)); static_assert(bit_extract_fast(0b100010, 0b101010) == bit_extract(0b100010, 0b101010)); static_assert(bit_extract_fast(0b101010, 0b101010) == bit_extract(0b101010, 0b101010));
优化思路说明
这种实现采用分治法,通过5轮固定操作(对应32位的log₂(32)=5)完成位处理:
- 对于
bit_deposit_fast:每一轮将未被mask覆盖的位段向右移动,逐步把src的位填充到mask标记的位置上,最后通过& mask确保只保留mask指定的位。 - 对于
bit_extract_fast:每一轮将mask标记的位段向左压缩,把分散在mask位置上的位集中到结果的低位,最终得到连续的提取结果。
每一轮操作都是O(1)的位运算,整体时间复杂度为O(log n),相比朴素的O(n)循环,在32位场景下仅需5步,性能提升显著,且保持了constexpr特性,可在编译期计算。
内容的提问来源于stack exchange,提问作者Jan Schultke
相关产品推荐
相关产品推荐

