我把for循环改为使用++i而不是i++递增,然后开始思考,这真的是必要的吗?当然,今天的编译器是自己进行这种优化的。
在本文中,http://leto.net/docs/C-optimization.php,从1997年迈克尔-李进行了其他优化,如内联,环路展开,环路干扰,环路反转,强度降低等。这些还相关吗?
我们应该做什么低级别的代码优化,我们可以安全地忽略哪些优化?
编辑:这与过早优化无关。优化的决定已经做出,现在的问题是什么是最有效的方法。
轶事:我曾经回顾过一个需求规范,它说:“程序员将离开一个移位,而不是乘以2”。
发布于 2009-04-18 17:51:32
如果优化没有成本,那么就执行它。在编写代码时,++i和i++一样容易编写,所以更喜欢前者。这是没有代价的。
另一方面,回去做这个改变需要时间,而且它很可能不会带来明显的变化,所以你可能不应该去理会它。
但是的,这会有很大的不同。对于内置类型,可能不是,但是对于复杂的类,编译器不太可能优化它。这是因为增量操作no不再是内置在编译器中的内部操作,而是类中定义的函数。编译器可以像任何其他函数一样对其进行优化,但通常不能假设可以使用前增量而不是增量后。这两种功能可能会起到完全不同的作用。
因此,在确定编译器可以进行哪些优化时,请考虑编译器是否有足够的信息来执行优化。在这种情况下,编译器不知道后置增量和预增量对对象执行相同的修改,因此不能假设其中一个可以被另一个替换。但是你有这方面的知识,所以你可以安全地进行优化。
您提到的其他许多内容通常都可以由编译器非常有效地完成:内联可以由编译器完成,而且它通常比您做得更好。它所需要知道的是,函数中有多大一部分是由头上的函数调用组成的,它被调用的频率有多大?通常被调用的大型函数可能不应该内联,因为您最终会复制大量代码,从而导致更大的可执行文件,以及更多的指令缓存错误。内联总是一种权衡,而编译器通常比您更善于权衡所有因素。
循环展开是纯粹的机械操作,编译器可以很容易地做到这一点。强度降低也是如此。内部循环和外部循环的交换更复杂,因为编译器必须证明遍历顺序的改变不会影响结果,这是很难自动完成的。因此,这里有一个优化,您应该自己做。
但是,即使在编译器能够完成的简单操作中,有时也有编译器不具备的信息。如果您知道某个函数将非常频繁地被调用,即使它只从一个地方调用,也可能值得检查编译器是否自动插入它,如果不是,则手动执行它。
有时,您可能比编译器更了解一个循环(例如,迭代的次数总是4的倍数,所以您可以安全地展开它4次)。编译器可能没有这个信息,所以如果要内联循环,就必须插入一个epilog,以确保最后几次迭代的执行是正确的。
因此,这样的“小规模”优化仍然是必要的,如果您确实需要性能,而2)您有编译器不需要的信息。
在纯机械优化方面,您不能超过编译器。但是,您可以假设编译器不能这样做,这时您可以比编译器更好地进行优化。
发布于 2009-04-18 16:46:26
这是一个老生常谈的话题,因此包含了许多好的和坏的建议。
让我告诉您,我从做性能调优的经验中发现了什么。
在性能与记忆和清晰度等其他方面之间存在权衡曲线,对吗?你会认为,为了获得更好的表现,你必须放弃一些东西,对吗?
只有当程序处于权衡曲线上时,这才是正确的。大多数软件,正如第一次写的,是数英里之外的权衡曲线。大多数时候,谈论放弃一件事而得到另一件事是不相关的和无知的。
我使用的方法不是测量,而是诊断。我不在乎不同的例程有多快,也不在乎它们被调用的频率。我想确切地知道哪些指令导致了缓慢,以及为什么。
在良好的软件工作(不是一个人的项目)中,性能不佳的主要和主要原因是奔腾的通用性。使用了太多的抽象层,每一层都提取了性能损失。这通常不是一个问题-直到它是一个问题-然后它是一个杀手。
所以我所做的就是一次解决一个问题。我称这些为“鼻涕虫”,简称为“慢虫”。我去除的每一个鼻涕虫都会产生一个从1.1倍到10倍不等的加速,这取决于它有多糟糕。每一只被移走的鼻涕虫都会占用剩余时间的更大一部分,因此它们更容易被找到。这样,所有的“低挂水果”都能被迅速处理掉。
在这一点上,我知道什么是花费时间,但修复可能更困难,例如部分重新设计软件,可能通过删除无关的数据结构或使用代码生成。如果有可能做到这一点,那就会引发新一轮的鼻涕虫清除,直到这个程序比它开始的速度快很多倍,而且更小、更清晰。
我建议您自己获得这样的经验,因为当您设计软件时,您将知道什么是不应该做的,并且您会首先做出更好(更简单)的设计。同时,你会发现自己与经验较少的同事意见不一致,他们不可能在不想出十多门课的情况下开始思考一个设计。
补充:现在,为了试图回答您的问题,当诊断表明您有一个热点时,应该进行低级别优化(即调用堆栈底部的一些代码出现在足够的呼叫堆栈样本(10%或10%以上)上,以便知道需要花费大量时间)。如果热点在代码中,你可以编辑。如果您在“新”、“删除”或“字符串比较”中有一个热点,请在堆栈上查找需要处理的内容。
希望这能有所帮助。
发布于 2009-04-18 16:05:37
这些优化仍然是相关的。对于您的示例,在内置算术类型上使用++i或i++没有任何影响。
对于用户定义的增量/递减操作符,++i更可取,因为它并不意味着复制增量对象。
因此,一种很好的编码方式是在for循环中使用前缀增量/递减。
https://stackoverflow.com/questions/763656
复制相似问题