七叶笔记 » golang编程 » C/C++的性能优化几句话

C/C++的性能优化几句话

C/C++并没有随着时间的推移死掉,随着人工智能,深度学习的发展,对于编程效率和硬件性能的考量,C/C++焕发了第二春,caffe是C代码, openCV ,ffmpeg,dlib,哪怕是 tensorflow ,也离不开C/C++,因此在Python大行其道的今天,在Golang不断发展的今天,C/C++依然是一门重要的编程语言,我就在深度学习,多媒体处理中常用的性能优化说几句。

第一,很重要的性能优化原则就是不要过早优化性能,本着先功能,后性能的原则,先保证程序的功能正确,然后再着手深入优化。一定要开始考虑优化的问题,建议你先优化你的数学公式,而不是代码。

第二,对于代码优化,不要平均用力,从逻辑主线上的运行最慢的代码入手。记住两个限定条件,逻辑主线,这意味着你的代码是一定会被调用的;运行最慢,这意味着你的代码是这条主线端到端响应时间的主要贡献者,也是慢的罪魁祸首。这样才能在最短的时间把实现比较有效果的优化。

第三,不要觉得优化会花时间少过写代码的时间,往往关键的优化可能会花掉比写代码多得多的时间。有这样的觉悟,你至少不会在优化代码的时候,看着时间一点一点的流逝而焦虑。

第四,对于函数的使用,对于人来说,可以复用逻辑,可以隔离代码的复杂度,是一件好事情,但是对于计算型执行指令来说,却是不小的负担。因为当你代码执行到调用函数的时候,你需要把当前代码断点压栈保存,函数输入参数押入堆栈,跳转到函数地址去执行函数,函数执行之后,还要把结果压栈,把原来主程序地址弹出找到,跳转回去,还要把函数执行的结果也弹栈取出。特别是函数在一些循环中间被反复调用的时候,这个开销不小,因此,在这种情况下,为了性能,只能在主程序里面把函数展开成执行代码,牺牲可读性。当然,你也可以使用 inline 这样的函数限定符,但是效果有限。

第五,数据在内存中始终是一维存放的,而对于深度学习的各种矩阵,张量,都是多维数组。而且很多时候这样的数据还是巨大无比的稀疏矩阵,你需要充分考虑对于数组中每个单元的访问距离对于寻址带来的额外开销,这个开销对于1000×1000这样的数组还能无所谓,比这个再大的数组,你一定需要考虑了。顺便说一句题外话,能够用数组解决的问题,就不要动用链表,寻址的开销相差不是一点点大。

第六,计算和公式是机器学习和深度学习的一大特点,那么使用移位运算来替代乘除法是很有必要的。对于像是视频对象识别这样的应用,你甚至需要考虑把公式的计算改成事先计算好的一张大的查询表,把复杂的计算公式转换成一张表格的查询,这几乎是用空间换时间的经典应用。

第七,注意对于类的各种小的计算符号的不同,比如,a+=1要比a = a+1快。相反,如果a是基本数据类型,比如int,那么结果是反的。

第八,能够不要用模版,就不要用模版,更极端一点来说,能用C就不要用C++,因为C++为了能够适合于更庞大的工程,做了一些对于C的逻辑上的包装,这种包装纯粹是为了在庞大代码里面凸显出逻辑,对于计算机而言,就是多余,这种多余,比如STL模版的引入,至少10%的性能损耗。

第九,能够在栈上分配内存就一定不要在堆上去做,在分配内存时候的计算开销,堆上要大得多。

第十,一个初学者无意中会跳过,反而是中高级C/C++使用者会上的全套就是去把 浮点运算 改整形运算。这本来是一个计算机的常识,就是浮点运算比同等的整形运算要慢。可是,现在大部分的CPU,特别是64位的 指令集 ,浮点运算已经几乎和整形运算一样快了。所以,完全没有必要去损失精度去做这样意见看上去很高端的优化。

按照中国人习惯,十条已经够了,实际上,对于多进程, 多线程 的性能优化需要的技巧和耐心可能还可以写十条。对于高度C++的代码,在类呀,继承啊,重载啊,接口啊的优化也可以再写十条还不止。对于深度学习的代码,一般都是构建计算图,构建激活函数,计算梯度,排序,所以上面的十条能够应付大部分的情况。

最后,祝会写C/C++的同仁考虑一下Golang,我也会专门开专题聊Golang的优化。

相关文章