内核源码的时候经常可以看到likely()
和unlikely()
函数,这两个函数的作用是什么?-- 先得学一学GCC提供的内建函数!!
likely和unlikely内核中的定义
# define likely(x) __builtin_expect(!!(x), 1)
# define unlikely(x) __builtin_expect(!!(x), 0)
# define likely_notrace(x) likely(x)
# define unlikely_notrace(x) unlikely(x)
内建函数
GUN C语言提供了一系列内建函数以进行优化,这些内建函数以“_builtin”(build in function)作为前缀。
__builtin_constant_p(x)
判断x是否在编译时就可以被确定为常量,如果x为常量,那么返回1,否则返回0。
#define udelay(n) \
(__builtin_constant_p(n) ? \
((n) > (MAX_UDELAY_MS * 1000) ? __bad_udelay() : \
__const_udelay((n) * UDELAY_MULT)) : \
__udelay(n))
__builtin_expect(exp, c)
__builtin_expect
的函数原型为long __builtin_expect (long exp, long c)
, __builtin_expect (lexp, c)的返回值仍是exp值本身,并不会改变exp的值。
这里的意思是exp==c的概率很大,用来引导GCC用来条件分支预测,开发人员最清楚最可能执行哪个分支,并将最有可能执行的分支告诉编译器,让编译器优化指令序列排序,使指令尽可能的顺序执行,从而提高CPU预取指令的正确性,提升CPU执行性能。
# define likely(x) __builtin_expect(!!(x), 1) //x为真的可能性较大
# define unlikely(x) __builtin_expect(!!(x), 0) //x为假的可能性较大
为什么要使用!!符号呢?
计算机中bool逻辑只有0和1,非0即是1,当likely(x)中参数不是逻辑值时,就可以使用!!符号转化为逻辑值1或0 。比如:!!(3)=!(!(3))=!0=1,这样就把参数3转化为逻辑1了。
likely使用实例
static inline void native_set_ldt(const void *addr, unsigned int entries)
{
if (likely(entries == 0))
asm volatile("lldt %w0"::"q" (0));
else {
unsigned cpu = smp_processor_id();
ldt_desc ldt;
set_tssldt_descriptor(&ldt, (unsigned long)addr, DESC_LDT,
entries * LDT_ENTRY_SIZE - 1);
write_gdt_entry(get_cpu_gdt_rw(cpu), GDT_ENTRY_LDT,
&ldt, DESC_LDT);
asm volatile("lldt %w0"::"q" (GDT_ENTRY_LDT*8));
}
}
加likely
的意思是变量entries
的值为0的可能性较大,那么执行if的机会大,如果以上代码likely
改为unlikely
,则表示entries
的值不为0的可能性大一些,执行else机会大一些,加上这种修饰,编译成二进制代码时likely使得if后面的执行语句紧跟着前面的程序,unlikely使得else后面的语句紧跟着前面的程序,这样就会被cache预读取,增加程序的执行速度。
__builtin_prefetch(const void *addr, int rw, int locality)
主动进行数据预取,在使用addr的值之前就把该值读到cache中,降低读取时延,从而提高性能。
// inlcude/linux/prefetch.h
#ifndef ARCH_HAS_PREFETCH
#define prefetch(x) __builtin_prefetch(x)
#endif
#ifndef ARCH_HAS_PREFETCHW
#define prefetchw(x) __builtin_prefetch(x,1)
#endif
#ifndef ARCH_HAS_SPINLOCK_PREFETCH
#define spin_lock_prefetch(x) prefetchw(x)
#endif
prefetch()使用实例
void __free_pages_core(struct page *page, unsigned int order)
{
unsigned int nr_pages = 1 << order;
struct page *p = page;
unsigned int loop;
/*
* When initializing the memmap, __init_single_page() sets the refcount
* of all pages to 1 ("allocated"/"not free"). We have to set the
* refcount of all involved pages to 0.
*/
prefetchw(p);
for (loop = 0; loop < (nr_pages - 1); loop++, p++) {
prefetchw(p + 1);
__ClearPageReserved(p);
set_page_count(p, 0);
}
__ClearPageReserved(p);
set_page_count(p, 0);
atomic_long_add(nr_pages, &page_zone(page)->managed_pages);
/*
* Bypass PCP and place fresh pages right to the tail, primarily
* relevant for memory onlining.
*/
__free_pages_ok(page, order, FPI_TO_TAIL | FPI_SKIP_KASAN_POISON);
}
在处理page数据结构之前,可通过prefetchw()预取到缓存中,从而提升性能。