找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 5109|回复: 2

论inline关键字与_asm内联汇编对程序速度的优化效果

[复制链接]
发表于 2015-10-28 01:39:24 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有账号?立即注册→加入我们

×
inline关键字本来是C++的,后来微软把它弄成.c后缀的文件也能用的关键字,意在提示编译器:请把这个函数内联到调用它的地方!
所谓内联函数,就是将函数本身的内容直接展开到调用它的地方,因为每次调用函数,CPU都要执行一个CALL指令来跳转到要调用的函数的入口,然后运行函数指令。内联的过程可以省略掉这个调用的过程,直接让编译器给你把要调用的函数的指令给你展开到用它的地方。看起来好处是显而易见的,然而实际情况是怎样的呢?

我们先看CPU运行一个指令需要多长时间?这要看这个指令占多少个时钟周期。时钟是什么?我们买CPU的时候,有个“主频”,比如我的一台电脑的CPU是i5的,主频是3.2 GHz,这是怎样的一个速度呢?1 GHz = 1000 MHz,1 MHz = 1000 KHz,1 KHz = 1000 Hz,而1000 Hz就是“每秒钟一千次”。
3.2 GHz = 3.2 x 1000 x 1000 x 1000 = 每秒钟3200000000个时钟周期。
x86平台PC平均每条CPU指令所需时钟数,我们就暂且算作平均每个指令2个周期。那么这相当于这个CPU每个核心每秒钟能运行1600000000个指令。

这么说是不是如果所有的函数都用内联的话,CPU是不是就可以少运行一些看起来没用的指令(比如CALL)来提高效率呢?然而别忘了CPU是从内存读取指令来运行的。内存的频率并不和CPU相同,举个例子,还是我那台电脑,它的内存是DDR3的,频率是1600 MHz。内存频率是啥?CPU每秒钟可以读写它的次数。每次读写需要至少一个内存时钟周期,而且每次读写的字节数非常有限。为了提升CPU的实际效率,现在的x86的CPU都有指令缓存,也就是CPU自己自带了一块高速内存,专门用于存储指令等数据。这块高速缓存的速度就比外设的内存要高很多,但是容量也非常有限,从一级缓存往下,容量依次从几KB到几十MB不等。

你的程序的指令第一次被运行的时候,运行速度相当于CPU读内存的速度,并不能达到CPU本身支持的最高速度。运行过一次的指令会被存储进缓存,缓存满了就会把之前存入缓存的指令删掉。随着CPU的运行,越来越多的指令就被缓存进CPU了。CPU执行缓存中的指令比执行没缓存过的指令要快很多。假设我们的程序中一个很长的函数被内联了,而这个函数被使用的频率还不低,那么我们就会编译出一个非常大的程序,这个程序被运行的时候,CPU其实并没有什么机会来重复利用缓存中的指令,运行速度并不理想。在AMD的机器上反映十分明显,我见过一个AMD的机器,CPU是4 GHz的,但是内存频率仅有333 MHz,没有被缓存的指令只有333 MHz的速度,而被缓存的却有4 GHz的速度。因此滥用inline关键字对函数进行内联并不能提升程序的运行速度。事实上inline关键字也只是个请求,请求编译器对其进行内联处理,并不是所有的编译器都会傻傻地去内联。典型的就有VS的编译器,它还有个__forceinline关键字,意思是“这个真的要内联!”(此所谓强制内联),然而该不内联的时候它也确实不给你内联。

内联这个技术也不是卵用都没有,对于面向对象的情况,经常会遇到传说中的"get"和"set",它们并不一起出现。比如下面这种。
  1. class foo
  2. {
  3. protected:
  4.     int credits;
  5. public:
  6.     foo()
  7.     {
  8.         credits = 0;
  9.     }

  10.     int recharge(int num)
  11.     {
  12.         credits += num;
  13.     }

  14.     int purchase(int num)
  15.     {
  16.         if(credits >= num)
  17.         {
  18.             credits -= num;
  19.             return num;
  20.         }
  21.         else
  22.             return 0;
  23.     }

  24.     int getcredits()
  25.     {
  26.         return credits;
  27.     }
  28. }
复制代码
这样的类,有很多成员方法(函数)的语句只有一两条。像这样的函数就有内联的价值了,因为把它们单独缓存起来并不划算。
然而就算是这样的情况,我们也可以不使用inline关键字,VS的编译器自动会将这些超简短的函数进行内联。所以inline关键字多半没什么卵用,VS编译器编译参数就有“完全不内联”、“只内联有inline关键字的”、“条件合适的都内联”这几个选项。到底该不该内联?也许编译器比你还清楚。

说完这个我们来说内联汇编。内联汇编虽然也带了“内联”两个字,但是这里说的和上面的不一样。内联汇编,是让用户自己使用_asm关键词,将汇编指令插入到C函数内。听起来很高大上,然而VS取消了x64平台对内联汇编的支持。

没错哈。内联汇编本来就是跨平台能力最弱的一种编码方式。而且论优化的话,假设你用的是VS2012,开满了优化,那么实际生成的指令的优化效果,恐怕要比程序员自己写汇编好的多得多。除非你需要使用sse,sse2,mmx等指令集,否则能不内联汇编就不要内联汇编了。

从跨平台上来说,先说x86平台,不同编译器内联汇编的语句是不一样的。VS使用的是_asm关键字,编写masm汇编语句,而gcc则使用__asm__("指令")的方式内联汇编,使用的是gas的语句(吐槽一下gas各种反人类,比如movl %eax,%ecx,你大概要醉了)。这种对平台依赖特别大的程序用C或者C++这种跨平台语言来写可真是浪费。因此还是乖乖写好C和C艹本身的语句比较好。

这里吐槽一下国产的那堆流氓软件(比如什么酷狗音乐之类的玩意儿、还有腾讯的各种游戏以及QQ等软件)的作者觉得大家的CPU其实都相当快了,因此他们对于程序优化和用户体验并不重视,于是实际用下来,你总是会吐槽“这电脑好卡……”



回复

使用道具 举报

发表于 2015-10-28 07:48:30 | 显示全部楼层
这么说来递归就没必要优化了?
回复 赞! 靠!

使用道具 举报

发表于 2018-1-14 15:27:29 | 显示全部楼层
可以可以!!
回复

使用道具 举报

本版积分规则

QQ|Archiver|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2024-11-21 20:30 , Processed in 0.035765 second(s), 24 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表