112
飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/ 嵌入式Linux 性能

Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

  • Upload
    others

  • View
    9

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

嵌入式Linux性能优化

Page 2: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

2飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

嵌入式Linux性能优化

• 性能评价

• 性能优化的流程

• 性能评测

• 优化的基本原则

• Shell脚本优化

• C和C++程序优化

Page 3: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

3飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

性能评价

• 如何定义快和慢?– 快和慢与个人感官密切相关,有的人认为快,有的人认为慢,我

们需要在整个开发过程中参与的人员有一个共同的标准。

• 为每一个用户的操作,定义具体的时间值

– 一般精度在1/10秒

– 为一个设备所定义的性能指标,可能达到几十个。

2000010000关机

3000021000开机

版本2版本1下限理想性能

Page 4: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

4飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

性能优化的流程

• 性能优化是一个不断迭代的过程,不要认为一蹴而就。

Page 5: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

5飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

性能的评测

• 摄像头方式(测试人员使用居多)

– 优点:

• 使用起来非常简单,不需要熟悉代码。

• 更加接近用户实际的感受。

– 缺点:

• 时间的精确度受摄像头拍摄帧数的限制,一般精度不高,误差在100ms左右。

• 代码中增加log(开发人员使用居多)

– 优点:

• 测试时间的精确度将更高,能够精确到1ms左右。

• 能够灵活的定位代码中各个部分所占用的时间。

– 缺点:

• 在代码中增加时间的log,需要对代码非常的熟悉。

• 从log中得出的时间,往往因为各种意外情况的出现,往往会与用户的实际感受不一致。

Page 6: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

6飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

性能分析

• 导致程序运行效率低下,主要有下面3种原因:

– 程序的运算量很大,导致CPU过于繁忙,CPU是

瓶颈。

– 程序需要做大量的I/O,读写文件、内存操作等等,CPU更多的是处于等待,I/O部分称为程序性

能的瓶颈。

– 程序之间相互等待,结果CPU利用率很低,但运

行速度依然很慢,事务间的共享与死锁制约了程序的性能。

Page 7: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

7飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

性能分析

• 对于大量IO操作所引起的性能问题,我们可以考虑使用异步IO方式来提高程序性

能。

推荐一篇文章:http://www.ibm.com/developerworks/cn/linux/l-async/

• 后面所有的内容,主要针对程序量大,CPU是瓶颈的问题。

Page 8: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

8飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

优化的原则

• 等效原则:优化前后程序实现的功能一致。

• 有效原则:优化后要比优化前运行速度快或占用存储空间小,或二者兼有。

• 经济原则:优化程序要付出较小的代价,取得较好的结果。

Page 9: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

9飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

Shell脚本优化

• 在Linux系统中,我们会经常使用到shell脚本,我这里只介绍由busybox实现shell引擎的脚本优化。

• 在busybox中,命令分为三种类型:

– APPLET:也即我们所说的applets,它由busybox创建一个fork出一个子进程,然后调用exec执行相应的功能,待执行完毕后,返还控制给父进程。

– APPLET_NOEXEC:系统将调用fork创建子进程,然后执行busybox中对应的功能,在执行完毕后,返回控制给父进程。

– APPLET_NOFORK:它相当于builts-in,只是执行busybox的内部函数,不必创建子进程,所以其效率高。

Page 10: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

10飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

Shell脚本优化

• 在Linux中调用fork、exec是很花时间的,所以我们应该使用APPLET_NOFORK命令,其次是APPLET_NOEXEC, 后APPLET。

• 在busybox 1.9中,属于APPLET_NOFORK的功能有:

[ basename cat dirname echo false hostid length logname mkdir pwd rmrmdir seq sleep sync touch true usleep whoami yes

• 属于APPLET_NOEXEC的功能有:

awk chgrp chmod chown cp cut dd find hexdump ln sort test xargs

例如:

printf “hello world\n”echo “hello world\n”

Page 11: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

11飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

Shell脚本优化

• 在bash脚本中命令,还有一些特殊的约定:

– 包含在pipe中的builts-in将被创建子进程来执行。

– 包含在 ` 的命令将创建子进程来执行。

• 优化busybox bash脚本

– 去掉无用的脚本

– 尽可能使用busybox 内部实现的命令

– 尽量不要使用pipe– 减少pipe中的命令数

– 尽量不要使用`我们可以参考:

http://tree.celinuxforum.org/CelfPubWiki/OptimizeRCScripts。

Page 12: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

12飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

进程启动速度

• 查看进程启动速度

– # strace -tt ./hello23:31:02.618348 execve("./hello", ["./hello"], [/* 9 vars */]) = 023:31:02.624391 uname({sys="Linux", node="(none)", ...}) = 023:31:02.628785 brk(0) = 0x1100023:31:02.631655 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)23:31:02.634035 open("/etc/ld.so.cache", O_RDONLY) = -1 ENOENT (No such file or directory)23:31:02.636079 open("/lib/tls/v6l/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or

directory)…….……23:31:02.664339 close(3) = 023:31:02.667817 mprotect(0x412b8000, 4096, PROT_READ) = 023:31:02.670321 fstat64(1, {st_mode=S_IFCHR|0600, st_rdev=makedev(136, 1), ...}) = 023:31:02.672884 mmap2(NULL, 4096, PROT_READ|PROT_WRITE,

MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x40000000

Page 13: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

13飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

进程启动速度

• 查看进程启动过程

– # LD_DEBUG=libs ./hello432: find library=libc.so.6; searching432: search cache=/etc/ld.so.cache432: search path=/lib/tls/v6l:/lib/tls:/lib/v6l:/lib:/usr/lib/tls/v6l:/usr/lib/tls:/usr/lib/v6l:/usr/lib (system

search path)…………432: trying file=/lib/libc.so.6432: calling init: /lib/libc.so.6432: initialize program: ./hello432: transferring control: ./hello– # LD_DEBUG=help ./helloValid options for the LD_DEBUG environment variable are:libs display library search pathsreloc display relocation processingfiles display progress for input file

……….– help display this help message and exit

Page 14: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

14飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

进程启动速度

• 进程启动过程

– 搜索其所依赖的动态库。

– 加载动态库

– 初始化动态库

– 初始化进程

– 将程序的控制权,移交给main函数。

Page 15: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

15飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

进程启动速度

• 减少加载动态库的数量

– 使用dlopen,将启动时不需要的动态库,延后加载

– 将一些动态库,改为静态库

• 优点:

– 减少了加载动态库的数量。

– 在与其他动态库(或进程)合并之后,动态库内部之间的函数调用不必再进行动态链接、符号查找,从而提高速度。

• 缺点:

– 该动态库如果被多个动态库或进程所依赖的话,那么该动态库将被复制多份合并到新的动态库中,导致整体的文件大小增加,占用更多的flash。

– 失去了动态库原有的代码段内存共享,因此可能会导致内存使用上的增加。

Page 16: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

16飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

进程启动速度

• 加载动态库时的搜索路径:

– HWCAP机制

– DT_NEED入口中包含的路径

– DT_RPATH入口给出的路径(存在的话)

– 环境变量LD_LIBRARY_PATH路径(setuid类的程序排除)

– LD_RUNPATH入口给出的路径(存在的话)

– 库高速缓存文件ld.so.conf中给出的路径

– /lib /usr/lib

Page 17: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

17飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

进程启动速度

• HWCAP机制

– 在各个不同的系统种,系统的硬件功能是不一致的,有的系统支持浮点运算,有的则不支持。这样,由于系统硬件功能的不同,软件需要加载不同的共享库,glibc的HWCAP就是为了这个功能所设

计的。根据不同的硬件特性,到不同的目录去搜索动态库,来实现前面提到的需求。

• 禁止HWCAP

– # export LD_HWCAP_MASK=0x00000000

Page 18: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

18飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

进程启动速度

• 优化动态库搜索路径

– 设置LD_HWCAP_MASK,禁掉一些不用的硬

件特性。

– 将所有的动态库都放在一个目录下,并将该目录放在LD_LIBRARY_PATH的开始。

– 不能放在一个目录的,在进程中加入-rpath选项,指定搜索路径。

Page 19: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

19飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

进程启动速度

• 动态库的初始化

– 动态库的构造和析构函数机制。

– 动态库的全局变量初始化工作。

Page 20: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

20飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

进程启动速度

• 动态库的构造函数与析构函数

void __attribute__ ((constructor)) my_init(void)

void __attribute__ ((destructor)) my_fini(void);

• 对于内置类型的全局变量,存储在data段,在加载动态库时,只需mmap将其映射到内存中即

可。

• 对于非内置类型的全局变量,其存储在bss节,在使用mmap映射到内存之后,还需要运行其构造

函数将其构造出来。

Page 21: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

21飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

进程启动速度

• 动态库 liba.soclass c1{public:

c1();int c1_i1;

};c1::c1(){

printf("library 0\n");};c1 g1;• # ./hello• library 0

Page 22: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

22飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

进程启动速度

• 对于某些全局变量,其在进程启动过程中并不需要,但它仍然会在加载动态库和进程时,运行期构造函数,会导致:

– 构造函数中修改成员变量,dirty page不必要的增加。

– 运行构造函数,导致进程的启动速度变慢。

• 减少非内置类型全局变量的使用

– #nm -f sysv hello | grep bss– 在bss节,其为OBJECT,不包含函数名,对象大小

>4,基本可以认为是全局对象。

– 在bss节,其为OBJECT,不包含函数名,对象大小=4,有可能是全局对象,需要你到代码中去搜索、确认。

Page 23: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

23飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

进程启动速度

• 动态链接

– 减少导出符号的数量

gcc a1.cc –-version-script=sym.map –o test

– 减少符号的长度,加快符号匹配的速度

– 使用Prelink

Page 24: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

24飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

进程启动速度

• 使用Prelink– 配置prelink.conf,在该文件中增加所要做预先链接的进程和动态库所在的路

径。

Prelink.conf# If a directory name is prefixed with `-l ', the directory hierarchy# will be walked as long as filesystem boundaries are not crossed.# If a directory name is prefixed with `-h ', symbolic links in a# directory hierarchy are followed.-l /lib-l /usr/lib-l /usr/local/lib

– 运行prelink命令,将prelink.conf里面路径下的所有ELF文件,进行预先链接。

# prelink -afmR –c /etc/prelink.conf

Page 25: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

25飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

进程启动速度

• Prelink都做了哪些事情:

– 分析所有的进程和动态库,为每个动态库指定一块唯一的内存地址。

• 总是一同出现的动态库,其动态库的加载地址一定不能重叠;

• 总是不同时出现的动态库,其动态库的加载地址可以重叠。

– 分析进程和动态库中,所有需要重定位的函数,全局变量等,使用loader进程符号查找,对齐地址进行解析。

– 修改进程和动态库的二进制文件。

• 使用Prelink的先决条件:

– 在编译你的动态库时,加上-fPIC选项,生成位置无关代码。

– Glibc的版本要高于2.3.1– Prelink对于dlopen打开的动态库没有效果。

Page 26: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

26飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

进程启动速度

• 如果我们做了前面所有的工作,仍然无法满足进程启动速度的要求,那么我们只能在调度上下功夫。

– 进程改为线程

– Preload进程

– 提前加载,延后退出

– 调整CPU频率

Page 27: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

27飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

进程启动速度

• 进程改为线程

– 我们可以把原来的进程分割为两个部分:

• 常驻内存部分,其为daemon进程,主要负责加载进程所需要的动态库,侦听用户信号,创建和销毁用户逻辑线程;

• 完成用户逻辑部分,由daemon部分创建线程,按用户需求完成用户逻辑。

– 这样就节省掉了加载动态库、初始动态库和全局变量部分,可以缩短进程的响应时间,来满足用户需求。

– 我们还可以再引申一下,将原来的多个daemon进程的常驻内存部分进行合并,根据用户逻辑的需求,创建不同的线程。

Page 28: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

28飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

进程启动速度

• 进程改为线程

– 好处:

• 创建线程时,不需要重新加载动态库,故缩短了进程的响应时间

• 多个业务逻辑共享动态库,避免了系统为每个业务逻辑创建动态库的数据段,从而节省了大量的内存。

– 缺点:

• 由原来的进程改为线程,工作量比较大,代码修改上存在一定的风险。

• 多个业务逻辑线程之间共享动态库,有可能会带来全局变量的冲突。

• 由于还是存在daemon进程部分,所以其堆所栈内存不会被释放,多个业务逻辑线程所存在的内存泄漏会纠缠在一起,从而是问题更加复杂。

Page 29: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

29飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

进程启动速度

• Preload进程

– 我们在进程的main函数中,插入一行语句

pause();这样,当进程启动时,加载完动态库后,就会停在这里,不会运行用户逻辑。

– 当我们需要响应用户时,向该进程发送一个信号,这样用户就会继续前进,处理用户逻辑,这样我们就节省了进程加载动态库的过程。

这里我们,需要注册一个信号处理函数。

void sigCont(int unused){return ;

}int main(int argc, char** argv){

signal(SIGCONT,sigCont);pause();

– 当用户逻辑执行完成后,就退出进程,同时再启动该进程,这时进程会在加载完动态库后,停留在那里。

Page 30: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

30飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

进程启动速度

• Preload进程

– 好处:

• 该方法对于目前的进程来讲,修改起来十分容易。

• 由于没有了daemon部分,堆段在进程退出后,所以进程的内存泄漏问题影响不大。

– 缺点:

• 由于在进程退出后,需要重新启动进程,会带来调度上复杂性。

• 在进程等待的时候,其加载的动态库还是要占用一部分内存。

• 由于该方法只是将进程的启动分为了两个部分,并没有在整体上缩短进程的启动时间,故对于那些需要频繁启动、退出的进程效果并不理想。

Page 31: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

31飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

进程启动速度

• 当进程启动速度无法满足时,我们往往想到了将其提前加载(在开机时启动),却没有想到其退出条件,而导致进程中又多了一个daemon进程。

• 提前加载,延后退出,更加精确控制进程的生命周期。

– 在进入到有可能加载该进程的界面时,才去加载该进程。

– 在返回到先前界面,不可能再调用该进程时,退出进程。

Page 32: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

32飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

进程启动速度

• 调整CPU频率

– 嵌入式设备中,CPU一般有几个工作频率。

– CPU频率越高,运行速度越快,耗电量越高。

– 我们可以在启动前调高CPU频率,在完成后再调低CPU频率。

– 其是以耗电量增加为代价,不推荐。

Page 33: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

33飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

程序性能优化

• 性能优化≠effective C++ ≠ 高效代码

– Effective C++更加侧重于代码的良好的写作习

惯,日常代码的编写。

– 性能优化:不只是代码的优化,还包括程序逻辑的优化;对于代码,它更加看重对热点代码的优化。

– 高效的代码,对程序的性能有帮助,但并不意味着不需要优化。

Page 34: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

34飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

查找程序热点

• 当你面对成千上万行代码时,你要做的寻找程序热点。

– 20%的代码占用了80%的时间。

– 如果我们花精力优化这20%的代码,将对程序的性能有很大的帮助。

• 如何查找程序热点:

– gprof

– Oprofile

Page 35: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

35飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

查找程序热点

• gprof的基本用法:

– 使用 -pg 编译和链接你的应用程序,如果要得到带注释的源码清单,则需要增加-g选项。

– 执行你的应用程序,会在当前目录下产生gmon.out文件。

– 使用gprof 程序分析gmon.out文件,需要把它和产生它的应用程序关联起来:

gprof hello gmon.out –p 得到每个函数占用的执行时间。

• gprof的问题:

– 不支持动态库

– 不支持多线程

基本没有什么实用价值

Page 36: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

36飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

查找程序热点

• Opofile的使用

# opcontrol –reset //重置Oprofile的统计数据

# opcontrol --no-vmlinux //不分析内核

# opcontrol -i /mnt/msc_int0/hello //指名进程的路径

# opcontrol -e CPU_CYCLES:5000:0:1:1 //设置跟踪事件

# opcontrol –start //开始采样

Using 2.6+ OProfile kernel interface.Using log file /var/lib/oprofile/oprofiled.logDaemon started.Profiler running.# ./hello

//运行进程

# opcontrol –shutdown //停止采样

Stopping profiling.Killing daemon.

Page 37: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

37飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

查找程序热点

• # opreport –lsamples % symbol name2345696 99.6996 slow_multiply3968 0.1687 main3099 0.1317 fast_multiply

• # opannotate --source --base-dirs=/home/bookcode/ --search-dirs=/mnt/msc_int0/ ./hello

:int slow_multiply(x, y)/* slow_multiply total: 2345582 99.7027 */

: int i, j, z;1626169 69.1229 : for (i = 0, z = 0; i < x; i++)716317 30.4482 : z = z + y;1278 0.0543 : return z;123 0.0052 :}

Page 38: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

38飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

查找程序热点

• 在嵌入式设备中使用oprofile– 嵌入式设备中由于flash有限,故动态库和进程往往是去除符号的。

– 由于flash有限,动态库和进程,不具备调试信息。

– 这直接导致了opreport –l,不能列出每个函数所占CPU的时间,也无法使用opannotate将代码与时间相对应。

如果这样,oprofile几乎对我们没有什么用途。

Page 39: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

39飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

查找程序热点

• 我们可以采用在设备内采样,外部解析的方式。

– 生成符号表:#nm -n -C hello– 在设备上采样

# opcontrol --reset# opcontrol --no-vmlinux# opcontrol -e CPU_CYCLES:5000:0:1:1# opcontrol -i /mnt/msc_int0/hello# opcontrol --separate=lib# opcontrol –startUsing 2.6+ OProfile kernel interface.Using log file /var/lib/oprofile/oprofiled.logDaemon started.Profiler running.# ./hello# opcontrol --shutdownStopping profiling.Killing daemon.

Page 40: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

40飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

查找程序热点

• 在设备上运行# opreport –d000003ec 394 0.0754

000003f0 124 0.0237

000003f4 136 0.0260

........

• 使用前面获得的符号表,对其进行解析,就可以知道每个函数所占用的时间。

• 还可以使用#addr2line -e ./hello 000003f0 ,来获得指令与代码的对应关系,前提是进程(动态库)编译时使用了-g选项。

Page 41: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

41飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

查找程序热点

• Oprofile的局限

– oprofpp 不能够正确地归类内联函数样品 —oprofpp 使用一个简单的地址范围机制来决定它所

在的是哪个函数的地址。内联函数样品不从属于那个内联函数,而是从属于那个内联函数所插入的函数。

– 非CPU约束的性能问题 — OProfile 能够找出受CPU 约束的进程的问题。OProfile 不会识别正处

于睡眠状态的进程,因为这些进程正在等待锁或其它事件的发生(如等待 I/O 设备完成操作)。

Page 42: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

42飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

查找程序热点• Oprofile实战

int funca(int a,int b) {int ret;ret=a*a*a/b;ret=ret/b;ret=ret/b;return ret;

}int ret;int funcb() {

int i=0;int sum;for(i=0;i<1024*1024;i++) {

ret+=funca(200,i);sum+=i;

}printf("sum=%d\n",sum);return 0;

}

int main() {funcb();sleep();return 0;

}

# opcontrol --reset# opcontrol --no-vmlinux# opcontrol -e CPU_CYCLES:5000:0:1:1# opcontrol --separate=lib# opcontrol -i /mnt/msc_int0/hello# opcontrol --startUsing 2.6+ OProfile kernel interface.Using log file /var/lib/oprofile/oprofiled.logDaemon started.Profiler running.# ./hellosum=-1091043676# opcontrol --shutdownStopping profiling.Killing daemon.

Page 43: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

43飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

查找程序热点

• # opreport –lsamples % image name symbol name18872 66.6337 hello __aeabi_idiv6727 23.7519 hello funca2651 9.3602 hello funcb

• 从这个结果上看,我们应该着重优化funca。• 可实际上,funca并没有用途,我们应该去掉。

• 这个例子告诉我们,在我们知道某个函数是热点函数之后,不要急于去优化该函数,还要花很大的精力去了解为什么会跑到这个函数,这个程序的逻辑是否合理?

Page 44: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

44飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

查找程序热点

• 当我们拿到每个函数占用时间的百分比之后。

– 先检测耗时前几名的函数,按照程序执行的逻辑来讲,是否合理,是否有存在的必要?

– 弄清楚排名前几名函数的调用关系。

• 比如:我们看到libc的memcpy函数占用了大量的内

存,但问题可能是,你的程序里为什么会做大量的memcpy,而不是去如何优化memcpy。

– 尝试去优化耗时前几名的函数。

Page 45: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

45飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

查找程序热点

• 一个Case:

一个屏幕,中间一个区域变化。

实现方案:当中间区域变化时,修改对应的变量,整个屏幕重画。

• 你应该如何优化?

• Oprofile并不能直接帮助我们发现程序逻

辑上的问题。

Page 46: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

46飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

程序性能优化

• 程序逻辑优化

一般我们通过代码的优化,可以将程序的性能提高几倍。可是如果我们在上层软件逻辑、算法上的优化,却可以将程序的性能提高十几倍。

– 先缕清楚程序运行过程中多做了哪些事情

– 程序在做这些事情的时候,都用了多少时间。

– 在我们知道了各部分所用的时间之后,就能很清楚的了解从逻辑层面上,性能的瓶颈在哪里。

– 这时我们就可以考虑如何优化程序的逻辑,哪些部分可以去掉、哪些部分可以优化?

Page 47: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

47飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

程序性能优化

• 优化的层次

– 上层业务逻辑的优化

这更多的是面向具体的业务逻辑,我们可以通过打log的方

式,来获得软件各个逻辑功能模块所占用的时间,找到性能瓶颈,进行优化。

– 底层基础函数性能优化

这是一个打基础的过程,如果底层地基打不好,那么上层应用就会疲于应付KPI,不断的简化业务逻辑,甚至有些功

能不得不舍弃,这不是我们所期望的。

这时候,我们要使用oprofile,多跑一些case,发现底层热点

函数,着重去优化它。

Page 48: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

48飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

程序性能优化

• 何时开始性能优化

– 在需求阶段,在提出用户需求的同时,也要把性能指标也要定义下来。

– 在软件设计阶段,要考虑这些性能指标,根据这些指标来考虑程序所使用的算法、逻辑,在这个阶段就要考虑逻辑上的优化。

– 在软件功能基本完成后,一方面软件的逻辑要做一些细微的调整,同时要开始使用oprofile来查找热点函数,对热点函数做代码优化。

Page 49: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

49飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

程序性能优化

• 程序逻辑优化

– Do it faster。

– Do it in parallel

– Do it later

– Don't do it at all

– Do it before.

Page 50: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

50飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

程序性能优化算法的优化

• 常用的算法优化

– 采用复杂度更低的排序算法

– 使用查表法取代计算密集型操作

– 考虑事件的特殊性

Page 51: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

51飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

程序的优化

• 代码优化的境界

– 当你面向代码时,你能看到编译器对其优化后所产生的汇编指令。这样你就可以清楚,什么样的代码,产生的汇编指令运行的速度更高,也就清楚如何去优化它。

– 你要了解你所面向的芯片组,当你面向代码时,能够看到汇编语言在硬件中的执行状态。

这两个境界,为所有代码优化人员指明了前进的方向。

Page 52: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

52飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

程序的优化

• GCC编译优化

• 标准C语言代码优化

• C++代码优化

• 硬件相关的优化

Page 53: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

53飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

GCC编译优化

• 使用条件编译,替代某些平台相关的参数判断。

– 在生成的代码中,指令更加精简,速度更快。

• 使用built-in函数,来完成一些特殊的功能,例如实现SIMD指令。

• GCC编译优化选项:http://man.lupaworld.com/content/develop/GCC_zh.htm

• GCC与G++的不同

• 指令CPU的型号

– #gcc –o hello –mcpu=arm1136js –O2 hello.c

Page 54: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

54飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

标准C代码优化

• 数据类型:int char short那种效率 高。

一种说法:

8位和16位的变量,在做完加法操作后,需要在32位的寄存器中进行符号扩展,其中带符号的变量,要用逻辑左移(LSL)接算术右移(ASR)两条指令才能完成符号扩展;无符号的变量,要使用一条逻辑与(AND)指令对符号位进行清零。

char a;

a=a+1

ADD a1,a1,#1

AND a1,a1,#&ff

short a;

a=a+1

ADD a1,a1,#1

MOV a1,a1,LSL #16

MOV a1,a1,ASR #16

int a;

a=a+1

ADD a1,a1,#1

Page 55: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

55飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

标准C代码优化

• 代码优化方法与你所使用编译的版本、目标的体系结构高度相关。

• 这里所面向的平台:

– 编译器:gcc version 3.4.3

– 硬件:ARM11芯片组,ARM1136JF-S

– 编程语言:C和C++

Page 56: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

56飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

标准C代码优化

• 实际上

char p=0;p++;mov r3, #0strsb r3, [fp, #-13]ldrsb r3, [fp, #-13]add r3, r3, #1strsb r3, [fp, #-13] 在ARM V4版本增加了半字的读取和写入指令;读取带符号的字节和半

字数据的指令。

故int char short的性能是一样的。

Page 57: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

57飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

标准C代码优化

• 数组

– 数组寻址

int m[20][32];

ret=m[2][3]

注意:r0=2;r1=3;r3为二维数组m的起始地址

add r1, r1, r0, lsl #5 //r1=3+2*32=67

ldr r1, [r3, r1, lsl #2]

二维数组的寻址地址由原来的3条,减少到现在的2条。其主要原因在于二维数组的列数为2的幂次方。

int m[20][20];

ret=m[2][3]

注意:r0=2;r1=3,r2为二维数组m的起始地址;

add r3, r0, r0, lsl #2 // r3=2+2*4=10

add r3, r1, r3, lsl #2 //r3=3+10*4=43

ldr r1, [r2, r3, lsl #2] //因为二维数组m是int型,所以偏移还要乘以4

Page 58: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

58飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

标准C代码优化

• 尽量使用一维数组

– 也就是我们上面看到的,一维数组在寻址时要比多维数组效率要高。

– 在数组遍历时,一维数组遍历要比多维数组遍历使用更少的指令,因而效率也更高。

Page 59: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

59飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

标准C代码优化

• 结构83e4: e24dd008 sub sp, sp, #8 ; 0x8 //为s1申请栈空间

83e8: e59d2004 ldr r2, [sp, #4] //r2=s1.n2

83ec: e1a01000 mov r1, r0 //r1=n

83f0: e3a03009 mov r3, #9 ; 0x9 //i=9

83f4: e0820001 add r0, r2, r1 //r0=s1.n2+n

83f8: e2533001 subs r3, r3, #1 ; 0x1 //i--

83fc: e1a02000 mov r2, r0 //s1.n2=r0

8400: 5afffffb bpl 83f4 <func+0x10>

8404: e28dd008 add sp, sp, #8 ; 0x8

8408: e12fff1e bx lr

struct MyStruct {

int n1, n2;

};

int func(int n) {

struct MyStruct s1;

int ret,i;

for(i=0;i<10;i++)

s1.n2+=n;

ret=s1.n2;

return ret;

}

Page 60: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

60飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

标准C代码优化

• 结构变量对程序性能的影响:

– 如果函数中存在结构变量,编译器将为其分配栈空间,即使还有空闲的寄存器,对程序的性能有负面作用。

– 函数中第一次访问结构成员变量时,会根据结构变量的首地址+偏移的方式,将结构成员变

量加载到寄存器中;以后再访问该变量时,直接操作寄存器就可以了,不会影响程序的性能。

Page 61: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

61飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

标准C代码优化

• 变量

– 全局变量与静态变量性能比较

• 全局变量和静态变量其生命周期都是一样,与process的生命周期相同,都是存储在程序的数据段

中,只是其作用域范围不同。不过这种作用域范围,只是针对于编译器来讲,在编译器编译时,会检测相关的错误;但在编译后的汇编代码执行角度来讲,其都是从数据段加载,静态变量所对应的数据段数据,在程序范围内都是有效的。

• 性能是一样的。

Page 62: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

62飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

标准C代码优化

• 栈变量与全局变量8430: e59fe034 ldr lr, [pc, #52] ; 846c //获取m的地址

8434: e59f0034 ldr r0, [pc, #52] ; 8470 //获取n的地址

8438: e59e3000 ldr r3, [lr] //r3=m

843c: e5902000 ldr r2, [r0] //r2=n

8440: e0833001 add r3, r3, r1 //m+=i

8444: e0822001 add r2, r2, r1 //n+=i

8448: e2811001 add r1, r1, #1 ; 0x1

844c: e151000c cmp r1, ip //比较i是否<a

8450: e58e3000 str r3, [lr]//将m的数值回写到数据段

8454: e5802000 str r2, [r0]//将n的数值回写到数据

段。

8458: bafffff6 blt 8438 <func+0x24>

int m=5;

int func(int a){

int ret,i;

static int n=7;

for(i=0;i<a;i++) {

n+=i;

n+=i;

}

……..

}

Page 63: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

63飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

标准C代码优化

• 栈变量与全局变量

int m=5;

int func(int a){

int ret,i;

static int n=7;

int j=m;

for(i=0;i<a;i++) {

j+=i;

n+=i;

}

m=j; ret =n+m;

return ret; }

int m=5;

int func(int a){

int ret,i;

static int n=7;

for(i=0;i<a;i++) {

m+=i;

n+=i;

}

ret =n+m;

return ret;

}

优化后优化前

Page 64: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

64飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

标准C代码优化• 变量作用域与栈顶指针的变化

int c1,c2;

for(i=0;i<10;i++) {

c1=0;

c2=1;

printf("%d %d\n",c1,c2);

}

for(i=0;i<10;i++)

{

int c1=0;

int c2=1;

printf("%d %d\n",c1,c2);

}

ldr r0, [pc, #36] ; 84d0 <.text+0x164> //获取字符串”%d %d\n"的地址。

add r5, r5, #1 ; 0x1 //r5对应与变量i

mov r2, #0 ; 0x0 //r2对应变量c1

mov r3, #1 ; 0x1 //r3对应变量c2

bl 8360 <__plt_header+0x38>//调用函数printf

cmp r5, #9 ; 0x9

Page 65: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

65飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

标准C代码优化

• 变量作用域与栈顶指针变化

8418: e1a02000 mov r2, r0 //r0对应于变量m

841c: e3520000 cmp r2, #0 ; 0x0

8420: e59f0024 ldr r0, [pc, #36] ; //r0对应”%d \n”字符产的地址

8424: 059f0024 ldreq r0, [pc, #36] ; //如果m为0,r0加载”%d 1\n”字符串的地址

8428: e24dde4b sub sp, sp, #1200 ; //为a[100]和b[200]开辟栈空间

842c: e24dd004 sub sp, sp, #4 ; 0x4

8430: 159d1320 ldrne r1, [sp, #800]//if m!=0 r1=a[0]

8434: 059d1000 ldreq r1, [sp] //if m==0 r1=b[0]

8438: ebffffc8 bl 8360 <__plt_header+0x38> //printf

843c: e3a00000 mov r0, #0 ; 0x0 //返回值0

int test(int m)

{

if(m) {

int a[100];

printf("%d\n",a[0]);

}else {

int b[200];

printf("%d 1\n",b[0]);

}

return 0;

}

Page 66: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

66飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

标准C代码优化

• 变量作用域与栈顶指针变化

– 在进入函数分配栈空间的时候,程序并不是按照执行分支的不同,不断的修改栈顶地址,而是在进入函数的时候,为所有的变量所占用的空间求和,一次性配置栈地址,不管这时变量是否有效。这样的好处在于GCC能够很方便

的知道各个变量在栈中的位置,不会导致栈因为程序执行路径的不同,变量位置不同所带来的定位的复杂性。这样所带来的后果便是,栈将会使用大量的内存。

Page 67: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

67飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

标准C代码优化

• 慢操作

– 除法

– 浮点运算

Page 68: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

68飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

标准C代码优化

• 除法

– 在ARM指令集中没有除法指令,其除法是通过C库函数实现的。根据执行情况和输入操作数的范围,一个32位的除法通常需要20~140个时钟周期。

– 尽量少用除法

– 用减法代替除法

– 当除数为2的幂次方时,使用移位操作代替除法

– 利用与运算代替求余运算

– 合并除法

– 用乘法代替除法

– 近似计算除法 (该替代方法不等价) – 用查表的方法代替除法

Page 69: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

69飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

标准C代码优化

• 浮点

– 如果没有浮点协处理器,我们可以使用软浮点

• #gcc test.c –msoft-float –o test

– 如果系统有浮点协处理器,我们可以使用硬浮点。

• #gcc -o hfloat –mcpu=arm1136js f1.c (gcc缺省支持硬浮点)

注意,这里我们还要告诉gcc CPU的型号,以便生成对应的指令。

– arm11jf-s使用的是VFP11协处理器,除了支持浮点运算的:单精度、双精度浮点数加、减、乘、除外,还支持乘方、平方根、绝对值、浮点取整、整数转换到浮点,单精度和双精度浮点之间的转换。

Page 70: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

70飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

标准C代码优化

• 浮点运算优化:

– 将浮点运算转换为乘除运算。

– 优先使用浮点处理器;如果系统不支持浮点处理器,使用glibc提供的软浮点。

– 对于计算密集型的算法,我们可以通过转化为查表来进行优化。

– 在保证计算精度的前提下,确定浮点型变量和表达式是 float 型

Page 71: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

71飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

标准C代码优化

• if语句

代码的性能是一样的。

e3540000 cmp r4, #0 ; 0x0

02855004 addeq r5, r5, #2 ; 0x2

e3540000 cmp r4, #0 ; 0x0

12855002 addne r5, r5, #2 ; 0x2

if(!c) ret+=2; if(c) ret+=2;

code2code1

Page 72: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

72飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

标准C代码优化

• if连接表达式

– 从左到右对表达式求值

– 当结果确定后,不再有其他需要计算的表达式,也就是俗称的“短路”。

• 优化方法:

– 删除冗余条件 例如:if(a>0&&a<0x666&&a!=0)– 删除肯定不成立的条件 if(a!=0&&a==0)– 利用短路机制,将计算速度 快的表达式放在左

边。

• if((strlen(a)>100) || (b>100))

Page 73: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

73飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

标准C代码优化

• if语句并不一定导致分支语句

84c8: e3540000 cmp r4, #0 ; 0x0 // if(b)

84cc: e2833001 add r3, r3, #1 ; 0x1

84d0: 12866002 addne r6, r6, #2 ;//if(b!=0) ret+=2

84d4: 12855001 addne r5, r5, #1 ;//if(b!=0) ret1+=1

84d8: 02866003 addeq r6, r6, #3 ;//if(b==0) ret+=3

84dc: 02855002 addeq r5, r5, #2 ; //if(b==0) ret1+=2

if(b)

{

ret+=2;

ret1+=1;

}else

{

ret+=3;

ret1+=2;

}

Page 74: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

74飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

标准C代码优化

• switch语句

– 在case值变化不大的时候,并且有足够的分支(大于4个分支)的时候,将采用跳转表的方式进行优化,default值的效率 高。

– 在case值变化比较大的时候,将采用二叉比

较的方式进行优化,减少判断的深度,这时取值中间的case值,效率 高。

Page 75: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

75飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

标准C代码优化

• 循环语句,优化的重点

– 将不变的代码移到循环之外

– 将分支语句提到循环的外面

if(n==1) {

for(i=nloop;i>=0;i--)

j+=2;

}else {

for(i=nloop;i>=0;i--)

j+=1;

}

for(i=nloop;i>=0;i--)

if(n==1)

j+=2;

else

j+=1;

Page 76: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

76飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

标准C代码优化

• 循环语句优化

– 循环的展开,通过循环分支的展开,我们可以降低循环的次数,从而减少分支语句对循环的影响。

for(n = 0; n <1024*256 ;n++)

{

n1++;

n1++;

n1++;

n1++;

}

31708 ms

for(n = 0; n <1024*512 ;n++)

{

n1++;

n1++;

}

41505 ms

for(n = 0; n <1024*1024 ;n++)

{

n1++;

}

103576 ms

Page 77: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

77飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

标准C代码优化

• 循环语句优化

– 循环的合并

– 用减1指令替换循环加1指令

for(b=0;b<10;b++) {

m[b]=b;

n[b]=b;

}

for(b=0;b<10;b++)

m[b]=b;

for(b=0;b<10;b++)

n[b]=b;

8450: e2533001 subs r3, r3, #1 ; 0x1

8454: e2811002 add r1, r1, #2 ; 0x2

8458: 5afffffc bpl 8450

842c: e2833001 add r3, r3, #1 ; 0x1

8430: e1530004 cmp r3, r4

8434: e2811002 add r1, r1, #2 ; 0x2

8438: dafffffb ble 842c

for(i=nloop;i>=0;i--) j+=2;for(i=0;i<=nloop;i++) j+=2;

Page 78: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

78飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

标准C代码优化

• 函数

– 内联函数

• 为了调试方便,当函数在编译时,没有采用-O指定优化级别,内联函数不做展开,除非用户使用always_inline,来声明函数。

• 编译器展开内联函数的前提是,在同一个编译单元能够找到该函数的定义,否则就不能展开内联函数。因此,内联函数的声明和定义一般都包含在头文件中。

• inline修饰符并非强制性的,编译器有可能会对它置之不理。例如,递归函数通常不会被编译成inline函数,编译器有权自行决定是否要将定义成inline的函数编译成inline。

• 展开inline函数并不一定会导致代码量增大。因为在函数调用时,需要使用一些指令来安排进出函数时,栈结构的变化;而如果inline函数展开,则省去了这些指令。如果inline函数体的代码量小于安排栈帧的代码量,那么将函数标识为inline,反而会使代码量减小。

• GCC在编译时,如果使用了-O3优化编译开关,其会将一些代码量小的函数转变成inline来处理,即使这个函数没有使用inline标识符。

• 在C++中,在类的内部定义了函数体的函数,被默认为是内联函数。而不管你是否有inline关键字。

Page 79: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

79飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

标准C代码优化

• 函数

– 纯函数

是指不会影响它自己范围外任何事情的函数。它可以读取全局变量或者通过指针传递的变量,但是不可以对这些变量进行些操作。它也不可以读取volatile变量和外部资源(比如文件)。

– 纯函数

是纯函数的更严格版本。它不会读取和写除参数外的任何数据,也不可以使用指针参数来读取数据。“函数的结果只依赖参数”。

int func(int a, int b) __attribute__ (pure);

Page 80: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

80飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

标准C代码优化

• 寄存器的使用,遵从ATPCS标准。

• ATPCS标准:

– 子程序间通过寄存器R0~R3来传递参数。被调用的子程序在返回前无需恢复寄存器R0~R3的内容。

– 在子程序中,使用寄存器R4~R11来保存局部变量。如果在子程序中使用了寄存器R4~R11中的某些寄存器,子程序进入时必须保存这些寄存器的值,在返回前必须恢复这些寄存器的值;对于子程序中没有用到的寄存器则不必进行这些操作。

– R12用作子程序间scratch寄存器,记作ip。在子程序间的连接代码段中经常使用这种规则。

– R13用作数据栈指针,记作sp。在子程序中寄存器R13不能用作其他用途。

– R14称为连接寄存器,记作lr。它用来保存子程序的返回地址。

– R15是程序计数器,记作pc。– 子程序返回结果为一个32位整数时,可以通过寄存器R0返回;结果为一个64

位整数时,可以通过寄存器R0和R1返回,依次类推。

Page 81: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

81飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

标准C代码优化

• 函数的参数 好不多于4个– 4个以下的形参可以通过寄存器来传递,4个以上的参数,则要通过栈来传递。

– 同时如果参数小于4个,则r0~r4中剩余的寄存器可以用来保存函数中的局部变量。

• 减少局部变量的个数

– 尽量限制函数内部循环所用局部变量的数目, 多不超过12个,以便编译器能把变量分配到寄存器。

– 如果没有局部变量保存到栈中,系统也将不必设置和恢复栈指针。

• 当函数内部寄存器变量多于12个时,并不意味着只是将前面的12个临时变量分配寄存器,之后的临时变量都是通过栈内存来操作。

– 当寄存器分配完后,遇到新的临时变量时:

– 查看已分配寄存器的局部变量,是否有在后面的代码中不会再被使用,则新的局部变量使用其所占用的寄存器。

– 如果已分配寄存器的局部变量,在后面的代码中都要使用,则要选择出一个临时变量,将其保存到栈中;将其所使用的寄存器分配给局部变量。

Page 82: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

82飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

标准C代码优化

• 文件操作

– 读写文件,缓冲区的buffer为4096 2048时 快。

– 利用mmap,读写文件。

利用mmap实现的一个文件拷贝例子

其基本流程是

• 1、创建一个与源文件大小相同的目标文件;

• 2、使用mmap,分别将源文件和目标文件映射到内存中。

• 3、使用memcpy,将文件读写操作,转换成内存的拷贝操

作。

Page 83: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

83飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

标准C代码优化

• 线程

– 创建线程是有代价的,如果只让线程做很少的事,而又频繁的创建和销毁线程,是得不偿失的。

– 使用异步IO,来取代多线程+同步IO的方式。

– 使用线程池,取代线程的创建和销毁。

Page 84: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

84飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

C++代码优化

• 构造函数与析构函数

• 对象的作用域

• 访问成员变量

• 成员函数

• 全局对象与静态对象

• 栈对象与堆对象

Page 85: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

85飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

C++代码优化

• 构造函数与析构函数

Myclass obj;– 1、在函数的栈中,为该对象分配一片内存,能够容纳

其所有的类成员。

– 2、如果其有基类,先运行基类的构造函数。

– 3、如果类的成员变量为类对象,那么还要运行类成员变量的构造函数。

• 步骤2和3,视类的继承层次是一个递归的过程。总的来讲,先构造基类,然后再构造父类。

• 小心使用对象数组,将为每个数组成员运行构造函数。

Page 86: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

86飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

C++代码优化

• 在声明对象时进行初始化

– 例如: Myclass obj = data;

• 构造函数初始化列表

class c1: public Base {public:c1():i(10) {………………….}private:

int i;}

• 内联构造函数和析构函数

Page 87: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

87飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

C++代码优化

• 对象的作用域

class Myclass;

………

for(int i=0;i<10;i++)

{

Myclass obj;

……..

}

for(int i=0;i<10;i++)

{

int j;

……..

}

非内置类型内置类型

Page 88: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

88飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

C++代码优化• 访问成员变量

8434: e52de004 str lr, [sp, #-4]!

8438: e24dd00c sub sp, sp, #12 ; 0xc//为对象obj分配栈空间。

843c: e1a0000d mov r0, sp //r0为对象的起始地址

8440: ebfffff6 bl 8420 //有了对象的起始地址,开始运行对象的构造函数

8444: e59d0004 ldr r0, [sp, #4] //对象首地址偏移4,就是成员变量m的地址,ret=obj.m。

8448: e28dd00c add sp, sp, #12 ; 0xc

844c: e8bd8000 ldmia sp!, {pc}

class Myclass{

public:

int n,m;

Myclass();

};

Myclass::Myclass(){

n=1; m=2;

}

int func(){

Myclass obj;

int ret=obj.m;

return ret;

}

Page 89: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

89飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

C++代码优化

• 访问成员变量

– 第一次是通过对象首地址加偏移的方式加载到寄存器中,以后对该成员变量的访问,都将在寄存器中执行。

– 公有变量与私有变量,只是编译器层的概念,执行的效率是一样的。

– 静态成员变量,存储在数据段,其性质与全局变量一样,在做循环时会频繁的读写数据段内存。

Page 90: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

90飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

C++代码优化

• 成员变量

– 隐含this指针

– 将值传递改为引用传递

• 比如:

• int func(Object a);• 改为

• int func(const Object &a);

int Myclass::sum(this){

int ret=this->m+this->a;

return ret;

}

int Myclass::sum(){

int ret=m+a;

return ret;

}

Page 91: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

91飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

C++代码优化

• 静态成员函数没有this指针。

• 虚函数在运行时动态绑定,效率要比其他函数低。

Page 92: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

92飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

C++代码优化

• 全局对象

– 在动态库加载时,运行其构造函数,创建全局对象。

• 函数内的静态对象

– 当第一次运行到静态对象声明语句时,运行其构造函数。那么是如何区分第一次的呢?

int func() {static Myclass obj;obj.n++;return obj.n ;

}在BSS区,为obj分配内存外,还分配了一块4字节的内存来指示obj是

否被创建。

Page 93: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

93飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

C++代码优化

• 在func函数中,其运行流程如下:

– 首先检测_ZGVZ4funcvE3obj是否为1。– 如果为1,说明静态对象obj已经创建,则不必运行其构

造函数。

– 如果为0,说明静态对象obj没有创建,则运行其构造函数,并在创建完对象后将_ZGVZ4funcvE3obj置为1。

• 因此全局对象与函数内的静态对象性能上的差别在于:

– 全局对象是在进程启动时创建;函数内的静态对象是在第一次运行时创建。

– 在代码中使用全局对象时,可以直接使用全局对象;

在代码中使用函数内的静态对象时,需要先判断该全局静态对象是否被创建。

Page 94: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

94飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

C++代码优化

• 栈对象与堆对象

• 栈是一片连续内存,因此一般其都已分配物理页面。

• 堆,即使紧挨着的两块内存分配,其内存地址也不是相连的,很可能导致page fault。

class Myclass;

void func(){

Myclass *pobj=new Myclass;

………

}

class Myclass;

void func() {

Myclass obj;

…….

}

Page 95: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

95飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

硬件相关的优化

• 想了解指令是如何在系统内执行的,那么 好的文档就是其体系结构。

– ARM11微结构的流水线与以前的ARM核不同,它包含8级流水,使贯通率比以前的核提高40%。

– 单指令发射 : ARM11微结构的流水线是标量的(SCALAR),即每次只发射一条指令(单发射)。有些流水线结构可以同时发射多条指令,例如,可以同时向ALU和MAC流水线发射指令。理论上,多发射微结构会有更高的效能,但实践上,多发射微结构无疑会增加前段指令译码级的复杂程度,因为需要更多的逻辑来处理指令相关(DEPENDENCY),这将使处理器的面积和功耗变得更大。

– 分支预测:ARM11微结构使用两种技术来预测分支。首先,动态的预测器使用历史记录来判断分支是 频繁发生,还是 不频繁发生。如果动态的分支预测器没有发现记录,就使用静态的分支算法。很简单,静态预测检查分支是向前跳转还是向后跳转。假如是向后跳转,就假定它是一个循环,预测该分支发生,假如是向前跳转,就预测该分支不发生。通过使用动态和静态的分支预测,ARM11微结构中分支指令中的85%被正确预测。

Page 96: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

96飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

硬件相关的优化

• ARM V6体系结构性能:

– 并行流水线:尽管流水线是单发射的,在流水线的后端还是使用了三个并行部件结构,ALU,MAC (乘加),LS(存取)。

– 64位数据路径:ARM11 微结构在局部合理使用64位结构,通过32位的成本来实现64位的性能。ARM11微结构在处理器整数部件与缓存之间,整数部件与协处理器之间使用了64位数据总线。64位的路径可以在一个周期内从缓存中读取两条指令,允许每周期传送两个ARM寄存器的数据。这使得许多数据移动操作与数据加工操作变得更为高性能。

– 浮点处理:使用了浮点寄存器

Page 97: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

97飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

硬件相关的优化

• 流水线

Page 98: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

98飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

硬件相关的优化

• 流水线的优化

– 减少分支和跳转指令

– 消除数据的相关性

– 内存与计算相结合

1 a=a+1;

2 c=c+1;

3 b=a+d;

1 a=a+1;

2 b=a+d;

3 c=c+1;

1 a1=A[1];

2 c++;

3 d++;

4 a2=A[2];

a1=A[1];

a2=A[2];

c++;

d++;

Page 99: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

99飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

硬件相关的优化

• 内存访问:

– 目前嵌入式设备所使用的内存一般为SDRAM。

Page 100: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

100飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

硬件相关的优化

• 内存访问流程

– 1、CPU试图访问一块内存;

– 2、CPU首先该内存是否已经被加载到Cache中。

– 3、如果加载到cache中,则直接在cache中定位。

– 4、如果未加载到cache中,则通过CPU和内存之间的地址总线,向内存发送地址的高27位地址。

– 5、当内存收到高27位地址后,利用SDRAM的突发交换模式(burst-exchange mode),将连续的32个字节,传送给CPU上的Cache,填充一个缓存行。

– 6、CPU可以通过地址的高27位来定位Cache的缓存行,利用地址的低5位定位到缓存行中具体的字节。

Page 101: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

101飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

硬件相关的优化

• 内存操作的优化

– 尽量使用占内存少的算法

– 利用流水线内存存取与计算并行的特点,组合内存访问与计算。

for(int i=0;i<1024;i++)

{

c=p[i];

a+=1;

b+=i;

}

for(int i=0;i<1024;i++)

{

a+=1;

b+=i;

c=p[i];

}

Page 102: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

102飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

硬件相关的优化

• Cache的优化

– 提高运行指令和数据的局部性。

– 循环的变换

for(i=0;i<1024;i++)

{

for(j=0;j<128;j++)

{

ret += b[i][j];

}

}

for(i=0;i<128;i++)

{

for(j=0;j<1024;j++)

{

ret += b[j][i];

}

}

Page 103: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

103飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

硬件相关的优化• Cache的优化

– 优化数据结构PhoneBook * FindName(charLast[],PhoneBook * pHead)

{

while(pHead != NULL)

{

if(stricmp(Last,pHead->LastName)==0)

return pHead;

pHead = pHead->pNext;

}

return NULL;

}

typedef struct _PHONE_BOOK_ENTRY {

char LastName[16];

char FirstName[16];

char email[16];

char phone[10];

char cell[10];

char addrl[16];

char addr2[16];

char stat[2];

char zip[5];

_PHONE_BOOK_ENTRY *pNext;

} PhoneBook;

Page 104: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

104飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

硬件相关的优化

• SIMD指令

– ARM V6支持简单的SIMD指令。

– ARM V7提供了64/128位单指令多数据流(SIMD)指令集,用于

新一代媒体和信号处理应用加速。

– 指令集

Page 105: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

105飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

硬件相关的优化

• SIMD指令使用:

– 使用汇编语言,我们可以使用内联汇编的方式,在C语言中嵌入汇编指令。

– 使用GCC编译器对于某些特殊指令提供了两种方法进行支持:Intrinsics和built-in函数。

intrinsic、built-in在C/C++程序中的语法是以函数形式出现, 编译时可以直接翻译为一条SIMD指令(复合情况会生成 直接的几条),换言之,如果不使用intrinsic,可能需要多条C/C++语句完成,而编译器却并不能保证将这几条语句能够生成这条

高效的SIMD指令。并不是每条SIMD指令都有对等的intrinsic,这需要查看GCC相应的手册。

Page 106: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

106飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

硬件相关的优化

• 内联汇编的一个例子:

static int addsub(int x,int y){

__asm__(

"qaddsubx %0 , %1 , %0;": "=r" (x): "r"(x),"r"(y)

) ;return x;

}

Page 107: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

107飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

系统级优化

• 调整进程的优先级

– Linux支持两种进程:实时进程和普通进程

– 实时进程的优先级是静态设定的,而且始终大于普通进程的优先级。对于实时进程来讲,其使用绝对优先级的概念,绝对优先级取值范围是0~99,数字越大,优先级别越高。

– 普通进程的绝对优先级取值是0 。在普通进程之间,其又具备静态优先级和动态优先级之分。静态优先级,我们可以通过程序来进行修改。同时系统在运行过程中,会在静态优先级基础上,不断的动态计算出每个进程的动态优先级,拥有 高动态优先级的进程被调度器选中。一般来讲,静态优先级越高,进程所能分配的时间片越长 。

Page 108: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

108飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

系统性能优化

• 实时进程设置

#include <sched.h>int sched_setscheduler (pid t pid, int policy, const struct sched param *param);

这个函数可以同时设置一个进程的静态优先级和调度策略。pid用来指名所要设置的进程号,如果pid为0,则表示为当前进程;policy设置进程的调度策略;param用来设置进程的绝对优先级。

参数policy 取值范围:

SCHE_OTHER 普通进程

SCHE_FIFO 实时进程,采用先进先出策略。

SCHE_RR 实时进程,采用轮转策略

参数 paramstruct sched_param {

int sched_priority;};sched_priority为进程的绝对优先级,其取值范围0~99。返回值:如果设置成功,返回值为0;如果失败,返回指为-1。

Page 109: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

109飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

系统性能优化

• 普通进程优先级调整

#include <sys/resource.h>int setpriority (int class, int id, int niceval);class的取值为:

PRIO_PROCESS id为进程的pid。PRIO_PGRP id为进程所在组的组ID。

PRIO_USER id为用户IDniceval 为进程的nice值,其取值范围为-20~19。返回值 成功返回0;失败返回-1。

另外一个常用的函数为int nice (int increment);increment为所设置进程nice值的增量。

Page 110: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

110飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

系统性能优化

• 让进程慢下来

– 对于某些进程,我们对其没有时限要求,只需要它在背后默默做事,尽量不影响其他进程运行的进程,它不着急完成任务,但要求其使用尽可能少的CPU。

– 可以考虑降低进程的优先级

– 在计算密集型区域,增加调度函数。sleep(0)、sched_yield()。

for(i=0;i<1000000;i++){

j+=i;sleep(0);

}

Page 111: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

111飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

系统性能优化

• 设备启动速度

– 一个不错的网址:http://tree.celinuxforum.org/CelfPubWiki/BootupTimeResources

– 尽量不要把某些进程放到启动脚本中,尝试daemon进程在第一次使用时启动。

Page 112: Linux 性能优化 - pudn.comread.pudn.com/downloads155/ebook/690598/inbuilt_Linux.pdf优化的原则 • 等效原则:优化前后程序实现的功能一 致。 • 有效原则:优化后要比优化前运行速度快

112飞翔 Email:[email protected] blog:http://blog.chinaunix.net/u/30686/

系统性能优化

• 系统的耗电量

– 对于软件来讲,CPU的主频越低,耗电量也越

低。

– 调整CPU主频的两个方法:

• 使用接口函数

• Daemon进程根据CPU的负载,自动调整CPU的频

率。