共计 1550 个字符,预计需要花费 4 分钟才能阅读完成。
1、volatile:
易变性:volatile 告诉编译器,某个变量是易变的,当编译器遇到这个变量的时候,只能从变量的内存地址中读取这个变量,不可以从缓存、寄存器、或者其它 任何地方读取。
顺序性:两个包含 volatile 变量的指令,编译后不可以乱序。注意是编译后不乱序,但是在执行的过程中还是可能会乱序的,这点需要由其它机制来保证,例如 memory- barriers。
不可优化性:volatile 告诉编译器,不要对这个变量进行各种激进的优化,甚至将变量直接消除,保证代码中的指令一定会被执行。
2、NRV(Named Return Value)优化:
函数返回一个类,例如下:
class X;X bar(){
X x1;
// 处理 x1..
return x1;
}
编译器实现:// 函数实现 void bar(X& __result) // 加上一个额外参数 {
// 预留 x1 的内存空间
X x1;
// 编译器产生的默认构造函数的调用,
x1.X::X();
// 处理 x1..
// 编译器产生的拷贝操作
__result.X::X(x1);
return;
}// 函数调用 X x2; // 这里只是预留内存,并未调用初始化函数 bar(x2);
NRV 优化后:void bar(X& __result){
// 调用__result 的默认构造函数
__result.X::X();
// 处理__result
return;
}
3、循环内变量优化:void test2(char *s);void test(){ int i; for (i = 0; i < 10; i ++) {
char buf[256];
test2(buf); // 调用 test2 是为了让编译器认为 buf 有用,以免被优化掉
}}
汇编代码:movl $10, %ebxsubl $272, %esp #分配 272 字节栈空间 leal -264(%ebp), %esi #取 buf 地址.L2:movl %esi, (%esp) #buf 地址入栈 call test2 #调用 test2subl $1, %ebxjne .L2 #循环未结束则跳到 L2
该函数中,buf 不会每次循环都生成,而是循环外生成,循环内不断的使用。
4、算数式优化 a * 2 被编译成 a +a;无符号数 a / 2 被编译成 a >>1;有符号数 a /2。
5、memset 函数优化 memset 函数常用来初始化大段内存,但对小数据来说 memset 能否保持足够高效呢?看这段程序:
编译成汇编:movl $0, -24(%ebp) #设置 s1movl $0, -20(%ebp)movl $0, -16(%ebp)movl $0, -12(%ebp)call test2 #调用 test2leal -8216(%ebp), %edx #设置 s2xorl %eax, %eaxmovl %edx, %edimovl $2048, %ecxrep stoslmovl %edx, (%esp) #调用 test2call test2movl %ebx, (%esp) #设置 s3movl $8193, 8(%esp)movl $0, 4(%esp)call memsetmovl %ebx, (%esp) #调用 test2call test2 当数据长度比较小时(如 s1 是 16 字节),memset 被编译成连续的赋值语句;当数据长度不大于 8KB 时(如 s2),memset 用串操作指令来实现;当数据长度大于 8KB 时(如 s3),memset 被编译成函数调用。
串操作类指令: 在内存一个存储区域连续存放着若干个字节(或字)数据,这样一组数据称为“数据串”(高级语言视为数组)。若每个数据是一个字节,称“字节串”;若是字,则称“字串”。串操作指令可以用来实现内存区域的数据串操作。串操作指令每次只处理数据串中的一个数据,但与重复前缀配合使用(重复前缀+串操作指令),则可使操作重复进行(其执行过程相当于一个循环程序的运行,重复次数由寄存器 CX 决定)。