应用两个显式类型转换的原因是什么?
if (unlikely(val != (long)(char)val)) {
Run Code Online (Sandbox Code Playgroud)
代码取自lxml源代码包中的lxml.etree.c源文件.
pli*_*nth 10
这是一种廉价的方法来检查高位中是否有任何垃圾.上面的8位,24位或56位(取决于sizeof(val))的char铸造印章然后将其提升回来.如果char已签名,它也将签名扩展.
更好的测试可能是:
if (unlikely(val & ~0xff)) {
Run Code Online (Sandbox Code Playgroud)
要么
if (unlikely(val & ~0x7f)) {
Run Code Online (Sandbox Code Playgroud)
取决于此测试是否关心第7位.
只是为了笑容和完整性,我写了以下测试代码:
void RegularTest(long val)
{
if (val != ((int)(char)val)) {
printf("Regular = not equal.");
}
else {
printf("Regular = equal.");
}
}
void MaskTest(long val)
{
if (val & ~0xff) {
printf("Mask = not equal.");
}
else {
printf("Mask = equal.");
}
}
Run Code Online (Sandbox Code Playgroud)
以下是在Visual Studio 2010中调试中的演员代码:
movsx eax, BYTE PTR _val$[ebp]
cmp DWORD PTR _val$[ebp], eax
je SHORT $LN2@RegularTes
Run Code Online (Sandbox Code Playgroud)
这是掩码:
mov eax, DWORD PTR _val$[ebp]
and eax, -256 ; ffffff00H
je SHORT $LN2@MaskTest
Run Code Online (Sandbox Code Playgroud)
在发布中,我得到了这个演员代码:
movsx ecx, al
cmp eax, ecx
je SHORT $LN2@RegularTes
Run Code Online (Sandbox Code Playgroud)
在发布中,我得到掩码代码:
test DWORD PTR _val$[ebp], -256 ; ffffff00H
je SHORT $LN2@MaskTest
Run Code Online (Sandbox Code Playgroud)
发生什么了?在转换的情况下,它正在使用符号扩展执行一个字节mov(ha!bug - 代码不一样,因为字符被签名)然后比较并且完全偷偷摸摸,编译器/链接器也使这个函数使用寄存器传递为了论证.在发布中的掩码代码中,它将所有内容都折叠成一条测试指令.
哪个更快?打败我 - 坦率地说,除非你在非常慢的CPU上运行这种测试或运行数十亿次,否则无关紧要.至少没有.
因此,在这种情况下的答案是编写清楚其意图的代码.我希望C/C++骑师可以看一下掩码并了解它的意图,但是如果你不喜欢它,你应该选择这样的东西:
#define BitsAbove8AreSet(x) ((x) & ~0xff)
#define BitsAbove7AreSet(x) ((x) & ~0x7f)
Run Code Online (Sandbox Code Playgroud)
要么:
inline bool BitsAbove8AreSet(long t){return(t&~0xff)!= 0; } //使它成为一个好的内联bool BitsAbove7AreSet(long t){return(t&~0x7f)!= 0; }
并使用谓词而不是实际代码.
总的来说,我认为"它便宜吗?" 除非你在一些非常具体的问题领域工作,否则这个问题并不是一个特别好的问题.例如,我从事图像处理工作,当我从一个图像到另一个图像进行某种操作时,我经常会看到如下代码:
BYTE *srcPixel = PixelOffset(src, x, y, srcrowstride, srcdepth);
int srcAdvance = PixelAdvance(srcrowstride, right, srcdepth);
BYTE *dstPixel = PixelOffset(dst, x, y, dstrowstride, dstdepth);
int dstAdvance = PixelAdvance(dstrowstride, right, dstdepth);
for (y = top; y < bottom; y++) {
for (x=left; x < right; x++) {
ProcessOnePixel(srcPixel, srcdepth, dstPixel, dstdepth);
srcPixel += srcdepth;
dstPixel += dstdepth;
}
srcPixel += srcAdvance;
dstPixel += dstAdvance;
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,假设ProcessOnePixel()实际上是一块内联代码,将被执行数十亿次数十亿次.在这种情况下,我非常关心不做函数调用,不做冗余工作,不重新检查值,确保计算流程将转化为明智地使用寄存器的东西,等等.但我实际主要关心的是代码可以被下一个看着它的可怜的骗子(可能是我)读.
在我们当前的编码世界中,对于几乎每个问题领域来说,最好花费一点时间在前面确保您的代码易于阅读和维护,而不是担心性能超出门.