将所有位低于最重要的设置位零的最有效方法是什么?
What is the most efficient way to zero all bits below the most significant set bit?
因此,对于以下序列:0001000111000
所需的结果将是:0001000000000
我完全意识到,可以通过汇编bsrl(或类似的咬合hack)找到MSB的索引,然后>>>>>>将数字移动(index -1),然后<<<通过(索引-1)转移但是我想知道,特别是有一个汇编指令还是一系列具有更好性能的说明,而不是可以做到这一点的有点扭曲。
没有一个指令可以做到这一点。BMI1 blsi dst,src
可以隔离最低设置位,而不是最高。即x & -x
。如果X86具有blsi
的位置版本,我们可以使用它,但不是。
,但是您可以做得比建议的要好得多。全零输入始终将是位扫描和移位的特殊情况。否则,我们的输出完全设置为1位。是1 << bsr(input)
。
;; input: x in RDI
;; output: result in RAX
isolate_msb:
xor eax, eax ; tmp = 0
bsr rdi, rdi ; edi = bit index of MSB in input
jz .input_was_zero
bts rax, rdi ; rax |= 1<<edi
.input_was_zero: ; return 0 for input=0
ret
显然,对于32位输入,仅使用32位寄存器。如果不可能零,请省略JZ。使用BSR代替LZCNT会给我们一个比特索引,而不是31-Bitidx,因此我们可以直接使用它。但是LZCNT在AMD上的速度明显更快。
XOR-Zeroing不在临界路径上,以准备BTS的输入。XOR-Zero BTS是在Intel CPU上实现1<<n
的最有效方法。在AMD上是2C延迟的2个UOP,因此mov rax,1
/shl rax,cl
在那里更好。但是在英特尔上更糟,因为可变计数偏移为3个UOPS,除非您使用BMI2 shlx
。
无论如何,这里的真正工作是BSR BTS,因此这是Intel SNB家族的3个周期 1周期延迟。(https://agner.org/optimize/)
在C/C 中,您将其写为
unsigned isolate_msb32(unsigned x) {
unsigned bitidx = BSR32(x);
//return 1ULL << bitidx; // if x is definitely non-zero
return x ? 1U << bitidx : x;
}
unsigned isolate_msb64(uint64_t x) {
unsigned bitidx = BSR64(x);
return x ? 1ULL << bitidx : x;
}
根据编译器支持的固有的固有,BSR32
是定义的。这是事情变得棘手的地方,尤其是如果您想要64位版本。没有单一的便携式固有。GNU C提供了计数领域的内在,但是GCC和ICC吸引了将63-__builtin_clzll(x)
优化回到BSR中。相反,他们否定了两次。是的BSR内置,但是这些是编译器特定的,而不是支持GNU扩展的MSVC与编译器(GCC/Clang/ICC)。Godbolt编译器探险器上,即使他们不知道x
不是零。
所有4个编译器都无法使用bts
实现1<<bit
。:(在英特尔上很便宜。
# clang7.0 -O3 -march=ivybridge (for x86-64 System V)
# with -march=haswell and later it uses lzcnt and has to negate. /sigh.
isolate_msb32(unsigned int):
bsr ecx, edi
mov eax, 1
shl rax, cl
test edi, edi
cmove eax, edi # return 1<<bsr(x) or x (0) if x was zero
ret
GCC和MSVC制作分支代码。例如
# gcc8.2 -O3 -march=haswell
mov eax, edi
test edi, edi
je .L6
bsr eax, edi
mov edi, 1
shlx rax, rdi, rax # BMI2: 1 uop instead of 3 for shl rax,cl
.L6:
ret
您的要求没有单个指令,否。
但是,如果您想避免扭动变量的位,则有一种替代方法:
声明与原始变量相同类型的第二个变量,然后将第二个变量设置为0。然后将原始变量的位循环从最高位到最低位,通过&
操作员测试每个位。如果发现一些位设置为1,请在第二个变量中设置相应位,然后退出循环。如果需要,将第二个变量分配给原始变量。
- 在C++中初始化向量映射的最有效方法
- 将此布尔值传递给此函数的最有效方法是什么?
- 比较C++变量的最有效方法
- 在 c++ 中解决段树以外的范围查询的有效方法是什么?
- 存储变量的更有效方法是什么?
- 确保套装新鲜度的有效方法
- 当映射包含字符串向量作为值时,从值中获取键的有效方法
- 映射唯一值和重复值的有效方法.可以访问键或值的位置
- 在C++事务之间存储大量字符数据的有效方法
- 在unordered_multimap中精确迭代一次每个键的有效方法
- 一种将 Dart 中的字节数据转换为 C++ 中的无符号字符*的有效方法?
- 检查两个向量是否并行的最有效方法
- 从浮点数中删除小数部分但保留类型的有效方法
- 传递非泛型函数的最有效方法是什么?
- 按升序打印矢量的所有元素直到它为空而没有重复项的最有效方法是什么?
- 创建字符串数组的有效方法
- 返回一个引用C++中另一个类对象的对象的有效方法
- C++去除前x个元素的有效方法,在不改变向量大小的情况下将第x+1个元素推到第一个
- 将一种数据类型的向量复制到同一数据类型的结构向量中的有效方法是什么
- 从std::map值中获取密钥的有效方法