在this hardcore article中,有一个函数find_maskwidth()
,基本上可以检测表示itemCount
dictinct值所需的位数:
unsigned int find_maskwidth( unsigned int itemCount )
{
unsigned int maskWidth, count = itemCount;
__asm {
mov eax, count
mov ecx, 0
mov maskWidth, ecx
dec eax
bsr cx, ax
jz next
inc cx
mov maskWidth, ecx
next:
}
return maskWidth;
}
问题是为什么他们使用ax
和cx
个注册代替eax
和ecx
?
答案 0 :(得分:2)
我只能猜测他们希望只处理不超过16位的字段。由于它用于确定可用于报告包中核心数或逻辑处理器数等的位数,因此在溢出超过65535之前可能需要一段时间。
我希望有人不会决定将这个例程用于更一般的目的。
只是一个FYI - 如果你想做这样的事情而不是放弃到x86程序集(虽然我猜这篇文章的目的,非便携性几乎是给定的),Bit Twiddling Hacks page你已经覆盖了
答案 1 :(得分:1)
我猜原始数据只有16位宽。由于cx获得了一个位数,所以无论如何它都不可能大于一个非常小的数字。
值得注意的是,在一个级别上,除了前缀字节之外,16位和32位ia32指令的操作码是相同的,因此发出全32位或全16位指令更有效,具体取决于你在哪种模式。我想这就是你问这个问题的原因......
答案 2 :(得分:1)
鉴于这段代码真的写错了(例如,绝对不需要maskWidth和count变量 - 除了让代码混乱之外)我想你可以放心,它只是另一个“这个代码中的坏事。
答案 3 :(得分:1)
例程基本确定itemCount的二进制(基数2)对数。
如果itemCount>它将提供完全错误的值。 2 ^ 16。它没有饱和或什么,这是完全错误的。输入参数是“unsigned int”,这使得它更加错误。因此它将停止在更多65536核心工作。
我的猜测是,英特尔的某个人挖掘了一些非常古老的代码,可以追溯到16位,没有真正了解它,并且使用它,因为65536将永远足够,完全就像640k内存将永远是足够的或者两位数的年份数字就足够了,直到时间结束。
答案 4 :(得分:1)
我想这是因为这段代码的作者可能并不真正知道他在做什么:-)。这些指令的16位版本更长,而且速度更快。实际上,它们可能会在下一条使用ECX的指令(即MOV)上导致部分寄存器停顿。
另请注意,跳转可以安全地移动一条指令(在DEC之后),因为DEC在输出为零时已经设置了ZF。这可以简化代码。
所以这就是我写这段代码的方式:
mov eax, [count]
xor ecx, ecx
dec eax
jz next
bsr ecx, eax
inc ecx
next:
mov [maskWidth], ecx
此外,在这里放弃组装的动机似乎是使用BSR指令,它在C语言或库中没有任何等价物。为此,您可以通过使用特定于编译器的内部函数来避免使用程序集。虽然这些本质上是不可移植的,但内联汇编也不是。
在GCC中,等效函数如下所示:
unsigned int find_maskwidth(unsigned int itemCount)
{
if(itemCount <= 1)
return 0;
else
return 32 - __builtin_clz(itemCount - 1);
}
更具可读性,不是吗?