什么是一些好的代码优化方法?

esa*_*sac 5 language-agnostic optimization performance

我想了解良好的代码优化方法和方法.

  1. 如果我正在考虑性能,如何避免过早优化.
  2. 如何在代码中找到瓶颈?
  3. 我如何确保随着时间的推移我的程序不会变慢?
  4. 要避免一些常见的性能错误(例如;我知道在try {} catch {}块的catch部分内部返回某些语言是不好的

Jos*_*man 40

对于您建议的各种优化,您应该编写代码以便清晰,而不是优化它们,直到您有证据证明它们是瓶颈.

尝试像这样的微优化的一个危险是你可能会让事情变得更慢,因为编译器比你在很多时候更聪明.

采取"优化":

const int windowPosX = (screenWidth * 0.5) - (windowWidth * 0.5);
Run Code Online (Sandbox Code Playgroud)

世界上没有严肃的编译器不知道除以2的最快方法是向右移动.乘以浮点数0.5实际上更昂贵,因为它需要转换为浮点数和返回值,并进行两次乘法(比移位更昂贵).

但是不要相信我的话.看看编译器实际上做了什么.gcc 4.3.3在32位Ubuntu(-O3,-msse3,-fomit-frame-pointer)上编译:

int posx(unsigned int screen_width, unsigned int window_width) {
  return (screen_width / 2) - (window_width / 2);
}
Run Code Online (Sandbox Code Playgroud)

对此:

00000040 <posx>:
  40:   8b 44 24 04             mov    eax,DWORD PTR [esp+0x4]
  44:   8b 54 24 08             mov    edx,DWORD PTR [esp+0x8]
  48:   d1 e8                   shr    eax,1
  4a:   d1 ea                   shr    edx,1
  4c:   29 d0                   sub    eax,edx
  4e:   c3    
Run Code Online (Sandbox Code Playgroud)

两个班次(使用立即操作数)和减法.非常便宜.另一方面,它汇编了这个:

int posx(unsigned int screen_width, unsigned int window_width) {
  return (screen_width * 0.5) - (window_width * 0.5);
}
Run Code Online (Sandbox Code Playgroud)

对此:

00000000 <posx>:
   0:   83 ec 04                sub    esp,0x4
   3:   31 d2                   xor    edx,edx
   5:   8b 44 24 08             mov    eax,DWORD PTR [esp+0x8]
   9:   52                      push   edx
   a:   31 d2                   xor    edx,edx
   c:   50                      push   eax
   d:   df 2c 24                fild   QWORD PTR [esp]
  10:   83 c4 08                add    esp,0x8
  13:   d8 0d 00 00 00 00       fmul   DWORD PTR ds:0x0
            15: R_386_32    .rodata.cst4
  19:   8b 44 24 0c             mov    eax,DWORD PTR [esp+0xc]
  1d:   52                      push   edx
  1e:   50                      push   eax
  1f:   df 2c 24                fild   QWORD PTR [esp]
  22:   d8 0d 04 00 00 00       fmul   DWORD PTR ds:0x4
            24: R_386_32    .rodata.cst4
  28:   de c1                   faddp  st(1),st
  2a:   db 4c 24 08             fisttp DWORD PTR [esp+0x8]
  2e:   8b 44 24 08             mov    eax,DWORD PTR [esp+0x8]
  32:   83 c4 0c                add    esp,0xc
  35:   c3                      ret
Run Code Online (Sandbox Code Playgroud)

您所看到的是转换为浮点数,乘以数据段中的值(可能在缓存中,也可能不在缓存中),并转换回整数.

当你想要执行像这样的微优化时,请想一想这个例子.它不仅为时过早,而且可能根本没有帮助(在这种情况下它会受到很大的伤害!)

说真的:不要这样做.我认为一个黄金法则永远不会做这样的优化,除非你像我在这里那样定期检查编译器的输出.

  • 非常感谢你做的这些.我不知道为什么我从未想过要拆卸自己.真正开眼界.它还强调你不应该相信你从其他程序员那里听到的一切(这是我'学习''0.5'而不是'*2'的地方).谢谢冠军 (3认同)

小智 27

  1. 不要考虑性能,考虑清晰度和正确性.
  2. 使用分析器.
  3. 继续使用分析器.
  4. 见1.


Kon*_*lph 17

编辑这个答案最初出现在另一个问题(已合并)中,其中OP列出了一些他认为必须有效的优化技术.所有这些都严重依赖于假设(例如x << 1总是快于x * 2).下面我试图指出这种假设的危险.


由于你的所有观点都可能是错误的,这表明存在这种过早和微不足道的优化的危险.将此类决定留给编译器,除非您非常清楚自己在做什么并且这很重要.

否则它 - 只是 - 不重要.

更重要的(并且根本不是过早)是一般程序结构的优化.例如,一遍又一遍地重新生成相同的大批量数据可能非常糟糕,因为在许多地方需要它.相反,必须将一些想法放入设计中以允许共享此数据,从而仅计算一次.

了解您正在使用的域名也非常重要.我来自生物信息学背景,并在C++中进行了大量的核心算法工作.我经常处理大量数据.目前,我正在用Java创建一个应用程序,每当我创建一个容器副本时我就会畏缩,因为我习惯于避免这样的操作.但是对于我看中的Java GUI来说,这些操作完全是微不足道的,对用户来说并不是很明显.哦,我必须克服自己.

顺便说说:

声明它们时初始化常量(如果可以)......

好吧,在许多语言(例如C++)中,常量(即标记为标识符的标识符const)必须在定义时初始化,因此您实际上没有选择.但是,遵循该规则一个好主意,不仅适用于常数,而且适用于一般情况.不过,原因不一定是表现.它只是更清晰的代码,因为它清楚地将每个标识符绑定到一个目的而不是让它飞来飞去.

  • 同意海报优化技巧不起作用. (3认同)

And*_*ter 14

优化俱乐部的规则:

  1. 优化俱乐部的第一条规则是,您不优化.
  2. 优化俱乐部的第二条规则是,如果不进行测量,则不进行优化.
  3. 如果您的应用程序运行速度超过底层传输协议,则优化结束.
  4. 一次一个因素.
  5. 没有marketroids,没有marketroid时间表.
  6. 只要必要,测试就会继续进行.
  7. 如果这是您在优化俱乐部的第一个晚上,您必须编写测试用例.

http://xoa.petdance.com/Rules_of_Optimization_Club

规则#3是让大多数人绊倒的规则.如果您的程序等待磁盘写入或网络传输,则计算速度无关紧要.

规则#6和#7:始终进行测试.如果您正在优化,那么您就是重构,并且如果没有可靠的测试套件,您不希望重构.


dtb*_*dtb 8

记住什么东西"成本"总是好的.一些C#示例:

  • 字符串连接总是创建一个新字符串,因为字符串是不可变的.因此,对于重复的连接,a StringBuilder更有效.

  • 重复或大内存分配通常是您应该注意的事项.

  • 抛出例外是非常昂贵的.这是异常仅应用于特殊情况的原因之一.

除此之外的大多数事情都是过早的优化.如果速度很重要,请使用分析器.


关于你的"优化":

  • 我怀疑浮点运算(* 0.5)比整数除法(/ 2)更快.

  • 如果你需要一个大小为300的数组,你应该初始化一个大小为300的数组.对于2的幂,没有什么"神奇"可以使256大小的数组更有效.

  • "代码中需要2次调用"是错误的.

  • 在Core2上,浮点乘法(fmul,5个周期延迟)实际上比整数除法明显便宜(idiv,18-42 cyles延迟,取决于数字的大小).有关详细信息,请参阅Agner Fog的说明表.但这在这里并不重要,因为这个例子实际上不会编译成idiv. (3认同)
  • @Josh:`fmul`可能比`idiv`更快 - 但是来自'double`的转换怎么样? (3认同)

Mic*_*ael 5

确保您有明确定义的性能目标和测试,以衡量这些目标,以便您可以快速找出是否有问题.

从设计角度考虑性能而不是从编码角度考虑 - 优化性能低下的设计只会导致代码速度变慢

当您遇到性能问题时,请使用分析器等工具来识别问题 - 您可以猜出瓶颈在哪里并且通常猜错了.

在开发早期修复性能问题而不是将其关闭 - 随着时间的推移和功能使其成为产品修复性能问题将变得越来越困难.