合并两个位掩码并解决冲突,任何两个设置位之间有一些所需的距离
Posted
技术标签:
【中文标题】合并两个位掩码并解决冲突,任何两个设置位之间有一些所需的距离【英文标题】:Merge two bitmask with conflict resolving, with some required distance between any two set bits 【发布时间】:2022-01-13 08:28:11 【问题描述】:我有两个整数值:
d_a = 6
和d_b = 3
,所谓设置位之间的距离。
使用适当距离创建的蒙版如下所示:
uint64_t a = 0x1041041041041041; // 0001 0000 0100 0001 0000 0100 0001 0000
// 0100 0001 0000 0100 0001 0000 0100 0001
uint64_t b = 0x9249249249249249; // 1001 0010 0100 1001 0010 0100 1001 0010
// 0100 1001 0010 0100 1001 0010 0100 1001
目标是有一个target
掩码,它的位设置为d_b
,但同时考虑到a
掩码中设置的位(例如,第一个设置的位被移位)。
第二件事是target
掩码中的距离不是恒定的,即target
掩码中设置位之间的零数量应等于d_b
,或者只要它们之间设置@987654331 中的位就增加@
uint64_t target = 0x4488912224488912; // 0100 0100 1000 1000 1001 0001 0010 0010
// 0010 0100 0100 1000 1000 1001 0001 0010
可视化问题的图片:
蓝色条是a
,黄色条是b
。
我宁愿使用位操作内在函数而不是逐位操作。
编辑: 实际上,我有以下代码,但我正在寻找指令数量较少的解决方案。
void set_target_mask(int32_t d_a, int32_t d_b, int32_t n_bits_to_set, uint8_t* target)
constexpr int32_t n_bit_byte = std::numeric_limits<uint8_t>::digits;
int32_t null_cnt = -1;
int32_t n_set_bit = 0;
int32_t pos = 0;
while(n_set_bit != n_bits_to_set)
int32_t byte_idx = pos / n_bit_byte;
int32_t bit_idx = pos % n_bit_byte;
if(pos % d_a == 0)
pos++;
continue;
null_cnt++;
if(null_cnt % d_b == 0)
target[byte_idx] |= 1 << bit_idx;
n_set_bit++;
pos++;
【问题讨论】:
您能解释一下如何将target
与a
和b
结合起来吗?我不确定我是否能听从你的解释。
我的目标是把它们结合起来。假设a
表示已经预留资源:``` uint64 available = 0xFFFFFFFFFFFFFFFF ^ a ```现在我需要使用available
根据d_b
创建target
但是组合规则是什么?为什么你不能,比如说,OR
在他们之间?
标量 OR
或简单的左移或右移没有内在函数,因为它们不是必需的:|
和 <<
on uint64_t
已经表达了与整数相同的行为或者会。除非您的意思是要一次对多个 uint64_t 元素执行此操作,使用 _mm_or_si128
或其他什么?仍然不清楚如何解决冲突,尽管您可以使用(x & (x<<1)) == 0
检测它们,等等,直到d_b
的班次计数。或者像x - (x>>d_b)
或(x<<d_b) - x
这样的东西可以在每个地区获得口罩? (未经测试,不确定是否完全有效)
我认为pdep
方法也适合数组处理,因为设置为uint64_t
的总位可以由popcnt
确定,并且可以通过左移调整掩码,
【参考方案1】:
如果目标是uint64_t
,则可以通过查找表将d_a
和d_b
转换为位掩码。喜欢你的问题中的lut[6] == 0x2604D5C99A01041
。
可以在初始化期间或在编译时使用宏或常量表达式 (constexpr
) 对每个程序运行初始化一次查找表。
要使d_b
展开,跳过d_a
位,您可以将pdep
与倒置的d_a
一起使用:
uint64_t tmp = _pdep_u64(d_b_bits, ~d_a_bits);
然后您可以将n_bits_to_set
转换为连续位掩码:
uint64_t n_bits = (1 << n_bits_to_set) - 1;
并再次使用pdep
传播它们:
uint64_t tmp = _pdep_u64(n_bits, tmp);
(请参阅Intrinsic Guide 关于 pdep。请注意,pdep 在 Zen3 之前的 AMD 上速度较慢。在 Intel CPU 和 Zen3 上速度较快,但在 Bulldozer-family 或 Zen1/Zen2 上速度较慢)
【讨论】:
如果您知道不会发生冲突,您可以使用_bzhi_u64(tmp, n_bits_to_set * d_b)
或类似设置的位数限制将该位位置归零或更高。 (即计算位散射将在哪里结束)。但是由于两个 LUT 结果之间的交互,一些位未设置,是的,另一个 pdep 可能是有意义的。
它接近预期的结果,但我需要传播d_b
,并将移位应用于冲突的d_a
位,而不是清除它们。我尝试使用 BMI2 指令但没有结果
@temteremte,第一个 pdep
的地址不正是这个:tmp = _pdep_u64(d_b_bits, ~d_a_bits);
吗? tmp
应该包含传播 d_b
,不是吗?
是的,我错误地切换了参数的顺序。这正是我所需要的。谢谢!以上是关于合并两个位掩码并解决冲突,任何两个设置位之间有一些所需的距离的主要内容,如果未能解决你的问题,请参考以下文章