linux编程的108种奇易做图巧计-3(magic 2)
在编码的时候,编译器生成什么样的代码,往往我们是不清楚的,但一旦清楚了以后,就能利用这种性质来做一些优化,当然这种优化可能是有限的,但在累积的效应下,这些优化会显得很大,比如每天省1毛钱,省一辈子,也是一笔不小的开销。
我们来用一段简单的代码来说明这个问题,下面这段代码可能太平平无奇了,其中结构体test_1是3个字节,而test_2是4个字节,为了让test_2凑够2的倍数,特别加了一个padding字段。
详细的过程参见代码和附带的注释信息,我们把最后的结论提前,因为代码和分析有点长。
结论如下:
1)在结构体大小的选择上,我们尽可能选择2的幂,2,4,8,16
2)在做乘法时,尽可能选择2的幂,这样可以大大加快计算速度。
3)在设定一些变量时,尽可能选择2的幂,512,1024,而不是选择整数例如500,1000,这在计算上都可能带来低效。
最后本文介绍了gdb单步调试汇编的一些技巧,如果看寄存器的话,还可以加上display $rsp等命令,以后会有例子用到。
我的实验环境为64位系统,32位也基本类似,希望有兴趣的读者可以自行实验,获得更加真实的体验。
#include <stdlib.h>
#include <stdio.h>struct test_1
{
char a;
char b;
char c;
};struct test_2
{
char a;
char b;
char c;
char padding;
};int main(void)
{
int a,b,c,d;
a = 0;
b = 1;
c = 2;
a *= 2;
b *= 3;
c *= 4;
d *= 50;
//////////////乘法和加法的关系////////////////////////////
test_1* p = (test_1*)malloc(10*sizeof(test_1));
test_2* q = (test_2*)malloc(10*sizeof(test_2));
printf("sizeof test_1:%d,test_2:%d ",sizeof(test_1),sizeof(test_2));
//////////////结构体大小是2的倍数的好处///////////////////
for(int i=0;i<10;++i)
{
p[i].a = 0;
q[i].a = 1;
}return 0;
}以上程序用g++ -g test.cpp -o test生成可执行程序。我们这里都没有使用编译器优化,有兴趣的读者可以看看
g++ -O3 -g test.cpp -o test后的代码,很多地方在编译过程中就被略去了。
可用objdump -d test看汇编代码,我们下面只给出gdp单步调试的过程。
(gdb) b main
Breakpoint 1 at 0x4005c0: file test.cpp, line 22.
(gdb) r
Starting program: /opt/intel/vtune/test/esp/testBreakpoint 1, main () at test.cpp:22
22 a = 0;
(gdb) display /i $pc
1: x/i $pc 0x4005c0 <main+8>: movl $0x0,0xffffffffffffffd8(%rbp)
(gdb) ni
23 b = 1;
1: x/i $pc 0x4005c7 <main+15>: movl $0x1,0xffffffffffffffdc(%rbp)
(gdb)
24 c = 2;
1: x/i $pc 0x4005ce <main+22>: movl $0x2,0xffffffffffffffe0(%rbp)
(gdb)
25 a *= 2;
1: x/i $pc 0x4005d5 <main+29>: shll 0xffffffffffffffd8(%rbp) //乘2的情况下,只需要一个shll指令就能完成
(gdb)
26 b *= 3;
1: x/i $pc 0x4005d8 <main+32>: mov 0xffffffffffffffdc(%rbp),%edx
(gdb)
0x00000000004005db 26 b *= 3;
1: x/i $pc 0x4005db <main+35>: mov %edx,%eax
(gdb)
0x00000000004005dd 26 b *= 3;
1: x/i $pc 0x4005dd <main+37>: add %eax,%eax
(gdb)
0x0000penny04005df 26 b *= 3;
1: x/i $pc 0x4005df <main+39>: add %edx,%eax
(gdb)
0x00000000004005e1 26 b *= 3;
1: x/i $pc 0x4005e1 <main+41>: mov %eax,0xffffffffffffffdc(%rbp) //乘3的情况下,相当于2个加法,b=b+b;b=b+b
(gdb)
27 c *= 4;
1: x/i $pc 0x4005e4 <main+44>: shll $0x2,0xffffffffffffffe0(%rbp) //乘4的情况下,1个shll命令就能搞定
(gdb)
28 d *= 50;
1: x/i $pc 0x4005e8 <main+48>: mov 0xffffffffffffffe4(%rbp),%eax
(gdb)
0x000liang004005eb 28 d *= 50;
1: x/i $pc 0x4005eb <main+51>: imul $0x32,%eax,%eax //50不是2的倍数且不能靠有限加法来解决,只好用imul
(gdb)
0x00000000004005ee 28 d *= 50;
1: x/i $pc 0x4005ee <main+54>: mov %eax,0xffffffffffffffe4(%rbp)
(gdb)
32 test_1* p = (test_1*)malloc(10*sizeof(test_1));
1: x/i $pc 0x4005f1 <main+57>: mov $0x1e,%edi
。。。略去一部分分配和打印sizeof的汇编代码(gdb)
0x000000000040062d 36 for(int i=0;i<10;++i)
1: x/i $pc 0x40062d <main+117>: jmp 0x400659 <main+161>
(gdb)
0x0000000000400659 36 for(int i=0;i<10;++i)
1: x/i $pc 0x400659 <main+161>: cmpl $0x9,0xfffffffffffffffc(%rbp)
(gdb)
0x000000000040065d 36 for(int i=0;i<10;++i)
1: x/i $pc 0x40065d <main+165>: jle 0x40062f <main+119>
(g
补充:综合编程 , 其他综合 ,