esa*_*sac 5 language-agnostic optimization performance
我想了解良好的代码优化方法和方法.
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)
您所看到的是转换为浮点数,乘以数据段中的值(可能在缓存中,也可能不在缓存中),并转换回整数.
当你想要执行像这样的微优化时,请想一想这个例子.它不仅为时过早,而且可能根本没有帮助(在这种情况下它会受到很大的伤害!)
说真的:不要这样做.我认为一个黄金法则永远不会做这样的优化,除非你像我在这里那样定期检查编译器的输出.
Kon*_*lph 17
编辑这个答案最初出现在另一个问题(已合并)中,其中OP列出了一些他认为必须有效的优化技术.所有这些都严重依赖于假设(例如x << 1总是快于x * 2).下面我试图指出这种假设的危险.
由于你的所有观点都可能是错误的,这表明存在这种过早和微不足道的优化的危险.将此类决定留给编译器,除非您非常清楚自己在做什么并且这很重要.
否则它 - 只是 - 不重要.
更重要的(并且根本不是过早)是一般程序结构的优化.例如,一遍又一遍地重新生成相同的大批量数据可能非常糟糕,因为在许多地方需要它.相反,必须将一些想法放入设计中以允许共享此数据,从而仅计算一次.
了解您正在使用的域名也非常重要.我来自生物信息学背景,并在C++中进行了大量的核心算法工作.我经常处理大量数据.目前,我正在用Java创建一个应用程序,每当我创建一个容器副本时我就会畏缩,因为我习惯于避免这样的操作.但是对于我看中的Java GUI来说,这些操作完全是微不足道的,对用户来说并不是很明显.哦,我必须克服自己.
顺便说说:
声明它们时初始化常量(如果可以)......
好吧,在许多语言(例如C++)中,常量(即标记为标识符的标识符const)必须在定义时初始化,因此您实际上没有选择.但是,遵循该规则是一个好主意,不仅适用于常数,而且适用于一般情况.不过,原因不一定是表现.它只是更清晰的代码,因为它清楚地将每个标识符绑定到一个目的而不是让它飞来飞去.
And*_*ter 14
优化俱乐部的规则:
http://xoa.petdance.com/Rules_of_Optimization_Club
规则#3是让大多数人绊倒的规则.如果您的程序等待磁盘写入或网络传输,则计算速度无关紧要.
规则#6和#7:始终进行测试.如果您正在优化,那么您就是重构,并且如果没有可靠的测试套件,您不希望重构.
记住什么东西"成本"总是好的.一些C#示例:
字符串连接总是创建一个新字符串,因为字符串是不可变的.因此,对于重复的连接,a StringBuilder更有效.
重复或大内存分配通常是您应该注意的事项.
抛出例外是非常昂贵的.这是异常仅应用于特殊情况的原因之一.
除此之外的大多数事情都是过早的优化.如果速度很重要,请使用分析器.
关于你的"优化":
我怀疑浮点运算(* 0.5)比整数除法(/ 2)更快.
如果你需要一个大小为300的数组,你应该初始化一个大小为300的数组.对于2的幂,没有什么"神奇"可以使256大小的数组更有效.
"代码中需要2次调用"是错误的.
确保您有明确定义的性能目标和测试,以衡量这些目标,以便您可以快速找出是否有问题.
从设计角度考虑性能而不是从编码角度考虑 - 优化性能低下的设计只会导致代码速度变慢
当您遇到性能问题时,请使用分析器等工具来识别问题 - 您可以猜出瓶颈在哪里并且通常猜错了.
在开发早期修复性能问题而不是将其关闭 - 随着时间的推移和功能使其成为产品修复性能问题将变得越来越困难.
| 归档时间: |
|
| 查看次数: |
3944 次 |
| 最近记录: |