Chinaunix首页 | 论坛 | 博客
  • 博客访问: 316365
  • 博文数量: 57
  • 博客积分: 146
  • 博客等级: 入伍新兵
  • 技术积分: 769
  • 用 户 组: 普通用户
  • 注册时间: 2012-05-29 14:57
文章分类
文章存档

2014年(39)

2013年(13)

2012年(5)

我的朋友

分类: C/C++

2014-02-20 16:51:52


1.构建泛型宏 

点击(此处)折叠或打开

  1. #define min(x, y) ({ \
  2. typeof(x) _min1 = (x); \
  3. typeof(y) _min2 = (y); \
  4. (void) (&_min1 == &_min2); \
  5. _min1 《 _min2 ? _min1 : _min2; }

编译器接受带双下划线的关键字:__typeof__typeof__。本文中的例子并没有遵循使用双下划线的惯例。从语句构成上看,typeof关键字后带圆括号,其中包含类型或表达式的名称。这类似于sizeof关键字接受的操作数(与sizeof不同的是,位字段允许作为typeof实参,并被解释为相应的整数类型)。从语义上看,typeof 关键字将用做类型名(typedef名称)并指定类型。

使用typeof的声明示例

下面是两个等效声明,用于声明int类型的变量a


点击(此处)折叠或打开

  1. typeof(int) a; /* Specifies variable a which is of the type int */
  2. typeof('b') a; /* The same. typeof argument is an expression consisting of
  3. character constant which has the type int */


以下示例用于声明指针和数组。为了进行对比,还给出了不带typeof的等效声明。


点击(此处)折叠或打开

  1. typeof(int *) p1, p2; /* Declares two int pointers p1, p2 */
  2. int *p1, *p2;

  3. typeof(int) * p3, p4;/* Declares int pointer p3 and int p4 */
  4. int * p3, p4;

  5. typeof(int [10]) a1, a2;/* Declares two arrays of integers */
  6. int a1[10], a2[10];

如果将typeof用于表达式,则该表达式不会执行。只会得到该表达式的类型。以下示例声明了int类型的var变量,因为表达式foo()int类型的。由于表达式不会被执行,所以不会调用foo函数。

extern int foo();
typeof(foo()) var;
使用typeof的声明限制

请注意,typeof构造中的类型名不能包含存储类说明符,如externstatic。不过允许包含类型限定符,如constvolatile。例如,下列代码是无效的,因为它在typeof构造中声明了extern

typeof(extern int) a;

下列代码使用外部链接来声明标识符b是有效的,表示一个int类型的对象。下一个声明也是有效的,它声明了一个使用const限定符的char类型指针,表示指针p不能被修改。

extern typeof(int) b;
typeof(char * const) p = "a";

TIPS:
1.(void)(&_min1 == &_min2) 是:为防止不同类型变量进行比较

点击(此处)折叠或打开

  1. int main(int ac, char** av)
  2. {
  3.     char i = 100;
  4.     long j = 1000;
  5.     long k = min(i, j);

  6.     return 0;
  7. }

编译上述代码时编译器会给出警告:comparison of distinct pointer types lacks a cast 
如果去掉(void) (&_min1 == &_min2) 这行,再编译是不会给出警告的。
2.使用该宏时,如果不关心返回值,没有圆括号()也可以,但是{}是必须的。



2. 范围的扩展
(1) switch 语句:

点击(此处)折叠或打开

  1. switch(a)
  2. {
  3. case 1 ... 3:
  4. printf(“fafadsf”);
  5. break;
  6. case 4 ... 8:
  7. printf(“dsafaf”);
  8. break;
  9. }

(2)数组的初始化:
int widths[] = { [0 。.. 9] = 1, [10 。.. 99] = 2, [100] = 3 };以上部分内核中用的很多。

3 .零长度的数组
struct iso_block_store {
atomic_t refcount;
size_t data_size;
quadlet_t data[0];
};
这允许结构中的元素引用结构实例后面紧接着的内存。在需要数量可变的数组成员时,这个特性很有用应用实例:
struct iso_block_store * p =(void *)malloc(sizeof(struct iso_block_store) + data_size);


4. 获得函数的返回地址
如下面的代码所示,__builtin_return_address 接收一个称为 level 的参数。这个参数定义希望获取返回地址的调用堆栈级别。例如,如果指定 level 为 0,那么就是请求当前函数的返回地址。如果指定 level 为 1,那么就是请求进行调用的函数的返回地址,依此类推。
void * __builtin_turn_address( unsigned int level );
在下面的示例中(见 。/linux/kernel/softirq.c),local_bh_disable 函数在本地处理器上禁用软中断,从而禁止在当前处理器上运行 softirqs、tasklets 和 bottom halves。使用 __builtin_return_address 捕捉返回地址,以便在以后进行跟踪时使用这个地址。
void local_bh_disable(void)
__local_bh_disable((unsigned long);
__builtin_return_address(0));
}


5 .常量检测
在编译时,可以使用 GCC 提供的一个内置函数判断一个值是否是常量。这种信息非常有价值,因为可以构造出能够通过常量叠算(constant folding)优化的表达式。__builtin_constant_p 函数用来检测常量。
__builtin_constant_p 的原型如下所示。注意,__builtin_constant_p 并不能检测出所有常量,因为 GCC 不容易证明某些值是否是常量。
int __builtin_constant_p( exp )
Linux 相当频繁地使用常量检测。在清单 3 所示的示例中(见 。/linux/include/linux/log2.h),使用常量检测优化 roundup_pow_of_two 宏。如果发现表达式是常量,那么就使用可以优化的常量表达式。如果表达式不是常量,就调用另一个宏函数把值向上取整到 2 的幂。
#define roundup_pow_of_two(n) \
( \
__builtin_constant_p(n) ? ( \
(n == 1) ? 1 : \
(1UL<<(ilog2((n) - 1) + 1)) \
) : \
__roundup_pow_of_two(n) \


6. 函数属性
GCC 提供许多函数级属性,可以通过它们向编译器提供更多数据,帮助编译器执行优化。本节描述与功能相关联的一些属性。
属性通过其他符号定义指定了别名。
# define __inline__ __inline__ __attribute__((always_inline))
# define __deprecated __attribute__((deprecated))
# define __attribute_used__ __attribute__((__used__))
# define __attribute_const__ __attribute__((__const__))
# define __must_check __attribute__((warn_unused_result))
定义是 GCC 中可用的一些函数属性。它们也是在 Linux 内核中最有用的函数属性。下面解释如何使用这些属性:
always_inline 让 GCC 以内联方式处理指定的函数,无论是否启用了优化。
deprecated 指出函数已经被废弃,不应该再使用。如果试图使用已经废弃的函数,就会收到警告。还可以对类型和变量应用这个属性,促使开发人员尽可能少使用它们。
__used__ 告诉编译器无论 GCC 是否发现这个函数的调用实例,都要使用这个函数。这对于从汇编代码中调用 C 函数有帮助。
__const__ 告诉编译器某个函数是无状态的(也就是说,它使用传递给它的参数生成要返回的结果)。
warn_unused_result 让编译器检查所有调用者是否都检查函数的结果。这确保调用者适当地检验函数结果,从而能够适当地处理错误。

下面是在 Linux 内核中使用这些属性的示例。deprecated 示例来自与体系结构无关的内核(。/linux/kernel/resource.c),const 示例来自 IA64 内核源代码(。/linux/arch/ia64/kernel/unwind.c)。

点击(此处)折叠或打开

  1. int __deprecated __check_region(struct resource *parent, unsigned long start, unsigned long n)

  2. static enum unw_register_index __attribute_const__decode_abreg(unsigned char abreg, int memory)

7 分支预测提示
在 Linux 内核中最常用的优化技术之一是 __builtin_expect。在开发人员使用有条件代码时,常常知道最可能执行哪个分支,而哪个分支很少执行。如果编译器知道这种预测信息,就可以围绕最可能执行的分支生成最优的代码。

如下所示,__builtin_expect 的使用方法基于两个宏 likely 和 unlikely(见 ./linux/include/linux/compiler.h)。
在内核代码中经常会看到unlikely和likely的踪影。他们实际上是定义在 linux/compiler.h 中的两个宏。
#define likely(x)    __builtin_expect(!!(x), 1)
#define unlikely(x)  __builtin_expect(!!(x), 0)
这里的__built_expect()函数是gcc的內建函数。
通过使用 __builtin_expect,编译器可以做出符合提供的预测信息的指令选择决策。这使执行的代码尽可能接近实际情况。它还可以改进缓存和指令流水线。
至于为什么要在内核代码中使用这两个宏,主要的目的是为了进行代码的优化,提高系统执行速度。
比如 :

点击(此处)折叠或打开

  1. if (likely(a>b)) {
  2. fun1();
  3. }
  4. if (unlikely(a<b)) {
  5. fun2();
  6. }

这里就是程序员可以确定 a>b 在程序执行过程中出现的可能相比较大,因此使用了likely()告诉编译器将fun1()函数的二进制代码紧跟在前面程序的后面,这样就cache在预取数据时就可以将fun1()函数的二进制代码拿到cache中。这样,也就增加了cache的命中率。
同样的,unlikely()的作用就是告诉编译器,a
我们不用对likely和unlikely感到迷惑,需要知道的就是 if(likely(a>b)) 和 if(a>b)在功能上是等价的,同样 if(unlikely(a

总之,likely和unlikely的功能就是增加cache的命中率,提高系统执行速度。

8 预抓取
另一种重要的性能改进方法是把必需的数据缓存在接近处理器的地方。缓存可以显著减少访问数据花费的时间。大多数现代处理器都有三类内存:
一级缓存通常支持单周期访问
二级缓存支持两周期访问
三系统内存支持更长的访问时间
为了尽可能减少访问延时并由此提高性能,最好把数据放在最近的内存中。手工执行这个任务称为预抓取。GCC 通过内置函数 __builtin_prefetch 支持数据的手工预抓取。在需要数据之前,使用这个函数把数据放到缓存中。如下所示,__builtin_prefetch 函数接收三个参数:
void __builtin_prefetch( const void *addr, int rw, int locality );
addr 数据的地址
rw   使用它指明预抓取数据是为了执行读操作,还是执行写操作
locality 使用它指定在使用数据之后数据应该留在缓存中,还是应该清除

Linux 内核经常使用预抓取。通常是通过宏和包装器函数使用预抓取。清单 6 是一个辅助函数示例,它使用内置函数的包装器(见 ./linux/include/linux/prefetch.h)。这个函数为流操作实现预抓取机制。使用这个函数通常可以减少缓存缺失和停顿,从而提高性能。

清单 6. 范围预抓取的包装器函数

点击(此处)折叠或打开

  1. #ifndef ARCH_HAS_PREFETCH
  2. #define prefetch(x) __builtin_prefetch(x)
  3. #endif

  4. static inline void prefetch_range(void *addr, size_t len)
  5. {
  6. #ifdef ARCH_HAS_PREFETCH
  7.     char *cp;
  8.     char *end = addr + len;

  9.     for (cp = addr; cp < end; cp += PREFETCH_STRIDE)
  10.         prefetch(cp);
  11. #endif
  12. }

9.变量属性
除了本文前面讨论的函数属性之外,GCC 还为变量和类型定义提供了属性。最重要的属性之一是 aligned 属性,它用于在内存中实现对象对齐。除了对于性能很重要之外,某些设备或硬件配置也需要对象对齐。aligned 属性有一个参数,它指定所需的对齐类型。

下面的示例用于软件暂停(见 ./linux/arch/i386/mm/init.c)。在需要页面对齐时,定义 PAGE_SIZE 对象。

char __nosavedata swsusp_pg_dir[PAGE_SIZE]
__attribute__ ((aligned (PAGE_SIZE)));

清单 7 中的示例说明关于优化的两点:

packed 属性打包一个结构的元素,从而尽可能减少它们占用的空间。这意味着,如果定义一个 char 变量,它占用的空间不会超过一字节(8 位)。位字段压缩为一位,而不会占用更多存储空间。
这段源代码使用一个 __attribute__ 声明进行优化,它用逗号分隔的列表定义多个属性。

清单 7. 结构打包和设置多个属性

点击(此处)折叠或打开

  1. static struct swsusp_header {
  2.         char reserved[PAGE_SIZE - 20 - sizeof(swp_entry_t)];
  3.         swp_entry_t image;
  4.         char orig_sig[10];
  5.         char sig[10];
  6. } __attribute__((packed, aligned(PAGE_SIZE))) swsusp_header;

简单__attribute__介绍:

阅读(1134) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~