【发布时间】:2015-05-08 02:18:54
【问题描述】:
在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,%1\n\tsbbl %0,%0"
:"=r" (oldbit)
:"m" (ADDR),"Ir" (nr));
return oldbit;
}
我了解__builtin_constant_p 用于检测变量是编译时常量还是未知变量。我的问题是:当参数是否为编译时间常数时,这两个函数之间是否存在性能差异?为什么有的时候用C版,没有的时候用汇编版?
更新:以下主要函数用于测试性能:
常量,调用常量_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 constant\n");
return 0;
}
这会正确输出句子j is a...
对于其他情况,只需取消注释将“随机”数字分配给j 的行并相应地更改函数名称。当取消注释该行时,输出将为空,这是预期的。
我用gcc test.c -O1编译,结果如下:
常数,常数_test_bit:
$ time ./a.out
j is compile time constant
real 0m0.454s
user 0m0.450s
sys 0m0.000s
constant, variable_test_bit(省略time ./a.out,下同):
j is compile time constant
real 0m0.885s
user 0m0.883s
sys 0m0.000s
变量,常量_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 不是常数时)变量版本是甚至比恒定的慢得多。
我在这里遗漏了什么吗?
【问题讨论】:
-
可能是,但找出答案的唯一方法是测量。
-
显然有人认为它对性能有影响,否则不会有 2 个版本。有关详细信息,您需要考虑 4 种情况(将常量/非常量传递给任一函数)。你认为每种情况会发生什么?你看过生成的程序集了吗?
-
@deviantfan 我已经添加了性能结果。
-
@MarcGlisse 这就是我的想法。以后可能会花一些时间研究汇编代码。
-
请注意,您并没有使用这些函数的“大数组中的一位”功能,而汇编版本可能会胜出(或不会)。
标签: c linux gcc optimization linux-kernel