此处的坏习惯,指的是以后在大型软件项目中写出糟糕代码的现象。毕竟OI竞赛就是高中几年(顶多加上大学ACM)的事,而开发大型项目的能力则是漫长的职业生涯非常重要的素质,所以我在此处列出几个我常见的糟糕代码例子(包括有些我自己以前也经常做的),以及不少的(对某项语言特性的)迷信。
此外,我还觉得,中国的OI竞赛因为各种非技术原因的奇葩规定(例如禁用STL,不开优化,不给用C++11),导致形成了了一些独有的坏习惯
在喷我之前,请确保你是在使用<b>现代的</b>语言(例如最新标准的C/C++,pascal就不要提了),和编译器(以及IDE,例如恐龙级的borland系列也不要提了),而且我说这些,都是面向企业大型项目的开发而言,在竞赛中也许用了会有较好的结果,但是在工作中这样写代码,在大公司早就被炒了。
另外,不看汇编输出谈优化都是耍流氓!想有效地优化得要先学编译原理和cpu架构
1. 异或交换两个数字。经典奇技淫巧。如果学过编译原理就知道这毫无意义(Copy Propagation)
2.使用register关键字。现代编译器里这个关键字直接忽略(对,完全没有作用)(C++17终于取消这个关键字了)。类似的还有对inline关键字的迷信和误用。 相反的,真正有可能产生优化效果的const &和static却不会用(static某些情况可以减少生成程序大小)
3.纠结++i,i++,i+=1的性能。为什么不反汇编一下呢?你重载++操作符另外说(而且这样也是非常糟糕的编程风格)
4.自己写基本数学函数,例如abs,max,min之类。十条汇编指令内的东西,基本上没几个人类能够写得好过编译器,所以不要浪费时间了。想做微优化,起码都得从矩阵乘法这种复杂度起步,而且凡是libc有的数学函数,都基本上优化得比你好了。用define的更加不能容忍
5.把几个有副作用的表达式放在同一语句。例如i=i+++++i之类的,不知道是不是受到谭浩强影响,这种未定义行为还有这么多人热衷。(如果你会编译原理,并且熟悉gcc的架构,你的确能够预测会发生什么,但是这样的东西你真好意思写出来?)
6.忽略I/O,内存缓存hit rate,分支预判准确率。这些通常才是真正瓶颈,特别是I/O,例如你要读1000个文件的元信息,并且按大小排序,结果你还纠结应该用哪种排序算法,这就是2B
7.自己维护指针来进行数组读取,例如int* ptr = &a[0] 然后自己*ptr++。64位汇编有专门的内存写法来处理数组,例如 (%rbp, %rax, 4)这样的,自己维护指针只会增加代码混乱度
8.认为开O3会导致不稳定。这纯属人云亦云
9.无理恐惧C++的异常,多态和模版。这里水太深,不多说。总之用现代的编译器,C++异常只要不抛出,那么就没有性能问题。
后面想到了补充
此外,我还觉得,中国的OI竞赛因为各种非技术原因的奇葩规定(例如禁用STL,不开优化,不给用C++11),导致形成了了一些独有的坏习惯
在喷我之前,请确保你是在使用<b>现代的</b>语言(例如最新标准的C/C++,pascal就不要提了),和编译器(以及IDE,例如恐龙级的borland系列也不要提了),而且我说这些,都是面向企业大型项目的开发而言,在竞赛中也许用了会有较好的结果,但是在工作中这样写代码,在大公司早就被炒了。
另外,不看汇编输出谈优化都是耍流氓!想有效地优化得要先学编译原理和cpu架构
1. 异或交换两个数字。经典奇技淫巧。如果学过编译原理就知道这毫无意义(Copy Propagation)
2.使用register关键字。现代编译器里这个关键字直接忽略(对,完全没有作用)(C++17终于取消这个关键字了)。类似的还有对inline关键字的迷信和误用。 相反的,真正有可能产生优化效果的const &和static却不会用(static某些情况可以减少生成程序大小)
3.纠结++i,i++,i+=1的性能。为什么不反汇编一下呢?你重载++操作符另外说(而且这样也是非常糟糕的编程风格)
4.自己写基本数学函数,例如abs,max,min之类。十条汇编指令内的东西,基本上没几个人类能够写得好过编译器,所以不要浪费时间了。想做微优化,起码都得从矩阵乘法这种复杂度起步,而且凡是libc有的数学函数,都基本上优化得比你好了。用define的更加不能容忍
5.把几个有副作用的表达式放在同一语句。例如i=i+++++i之类的,不知道是不是受到谭浩强影响,这种未定义行为还有这么多人热衷。(如果你会编译原理,并且熟悉gcc的架构,你的确能够预测会发生什么,但是这样的东西你真好意思写出来?)
6.忽略I/O,内存缓存hit rate,分支预判准确率。这些通常才是真正瓶颈,特别是I/O,例如你要读1000个文件的元信息,并且按大小排序,结果你还纠结应该用哪种排序算法,这就是2B
7.自己维护指针来进行数组读取,例如int* ptr = &a[0] 然后自己*ptr++。64位汇编有专门的内存写法来处理数组,例如 (%rbp, %rax, 4)这样的,自己维护指针只会增加代码混乱度
8.认为开O3会导致不稳定。这纯属人云亦云
9.无理恐惧C++的异常,多态和模版。这里水太深,不多说。总之用现代的编译器,C++异常只要不抛出,那么就没有性能问题。
后面想到了补充