通常会听到“高度优化的代码”或某些开发人员需要优化他们和诸如此类的东西。然而,作为一个自学成才的新程序员,我从未真正理解人们在谈论这些事情时究竟是什么意思。
关心解释一般的想法吗?此外,推荐一些阅读材料,无论你想在这件事上说什么。随意咆哮和讲道。
答案 0 :(得分:26)
优化是我们懒惰地用来表示“以某种方式让事情变得更好”的术语。我们很少“优化”某些东西 - 更多,我们只是改进它,直到它满足我们的期望。
优化是我们为优化计划的某些部分而做出的改变。 完全优化的程序通常意味着开发人员将可读性从窗口中提取出来并以非显而易见的方式重新编码算法以最小化“挂起时间”。 (并不要求“优化代码”难以阅读,这只是一种趋势。)
可以优化:
内存消耗 - 使程序或算法的运行时间更小。
CPU消耗 - 使算法在计算上不那么密集。
壁垒时间 - 尽一切努力让事情更快
可读性 - 您可以让人们更轻松地阅读它,而不是让您的应用更适合计算机。
优化代码的一些常见(和过度概括)技术包括:
更改算法以改善性能特征。如果您的算法需要O(n ^ 2)时间或空间,请尝试将该算法替换为采用O(n * log n)的算法。
要减轻内存消耗,请查看代码并查找内存浪费。例如,如果您有一个字符串密集型应用程序,则可以切换到使用子字符串引用(其中引用包含指向字符串的指针,以及用于定义其边界的索引),而不是从原始字符串分配和复制内存。
要减轻CPU消耗,请尽可能多地缓存中间结果。例如,如果您需要计算一组数据的标准偏差,请在每次需要知道std dev时保存该单个数值结果,而不是循环遍历该集合。
答案 1 :(得分:8)
我大多没有实际的建议。
先测量。应该对重要的地方进行优化。 高度优化的代码通常难以维护并且是问题的根源。在代码不会减慢执行速度的地方,我更喜欢可维护性优化。熟悉分析,包括侵入式(仪表化)和非侵入式(低开销统计)。学习阅读配置文件堆栈,了解时间包含/时间独占的时间,为什么会显示某些模式以及如何识别故障点。
您无法修复无法衡量的内容。让您的程序通过一些性能基础架构报告其执行的操作和所需的时间。我来自Win32背景,所以我习惯了性能计数器,我非常慷慨地将它们全部洒在我的代码上。我甚至automatized the code to generate them。
最后有一些关于优化的话。关于优化的大多数讨论我都看到了关注任何编译器将为您免费优化的内容。根据我的经验,“高度优化的代码”的最大收益来源完全在其他地方:内存访问。在现代架构中,CPU大部分时间处于空闲状态,等待内存被提供到其管道中。在L1和L2缓存未命中,TLB未命中,NUMA跨节点访问,甚至必须从磁盘获取页面的GPF之间,现代应用程序的内存访问模式是最单一的人们可以做出重要的优化。我稍微夸张一点,当然会有反例的工作负载,这对于这种技术不利于内存访问局部性。但大多数应用都会。具体而言,这些技术的含义很简单:将数据集中在内存中,以便单个CPU可以在包含所有需要的紧凑内存范围内工作,无需在缓存行或当前页面外部进行昂贵的内存引用。在实践中,这可能意味着像按行而不是按列访问数组一样简单。
我建议你在1995年阅读Alpha-Sort paper presented at the VLDB conference。本文介绍了专门为现代CPU架构设计的高速缓存敏感算法如何能够摆脱以前的基准测试:
我们认为现代建筑 需要算法设计师来 重新审视他们对记忆的使用 层次结构。 AlphaSort使用群集 数据结构以获得良好的缓存 局部性...
答案 2 :(得分:5)
一般的想法是,当你在编译阶段创建源代码树时,在通过解析生成代码之前,你做了一个额外的步骤(优化),根据某些启发式方法,你将分支折叠在一起,删除分支未使用或为多次使用的临时变量添加额外节点。
想想这段代码:
a=(b+c)*3-(b+c)
被翻译成
-
* +
+ 3 b c
b c
对于解析器来说,很明显,具有2个后代的+节点是相同的,因此它们将合并为临时变量t,并且树将被重写:
-
* t
t 3
现在一个更好的解析器会看到,因为t是一个整数,树可以进一步简化为:
*
t 2
并且您运行代码生成步骤的中间代码最终将是
int t=b+c;
a=t*2;
将t标记为寄存器变量,这正是为汇编编写的。
最后要注意的是:您可以优化运行时速度。您还可以优化内存消耗,这是相反的。展开循环和创建临时副本有助于加快代码的速度,他们也会使用更多的内存,所以这取决于你的目标。
答案 3 :(得分:4)
这是我最近做过的一些优化(修复一个做得不好的决定)的例子。它非常基础,但我希望它说明即使从简单的变化中也可以获得好的收益,而且“优化”不是魔术,它只是做出最好的决定来完成手头的任务。
在我正在处理的应用程序中,有几个LinkedList数据结构用于存放各种foo实例。
当应用程序正在使用时,它经常检查LinkedListed是否包含对象X.随着X的开始增长,我注意到应用程序的执行速度比应该的速度慢。
我运行了一个分析器,并意识到每个'myList.Contains(x)'调用都有O(N)因为列表必须迭代它包含的每个项目,直到它到达结尾或找到匹配。这绝对不是很有效。
那么我该怎么做才能优化这段代码呢?我将大多数LinkedList数据结构切换为HashSets,它可以在O(1)中执行'.Contains(X)'调用 - 更好。
答案 4 :(得分:3)
这是一个很好的问题。
通常最好的做法是1)只需编写代码来完成你需要做的事情,2)然后处理性能,但前提是它只是一个问题。如果程序“足够快”,那不是问题。
如果程序不够快(就像它让你等待),那么尝试一些性能调整。性能调优与编程不同。在编程中,你先思考然后再做一些事情。在性能调优中,首先思考是一个错误,因为那是猜测。
不要猜测要修复的内容;诊断程序正在做什么。 每个人都知道,但无论如何他们大部分都是这样做的。 很自然地说“问题可能是X,Y或Z”,但只有新手作用于猜测。专家说“但我可能错了”。
有多种方法可以诊断性能问题。
最简单的方法是在汇编语言级别单步执行程序,不要使用任何快捷方式。这样,如果程序正在做不必要的事情,那么你正在做同样的事情,这将变得非常明显。
另一种方法是获取分析工具,正如其他人所说,衡量,衡量和衡量。
我个人不关心测量。我认为它是一个模糊显微镜,用于精确定位性能问题。我更喜欢this method和this is an example of its use。
祝你好运。补充:我想您会发现,如果您经历过几次这样的练习,您将了解哪些编码实践往往会导致性能问题,并且您会本能地避免它们。 (这与“过早优化”略有不同,“过早优化”在开始时假定您必须关注性能。事实上,如果您还不知道,您可能会了解到对性能的过早关注可能会导致它试图避免的非常问题。)
答案 5 :(得分:2)
优化计划意味着:让它更快地运行
使程序更快的唯一方法是使更少:
进一步的规则:
此外,还有一些方法可以使程序更快出现
。答案 6 :(得分:2)
然而,作为一名自学成才的新程序员,我从未真正理解人们在谈论这些事情时究竟是什么意思。
让我与你分享一个秘密:没有人这样做。在某些方面,我们在数学上知道什么是慢的,哪些不慢。但在大多数情况下,性能太复杂,无法理解。如果你加速代码的一部分,你很可能会放慢另一部分的速度。
因此,任何告诉你一种方法比另一种方法更快的人,除非有三种方法之一是真的,否则很有可能他们只是猜测:
答案 7 :(得分:1)
优化意味着尝试改进计算机程序以实现速度等目标。问题非常广泛,因为优化可能涉及编译器改进程序以提高速度,或者人类也在做同样的事情。
答案 8 :(得分:1)
我建议你先阅读一些理论(从书本或谷歌到演讲幻灯片):
数据结构和算法 - O()符号是什么,如何计算, 可以使用哪些数据结构和算法来降低O-复杂度 书:Thomas H. Cormen,Charles E. Leiserson和Ronald L. Rivest的算法简介
编译器和汇编 - 代码如何转换为机器指令
计算机架构 - CPU,RAM,缓存,分支预测,乱序执行......如何工作
操作系统 - 内核模式,用户模式,调度进程/线程,互斥锁,信号量,消息队列
在阅读了每一篇文章后,您应该对优化的所有不同方面有基本的把握。
注意:我维护了这个,以便人们可以添加图书推荐。
答案 9 :(得分:1)
我认为优化代码是为了在更短的时间内获得相同的结果。完全优化只意味着他们没有想法让它更快。我对“完全优化”的代码提出了大量的蔑视!没有这样的事情。
所以你想让你的应用程序/程序/模块运行得更快?首先要做的事情(如前所述)是测量,也称为剖析。不要猜测在哪里优化。你不聪明,你会错的。我的猜测一直都是错误的,我一年中的大部分时间用于分析和优化。所以让计算机为你做。对于PC VTune是一个伟大的探测器。我认为VS2008有一个内置的分析器,但我还没有调查过。否则,使用性能计数器测量函数和大块代码。您将在MSDN上找到使用性能计数器的示例代码。
那你的周期在哪里?您可能正在等待来自主内存的数据。阅读L1& L2缓存。了解缓存如何工作是成功的一半。提示:使用紧密,紧凑的结构,更适合缓存行。
优化很有趣。它永远不会结束:)
关于优化的好书是写出伟大的代码:兰德尔海德理解机器。
答案 10 :(得分:0)
在开始优化之前,请确保您的应用程序生成正确的结果。