每天学点C++知识:不要去做编译器的工作

对于C++编程的老鸟来说,有时候他们喜欢把一些东西按照编译器的工作原理进行改写,以便提高代码的运行效率。这么做确实高明,也能体现出程序员的水平,但是这么做也是有风险的。因为有时候你可能会因为一些简单的笔误,而造成非常难以察觉的错误。本文就给出了类似的例子。

这个 Bug 出现在 MySQL 源代码中。

错误代码:

说明:

这是一个在对代码段进行拷贝粘贴时出现的典型错误。程序员很可能是把“if (a[1] != b[1]) (int) a[1] – (int) b[1];” 这段代码拷贝了好几遍(然后手动改数组下标),用来实现一个循环。不过程序员忘记把其中某一行的数组下标 1 改成 5。结果就是函数有时候能返回正确的值(,有的时候则不行),这种错误是很难侦测的。事实上这个错误的确很难捕捉,在我们用 PVS-Studio 扫描 MySQL 源代码之前,所有其他的测试都没能发现这个错误。

正确的代码:

尽管之前的代码看上去整洁易读,但是程序员还是很有可能漏看这个错误。因为这个代码块的内部结构很相似,所以你本能地会一扫而过,而不会特别集中注意力去阅读代码。

之所以把代码写成这样,很可能是程序员想尽可能地优化代码。他(或她)想手动“展开循环”(来进行优化)。不过我想在这儿可不是个好主意。

首先,我很怀疑程序员是不是真的能通过这种方法达到效果。要知道,现代编译器已经相当智能了,如果真的能优化程序性能,(编译器)自动就会完成展开循环的优化。

其次,由于尝试进行优化却造成了代码中出现 bug。如果程序员一开始能老老实实写一个简单循环,那么犯错误的几率就会降低很多。

我建议把这个方法写成这样:

这种写法有两个优势:

  • 这个函数更容易阅读和理解。
  • 编写代码时,降低犯错几率。

至于性能方面,我敢说这个版本不会比之前写得很长的那个版本慢。

这个推荐的方法实际上表达了下面的意思:代码要简单易读。简单的代码通常即是正确的代码。不要去做编译器的工作——例如,(手动)展开循环。编译器很明确知道自己该做什么,并不需要你的帮助。手动代码优化工作只针对某些特定的关键代码,而且只在分析器已经确认这些代码是瓶颈以后,才可能恰当。

这个错误由 PVS-Studio 分析工具捕获。错误文本:V525代码包含一组相似的代码块。

打赏支持我翻译更多好文章,谢谢!

打赏译者

打赏支持我翻译更多好文章,谢谢!

2 6 收藏 2 评论

关于作者:黄小非

黄小非:毕业于重庆大学计算机系,南开大学软件工程硕士,SCJP。 目前在一家国企信息中心任职软件开发工程师。主要技术兴趣为Java平台相关技术、系统构架、C/C++、计算机图形学等。(新浪微博:@黄小非) 个人主页 · 我的文章 · 65 ·  

相关文章

可能感兴趣的话题



直接登录
最新评论
  • gongweixue   2016/01/17

    这个例子举的未免有些以偏概全了。
    作者所说的现代编译器有很好的优化能力,可能是指Clang,Gcc,vc++的cl等编译器。但是在很多项目中,涉及到arm、mips、sparc等架构的无法使用这些编译器,可能只允许用客户厂商提供的编译器,达不到前面所说的智能的loop unrolling处理。有些时候我们就不得不写一些像文中提到的冗长的代码来提高指令流水线的吞吐率,从而提高性能。
    我想作者想表达的是“不要去做编译器可以做的工作”。: )

    • 黄小非 程序员 2016/01/18

      我想作者要表达的意思是:除非你真的非常明确你的手动优化真的能提高程序的运行效率,否则不要轻易去做手动优化。当然不同的编译器对代码的优化都是有区别的,所以说这个问题还真的不能一概而论。不过我觉得作者推荐用静态分析工具多做分析的方法是可取的。

跳到底部
返回顶部