我们假设数据为1011 1001
且掩码为0111 0110
,那么您有:
input data: 1011 1001
input mask: 0111 0110
apply mask: 0011 0000 (based on `input mask`)
bits selected: -011 -00- (based on `input mask`)
right packed: ---0 1100
expected result: 0000 1100 (set left `8 - popcount('input mask')` bits to zero)
所以最终输出是0000 1100
(请注意,左边3个未指定的位置是零填充的。)
您可以看到input mask
中的位数为1时,input data
中的相应值已选择(位于bits selected
以上),然后是所有选择的位在结果的最低有效位中连续打包(如上面的right packed
所示)。最后,打包后遗留的最左边的位被设置为0(这些位将有8 - popcount(mask)
个。)
明显的选择是旋转和选择,但这将消耗5个操作,因为掩码有5位。我可以一步到位吗?
注意:
掩码可以是具有任意n
位ON
的任何内容(在上面
例如n=5
)。您所知道的是ON
中的位数
面具和面具本身。掩码将随着n
位ON
继续变化。
在上面的示例中,我使用了8位的数据和掩码,但实际上是这样 用法可以是8,16,32,64和128位。
答案 0 :(得分:7)
如果你的目标是x86
,大多数编译器都会有pdep
(parallel bit deposit)指令的内在函数,它直接执行你想要的操作,在硬件中,速率为1每个周期(3个周期延迟) 1 ,在支持它的英特尔硬件上。例如,gcc offers it作为_pdep_u32
和_pdep_u64
内在函数。
不幸的是,在AMD Ryzen(唯一支持BMI2的AMD硬件)上,此操作非常缓慢:每18个周期一个。您可能希望有一个单独的代码路径来支持非英特尔平台,如果它们对您很重要的话。
如果您不在x86
,您可以找到这些选项的通用实现here - 您想要的具体操作是expand_right
- 而这另一部分可能是非常感兴趣的是它特别涵盖了你处理单词大小元素的简单情况。
实际上,如果你真的在处理8位数据和掩码值,你可能只使用预先计算的查找表 - 大8位x 8位= 65k,覆盖所有{data, mask}
组合和它可以直接给你答案,或者是一个涵盖所有mask
值的256项,并为你提供一些简单的位移计算或基于乘法的代码的系数。
FWIW,我不知道如何使用5个旋转指令轻松完成,因为看起来天真的解决方案需要每个位1个旋转指令,无论是否设置(所以对于8位的字大小, 7或8旋转 2 指令。
1 当然,性能原则上取决于硬件,但是在实现它的所有主流Intel CPU上,它是1个周期的吞吐量,3个周期的延迟(不确定AMD)。 / p>
2 只有7个旋转,因为最低位的“旋转0”操作显然可以省略。