在Linux内核代码中,有一个用于测试位的宏(Linux版本2.6.2):
#define test_bit(nr, addr)
(__builtin_constant_p((nr))
? constant_test_bit((nr), (addr))
: variable_test_bit((nr), (addr)))
其中constant_test_bit
和variable_test_bit
定义为:
static inline int constant_test_bit(int nr, const volatile unsigned long *addr )
{
return ((1UL << (nr & 31)) & (addr[nr >> 5])) != 0;
}
static __inline__ int variable_test_bit(int nr, const volatile unsigned long *addr)
{
int oldbit;
__asm__ __volatile__(
"btl %2,%1ntsbbl %0,%0"
:"=r" (oldbit)
:"m" (ADDR),"Ir" (nr));
return oldbit;
}
我知道__builtin_constant_p
用于检测变量是编译时间常数还是未知。我的问题是:当参数是编译时间常数时,这两个函数之间是否有任何性能差异?为什么在c版本时使用c版本并在不使用时使用汇编版本?
更新:以下主要功能用于测试性能:
常数,调用constant_test_bit:
int main(void) {
unsigned long i, j = 21;
unsigned long cnt = 0;
srand(111)
//j = rand() % 31;
for (i = 1; i < (1 << 30); i++) {
j = (j + 1) % 28;
if (constant_test_bit(j, &i))
cnt++;
}
if (__builtin_constant_p(j))
printf("j is a compile time constantn");
return 0;
}
这正确输出句子 J是...
对于其他情况,只需输入即可为j
分配"随机"号的行并相应地更改函数名称。当该线未注销时,输出将是空的,这是可以预期的。
我使用 gcc test.c -O1
编译,这是结果:
常数,constant_test_bit:
$ time ./a.out
j is compile time constant
real 0m0.454s
user 0m0.450s
sys 0m0.000s
常数,variable_test_bit(省略time ./a.out
,以下相同):
j is compile time constant
real 0m0.885s
user 0m0.883s
sys 0m0.000s
变量,constant_test_bit:
real 0m0.485s
user 0m0.477s
sys 0m0.007s
变量,variable_test_bit:
real 0m3.471s
user 0m3.467s
sys 0m0.000s
我的每个版本都运行了几次,上述结果是它们的典型值。constant_test_bit
函数似乎总是比variable_test_bit
函数更快,无论该参数是否为编译时间常数...对于最后两个结果(当j
不是常数时),变量版本甚至都比常数慢。一。我在这里错过了什么吗?
使用Godbolt我们可以使用Constant_test_bit进行实验,以下两个测试功能是用-O3
标志编译的gcc
:
// Non constant expression test case
int func1(unsigned long i, unsigned long j)
{
int x = constant_test_bit(j, &i) ;
return x ;
}
// constant expression test case
int func2(unsigned long i)
{
int x = constant_test_bit(21, &i) ;
return x ;
}
我们看到优化器能够优化常数表达情况:
shrq $21, %rax
andl $1, %eax
虽然非恒定表达情况最终如下:
sarl $5, %eax
andl $31, %ecx
cltq
leaq -8(%rsp,%rax,8), %rax
movq (%rax), %rax
shrq %cl, %rax
andl $1, %eax
因此,优化器能够为恒定表达情况产生更好的代码,我们可以看到,与variable_test_bit
中的手滚动组件相比,constant_test_bit
的非恒定案例非常糟糕,并且实现者必须相信恒定的表达式案例对于constant_test_bit
,最终比:
btl %edi,8(%rsp)
sbbl %esi,%esi
在大多数情况下。
为什么您的测试案例似乎显示出不同的结论是您的测试案例存在缺陷。我无法解决所有问题。但是,如果我们使用具有非恒定表达式的constant_test_bit
查看这种情况,我们可以看到优化器能够将所有工作移动到外观之外,并减少LOOP内部与constant_test_bit
相关的工作。
movq (%rax), %rdi
即使使用了较旧的gcc
版本,但这种情况可能与test_bit
的情况无关。在某些情况下可能不可能进行此类优化。