本文译自Dr. Dobb’s Blogger的Walter Bright写的《Overlooked
Essentials For Optimizing Code》
我编写程序至今有35年了,我做了很多关于程序执行速度方面优化的工作(一个示例),我也看过其它人做的优化。我发现有两个最基本的优化技术总是被人所忽略。
注意,这两个技术并不是避免时机不成熟的优化,并不是把冒泡排序变成快速排序(算法优化),也不是语言或是编译器的优化,也不是把 i*4写成i<<2 的优化。 这两个技术是:
-
使用 一个profiler。
-
查看程序执行时的汇编码。
使用这两个技术的人将会成功地写出运行快的代码,不会使用这两个技术的人则不行。下面让我为你细细道来。
使用一个 Profiler
我们知道,程序运行时的90%的时间是用在了10%的代码上。我发现这并不准确。一次又一次地,我发现,几乎所有的程序会在1%的代码上花了99%的运行时间。但是,是哪个1%?一个好的Profiler可以告诉你这个答案。就算我们需要使用100个小时在这1%的代码上进行优化,也比使用100个小时在其它99%的代码上优化产生的效益要高得多得多。问题是什么?人们不用profiler?不是。我工作过的一个地方使用了一个华丽而奢侈的Profiler,但是自从购买这个Profiler后,它的包装3年来还是那么的暂新。为什么人们不用?我真的不知道。有一次,我和我的同事去了一个负载过大的交易所,我同事坚持说他知道哪里是瓶颈,毕竟,他是一个很有经验的专家。最终,我把我的Profiler在他的项目上运行了一下,我们发现那个瓶颈完全在一个意想不到的地方。
就像是赛车一样。团队是赢在传感器和日志上,这些东西提供了所有的一切。你可以调整一下赛车手的裤子以让其在比赛过程中更舒服,但是这不会让你赢得比赛,也不会让你更有竞争力。如果你不知道你的速度上不去是因为引擎、排气装置、空体动力学、轮胎气压,或是赛车手,那么你将无法获胜。编程为什么会不同呢?只要没有测量,你就永远无法进步。 这个世界上有太多可以使用的Profiler了。随便找一个你就可以看到你的函数的调用层次,调用的次数,以前每条代码的时间分解表(甚至可以到汇编级)。我看过太多的程序员回避使用Profiler,而是把时间花在那些无用的,错误的方向上的“优化”,而被其竞争对手所羞辱。(译者陈皓注:使用Profiler时,重点需要关注:1)花时间多的函数以优化其算法,2)调用次数巨多的函数——如果一个函数每秒被调用300K次,你只需要优化出0.001毫秒,那也是相当大的优化。这就是作者所谓的1%的代码占用了99%的CPU时间)
查看汇编代码
几年前,我有一个同事,Mary Bailey,她在华盛顿大学教矫正代数(remedial algebra),有一次,她在黑板上写下:x + 3 = 5
然后问他的学生“求解x”,然后学生们不知道答案。于是她写下:__ + 3 = 5
然后,再问学生“填空”,所有的学生都可以回答了。未知数x就像是一个有魔法的字母让大家都在想“x意味着代数,而我没有学过代数,所以我就不知道这个怎么做”。
汇编程序就是编程世界的代数。如果某人问我“inline函数是否被编译器展开了?”或是问我“如果我写下i*4,编译器会把其优化为左移位操作吗?”。这个时候,我都会建议他们看看编译器的汇编码。这样的回答是不是很粗暴和无用?通常,在我这样回答了提问者后,提问都通常都会说,对不起,我不知道什么是汇编!甚至C++的专家都会这么回答。 汇编语言是最简单的编程语言了(就算是和C++相比也是这样的),如:
ADD ESI,x
就是(C风格的代码)
ESI += x;
而:
CALL foo
则是:
foo();
细节因为CPU的种类而不同,但这就是其如何工作的。有时候,我们甚至都不需要细节,只需要看看汇编码的长啥样,然后和源代码比一比,你就可以知道汇编代码很多很多了。 那么,这又如何帮助代码优化?举个例子,我几年前认识一个程序员认为他应该去发现一个新的更快的算法。他有一个benchmark来证明这个算法,并且其写了一篇非常漂亮的文章关于他的这个算法。但是,有人看了一下其原来算法以及新算法的汇编,发现了他的改进版本的算法允许其编译器把两个除法操作变成了一个。这和算法真的没有什么关系。我们知道除法操作是一个很昂贵的操作,并且在其算法中,这俩个除法操作还在一个内嵌循环中,所以,他的改进版的算法当然要快一些。但,只需要在原来的算法上做一点点小的改动——使用一个除法操作,那么其原来的算法将会和新的一样快。而他的新发现什么也不是。
下一个例子,一个D用户张贴了一个 benchmark 来显示 dmd (Digital Mars D 编译器)在整型算法上的很糟糕,而ldc (LLVM D 编译器) 就好很多了。对于这样的结果,其相当的有意见。我迅速地看了一下汇编,发现两个编译器编译出来相当的一致,并没有什么明显的东西要对2:1这么大的不同而负责。但是我们看到有一个对long型整数的除法,这个除法调用了运行库。而这个库成为消耗时间的杀手,其它所有的加减法都没有速度上的影响。出乎意料地,benchmark 和算法代码生成一点关系也没有,完全就是long型整数的除法的问题。这暴露了在dmd的运行库中的long型除法的实现很差。修正后就可以提高速度。所以,这和编译器没有什么关系,但是如果不看汇编,你将无法发现这一切。
查看汇编代码经常会给你一些意想不到的东西让你知道为什么程序的性能是那样。一些意想不到的函数调用,预料不到的自傲,以及不应该存在的东西,等等其实所有的一切。但也不需要成为一个汇编代码的黑客才能干的事。
结论
如果你觉得需要程序有更好的执行速度,那么,最基本的方法就是使用一个profiler和愿意去查看一下其汇编代码以找到程序的瓶颈。只有找到了程序的瓶颈,此时才是真正在思考如何去改进的时候,比如思考一个更好的算法,使用更快的语言优化,等等。 常规的做法是制胜法宝是挑选一个最佳的算法而不是进行微优化。虽然这种做法是无可异议的,但是有两件事情是学校没有教给你而需要你重点注意的。第一个也是最重要的,如果你优化的算法没有参与到你程序性能中的算法,那么你优化他只是在浪费时间和精力,并且还转移了你的注意力让你错过了应该要去优化的部分。第二点,算法的性能总和处理的数据密切相关的,就算是冒泡排序有那么多的笑柄,但是如果其处理的数据基本是排好序的,只有其中几个数据是未排序的,那么冒泡排序也是所有排序算法里性能最好的。所以,担心没有使用好的算法而不去测量,只会浪费时间,无论是你的还是计算机的。
就好像赛车零件的订购速底是不会让你更靠进冠军(就算是你正确安装零件也不会),没有Profiler,你不会知道问题在哪里,不去看汇编,你可能知道问题所在,但你往往不知道为什么。 (全文完)
分享到:
相关推荐
概要:这篇文章介绍了Visual C++.net 2003中的代码优化。另外,有些读者可能对VC.NET 2002的优化不太了解,所以我们会简短介绍一下全程优化(Whole Program Optimization)。最后我们用一些例子充分表现一下VC.NET的...
内容概要:通过带着读者手写离散优化算法,了解 MATLAB 中离散优化的核心原理。在手写离散优化算法的过程中,将摘取整体框架中的核心逻辑,简化代码实现过程,保留核心功能,例如:线性规划、整数规划、多目标规划等...
通过使用贝叶斯优化算法,本代码可以高效地调整LightGBM模型的超参数,以达到优化模型性能的目的。同时,代码中还集成了k折交叉验证机制,以更准确地评估模型性能,并减少过拟合的风险。 适用人群 机器学习爱好者...
[Apress] 软件开发 降魔录 (遗留代码调试优化手册) (英文版) [Apress] Software Exorcism (E-Book) ☆ 图书概要:☆ Software Exorcism: A Handbook for Debugging and Optimizing Legacy Code takes an ...
旅游管理系统概要设计 旅游管理系统概要设计全文共14页,当前为第1页。旅游管理系统概要设计全文共14页,当前为第1页。旅游管理系统概要设计 旅游管理系统概要设计全文共14...不论花多少时问和精力来编制和优化代码,应
最佳实践: 作者分享了一些在重构过程中的最佳实践和经验,帮助读者更加高效地进行代码转换和优化。 代码质量和性能: 书中还涵盖了如何通过 Kotlin 的特性来提高代码质量和性能,包括空安全性、扩展函数、Lambda ...
然后重点概述了数据库性能优化的常见方法,如索引优化、查询优化、分区表、缓存等,并给出了示例代码。最后通过一个图书管理系统的数据库设计案例,讲解了具体的数据库表设计和查询方法。内容全面,条理清晰。 适合人群...
本课件先从性能优化的角度,详细解释了索引优化、查询优化、缓存利用等技术手段,给出了示例代码。然后介绍了数据库扩展的垂直和水平两种方式。课件还以高可用性和负载均衡作为案例,讲解了主从复制、读写分离等数据库...
内容概要:通过带着读者手写简化版 Matlab 方程求解类型源代码,了解 Matlab 核心原理。在手写 Matlab 源码的过程中会摘取整体框架中的核心逻辑,简化代码实现过程,保留核心功能,例如:数值计算、线性代数、优化...
在此基础上,讨论了三角函数计算的优化策略,如利用数学库的性能优势、使用泰勒级数近似等方法,分析了优化策略的原理、实现代码和效果。内容由浅入深,既包含基础知识,又引导思考优化方法。 适合人群: 本文适合学习...
在MATLAB环境中运行示例代码,观察PSO优化结果和自抗扰控制效果; 根据实际问题修改和扩展示例代码,尝试不同的参数设置和优化目标; 学习其他优化算法和控制策略,拓宽对自抗扰控制领域的认识和应用; 阅读相关领域...
在开发过程中,平台严格遵循软件工程的思想,从系统需求分析、概要设计、详细设计到具体的编码实现和后期的代码优化、功能测试,都经过了精心的规划和实施。源代码是平台开发的核心,它采用了ASP.NET技术,确保了...
近年来,网络技术的不断发展,为远程监控技术的发展创造了条件。远程监控系统软件越来越受到人们...本系统从系统需求分析、概要设计、详细设计到具体的编码实现和后期的代码优化、功能测试都严格遵循了软件工程的思想。
内容概要:通过手写简化版 Matlab 数学规划源代码,了解 Matlab 数学规划的核心原理。在手写源码的过程中会摘取整体框架中的核心逻辑,简化代码实现过程,保留核心功能,例如:线性规划、非线性规划、整数规划等内容...
内容概要:通过手写简化版 Matlab 微积分和微分方程源代码,深入了解其核心原理。在手写 Matlab 微积分和微分方程源代码的过程中,摘取整体框架中的核心逻辑,简化代码实现过程,保留核心功能,例如:数值积分、微分...
**内容概要:** 该资源为一个工具箱的源代码及书签文件,包含了多个知识领域、技术关键词和内容关键词相关的在线工具。通过该资源,用户可以快速定位并使用各种实用的在线工具,提高工作和学习效率。 **适用人群:*...
知识点:1、Delphi自带的压缩解压单元system.zlib.pas中核心函数的使用;...3、Base64编码的规则;4、为何要分块断点续传,并使用TFileStream文件流替代内存流TMemoryStream...6、优化上传下载的速度与并发性能的综合考虑
第二,根据业务基本要求和协议(包括L2、L3)内容特点分析数据加速过程中涉及到的核心代码,分析骨干代码主体设计思路,完成OFP、ODP、DPDK核心内容编译开发示例调试,熟悉整个代码基本原理和代码设计逻辑。...
内容概要: 本资源为【数学建模】2020年C题——睡眠分期题目的Python处理代码。...本代码仅为参赛者提供一个参考和起点,参赛者需要根据自己的理解和创新,进一步完善和优化代码,以达到更好的竞赛效果。
《数据库原理及应用技术》课程指导平台的开发 ...本课程指导平台从系统需求分析、概要设计、详细设计到具体的编码实现和后期的代码优化、功能测试都严格遵循了软件工程的思想。 网络教学;数据库;指导平台;ASP.NET