今天发现一个奇怪的现象,用linux内核的bitops.h里面的bit_set和test_bit居然没有不用汇编的代码快。记录下来,备忘。
下面是可执行程序,THUIRDB暂时没有bitops里面的实现,改用我自己写的实现了。当然bitops里的实现,nr可以很大,而我的代码nr只能是0-63之间的一个数,对THUIRDB的应用来说,足够了。
#include "stdint.h"
#include <iostream>
using namespace std;
#define BITOP_ADDR(x) "=m" (*(volatile long *) (x))
#define ADDR BITOP_ADDR(addr)
#ifdef CONFIG_SMP
#define LOCK_PREFIX /
".section .smp_locks,/"a/"/n" /
_ASM_ALIGN "/n" /
_ASM_PTR "661f/n" /
".previous/n" /
"661:/n/tlock; "
#else
#define LOCK_PREFIX ""
#endif
static inline void
set_bit(unsigned int nr, volatile unsigned long *addr)
{
__asm__ volatile(LOCK_PREFIX "bts %1,%0"
: BITOP_ADDR(addr) : "Ir" (nr) : "memory");
}
static inline bool test_bit(int nr, volatile const unsigned long *addr)
{
int oldbit;
asm volatile("bt %2,%1/n/t"
"sbb %0,%0"
: "=r" (oldbit)
: "m" (*(unsigned long *)addr), "Ir" (nr));
return oldbit!=0;
}
static void _set_bit(int nr, uint64_t* addr)
{
addr[0] |= ( 1UL << nr);
};
static bool _test_bit(int nr,uint64_t* addr)
{
return (0 < (addr[0] & (1UL<<nr)));
};
int main(void)
{
uint64_t bit_arr=0;
#ifdef T1
for(int i = 0; i<1000000;++i)
{
_set_bit(id,&bit_arr);
}
#endif
#ifdef T2
for(int i = 0; i<1000000;++i)
{
set_bit(id,&bit_arr);
}
#endif
#ifdef T3
for(int i = 0; i<1000000;++i)
{
_test_bit(id,&bit_arr);
}
#endif
#ifdef T4
for(int i = 0; i<1000000;++i)
{
test_bit(id,&bit_arr);
}
#endif
return 0;
}
以下是反汇编的结果,可以看出用内核的方法,代码短小,但效率较低,用移位与或的方法代码多,但效率高。
000000000040067c <_ZL7set_bitjPVm>: //set_bit
40067c: 55 push %rbp
40067d: 48 89 e5 mov %rsp,%rbp
400680: 89 7d fc mov % edi,-0x4(%rbp)
400683: 48 89 75 f0 mov %rsi,-0x10(%rbp)
400687: 48 8b 45 f0 mov -0x10(%rbp),%rax
40068b: 8b 55 fc mov -0x4(%rbp),% edx
40068e: 0f ab 10 bts % edx,(%rax)
400691: c9 leaveq
400692: c3 retq
0000000000400693 <_ZL8test_bitiPVKm>://test_bit
400693: 55 push %rbp
400694: 48 89 e5 mov %rsp,%rbp
400697: 89 7d ec mov % edi,-0x14(%rbp)
40069a: 48 89 75 e0 mov %rsi,-0x20(%rbp)
40069e: 48 8b 45 e0 mov -0x20(%rbp),%rax
4006a2: 8b 55 ec mov -0x14(%rbp),% edx
4006a5: 0f a3 10 bt % edx,(%rax)
4006a8: 19 c0 sbb % eax,% eax
4006aa: 89 45 fc mov % eax,-0x4(%rbp)
4006ad: 83 7d fc 00 cmpl $0x0,-0x4(%rbp)
4006b1: 0f 95 c0 setne %al
4006b4: c9 leaveq
4006b5: c3 retq
00000000004006b6 <_ZL8_set_bitiPm>://_set_bit
4006b6: 55 push %rbp
4006b7: 48 89 e5 mov %rsp,%rbp
4006ba: 53 push %rbx
4006bb: 89 7d f4 mov % edi,-0xc(%rbp)
4006be: 48 89 75 e8 mov %rsi,-0x18(%rbp)
4006c2: 48 8b 45 e8 mov -0x18(%rbp),%rax
4006c6: 48 8b 10 mov (%rax),%rdx
4006c9: 8b 45 f4 mov -0xc(%rbp),% eax
4006cc: bb 01 00 00 00 mov $0x1,% ebx
4006d1: 48 89 de mov %rbx,%rsi
4006d4: 89 c1 mov % eax,% ecx
4006d6: 48 d3 e6 shl %cl,%rsi
4006d9: 48 89 f0 mov %rsi,%rax
4006dc: 48 09 c2 or %rax,%rdx
4006df: 48 8b 45 e8 mov -0x18(%rbp),%rax
4006e3: 48 89 10 mov %rdx,(%rax)
4006e6: 5b pop %rbx
4006e7: c9 leaveq
4006e8: c3 retq
00000000004006e9 <_ZL9_test_bitiPm>://_test_bit
4006e9: 55 push %rbp
4006ea: 48 89 e5 mov %rsp,%rbp
4006ed: 53 push %rbx
4006ee: 89 7d f4 mov % edi,-0xc(%rbp)
4006f1: 48 89 75 e8 mov %rsi,-0x18(%rbp)
4006f5: 48 8b 45 e8 mov -0x18(%rbp),%rax
4006f9: 48 8b 10 mov (%rax),%rdx
4006fc: 8b 45 f4 mov -0xc(%rbp),% eax
4006ff: 48 89 d3 mov %rdx,%rbx
400702: 89 c1 mov % eax,% ecx
400704: 48 d3 eb shr %cl,%rbx
400707: 48 89 d8 mov %rbx,%rax
40070a: 83 e0 01 and $0x1,% eax
40070d: 5b pop %rbx
40070e: c9 leaveq
40070f: c3 retq
T2用-O2优化后的代码,内联到main里面从400910到40093b