性能反模式
我目前为一位客户工作,由于“性能原因”,他对更改糟糕的不可测试和不可维护的代码感到害怕。 显然,存在着许多误解,而且不明白原因,只是盲目相信。
我遇到的一种这样的反模式是需要标记尽可能多的类密封内部...
*重新编辑:我看到将所有内容标记为密封内部(在 C# 中)作为过早的优化。*
我想知道人们可能知道或遇到的其他一些性能反模式是什么?
I am currently working for a client who are petrified of changing lousy un-testable and un-maintainable code because of "performance reasons". It is clear that there are many misconceptions running rife and reasons are not understood, but merely followed with blind faith.
One such anti-pattern I have come across is the need to mark as many classes as possible as sealed internal...
*RE-Edit: I see marking everything as sealed internal (in C#) as a premature optimisation.*
I am wondering what are some of the other performance anti-patterns people may be aware of or come across?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(18)
我遇到的最大的性能反模式是:
更改后。
收集性能数据将显示某种技术是否成功。 不这样做会导致相当无用的活动,因为当一切都没有改变时,有人会“感觉”性能有所提高。
The biggest performance anti-pattern I have come across is:
after the changes.
Collecting performance data will show if a certain technique was successful or not. Not doing so will result in pretty useless activities, because someone has the "feeling" of increased performance when nothing at all has changed.
房间里的大象:专注于实现级微优化,而不是更好的算法。
The elephant in the room: Focusing on implementation-level micro-optimization instead of on better algorithms.
变量重用。
我过去一直这样做,认为可以节省声明上的几个周期并减少内存占用。 与它使代码难以调试的程度相比,这些节省的价值微乎其微,特别是如果我最终移动了代码块并且关于起始值的假设发生了变化。
Variable re-use.
I used to do this all the time figuring I was saving a few cycles on the declaration and lowering memory footprint. These savings were of minuscule value compared with how unruly it made the code to debug, especially if I ended up moving a code block around and the assumptions about starting values changed.
我想到了过早的性能优化。 我倾向于不惜一切代价避免性能优化,当我决定确实需要它们时,我会将问题传递给我的同事几轮,试图确保我们将混淆......呃优化放在正确的位置。
Premature performance optimizations comes to mind. I tend to avoid performance optimizations at all costs and when I decide I do need them I pass the issue around to my collegues several rounds trying to make sure we put the obfu... eh optimization in the right place.
我遇到的一个问题是向严重损坏的代码扔硬件,试图使其足够快,这与鲁拉斯评论中提到的杰夫·阿特伍德的文章相反。 我并不是在谈论通过在更快的硬件上运行来加速使用基本正确算法的排序与使用优化算法之间的区别。 我正在讨论的是,当标准库中有 O(n log n) 算法时,使用不明显正确的自制 O(n^3) 算法。 还有一些诸如手动编码例程之类的事情,因为程序员不知道标准库中有什么。 那是非常令人沮丧的。
One that I've run into was throwing hardware at seriously broken code, in an attempt to make it fast enough, sort of the converse of Jeff Atwood's article mentioned in Rulas' comment. I'm not talking about the difference between speeding up a sort that uses a basic, correct algorithm by running it on faster hardware vs. using an optimized algorithm. I'm talking about using a not obviously correct home brewed O(n^3) algorithm when a O(n log n) algorithm is in the standard library. There's also things like hand coding routines because the programmer doesn't know what's in the standard library. That one's very frustrating.
使用设计模式只是为了让它们被使用。
Using design patterns just to have them used.
使用 #defines 而不是函数来避免函数调用的惩罚。
我见过一些代码,其中定义的扩展结果会生成巨大且非常慢的代码。 当然,调试也是不可能的。 内联函数是实现此目的的方法,但也应谨慎使用它们。
我见过代码,其中独立测试已转换为可在 switch 语句中使用的字中的位。 切换可能非常快,但是当人们将一系列独立测试转换为位掩码并开始编写一些 256 个优化的特殊情况时,他们最好有一个非常好的基准测试来证明这可以带来性能增益。 从维护的角度来看,这确实很痛苦,独立处理不同的测试会使代码变得更小,这对性能也很重要。
Using #defines instead of functions to avoid the penalty of a function call.
I've seen code where expansions of defines turned out to generate huge and really slow code. Of course it was impossible to debug as well. Inline functions is the way to do this, but they should be used with care as well.
I've seen code where independent tests has been converted into bits in a word that can be used in a switch statement. Switch can be really fast, but when people turn a series of independent tests into a bitmask and starts writing some 256 optimized special cases they'd better have a very good benchmark proving that this gives a performance gain. It's really a pain from maintenance point of view and treating the different tests independently makes the code much smaller which is also important for performance.
缺乏清晰的程序结构是最大的代码缺陷。 被认为很快的复杂逻辑几乎从来都不是。
Lack of clear program structure is the biggest code-sin of them all. Convoluted logic that is believed to be fast almost never is.
在编写代码时不要重构或优化。在完成代码之前不要尝试优化代码,这一点非常重要。
Do not refactor or optimize while writing your code. It is extremely important not to try to optimize your code before you finish it.
Julian Birch 曾经告诉我:
“是的,但是运行应用程序实际上需要多少年才能弥补开发人员花费的时间?”
他指的是节省的累计时间量在每个事务期间,通过需要一定时间来实施的优化。
来自古老圣人的明智之言......在考虑进行时髦的优化时,我经常想到这个建议。 您可以通过考虑开发人员花在处理当前状态的代码上的时间与用户节省的时间来进一步扩展相同的概念。 如果您愿意,您甚至可以按开发人员与用户的小时费率来衡量时间。
当然,有时这是无法衡量的,例如,如果电子商务应用程序需要多花 1 秒的时间来响应,您将因用户在这 1 秒内感到无聊而损失一些小%的钱。 为了弥补这一秒,您需要实现并维护优化的代码。 优化对毛利润产生正向影响,对净利润产生负向影响,因此更难平衡。 你可以尝试 - 具有良好的统计数据。
Julian Birch once told me:
"Yes but how many years of running the application does it actually take to make up for the time spent by developers doing it?"
He was referring to the cumulative amount of time saved during each transaction by an optimisation that would take a given amount of time to implement.
Wise words from the old sage... I often think of this advice when considering doing a funky optimisation. You can extend the same notion a little further by considering how much developer time is being spent dealing with the code in its present state versus how much time is saved by the users. You could even weight the time by hourly rate of the developer versus the user if you wanted.
Of course, sometimes its impossible to measure, for example, if an e-commerce application takes 1 second longer to respond you will loose some small % money from users getting bored during that 1 second. To make up that one second you need to implement and maintain optimised code. The optimisation impacts gross profit positively, and net profit negatively, so its much harder to balance. You could try - with good stats.
利用你的编程语言。使用异常处理而不是 if/else 之类的事情只是因为在 PLSnakish 1.4 中它更快。 你猜怎么了? 很可能它一点也不快,两年后维护你的代码的人会对你非常生气,因为你混淆了代码并使它运行得更慢,因为在 PLSnakish 1.8 中语言维护者修复了这个问题,现在 if/else比使用异常处理技巧快 10 倍。 使用您的编程语言和框架!
Exploiting your programming language. Things like using exception handling instead of if/else just because in PLSnakish 1.4 it's faster. Guess what? Chances are it's not faster at all and that two years from now someone maintaining your code will get really angry with you because you obfuscated the code and made it run much slower, because in PLSnakish 1.8 the language maintainers fixed the problem and now if/else is 10 times faster than using exception handling tricks. Work with your programming language and framework!
一次更改多个变量。 这让我彻底疯狂! 当不止一处发生更改时,如何确定更改对系统的影响?
与此相关的是,做出观察结果无法保证的改变。 如果进程不受 CPU 限制,为什么要添加更快/更多的 CPU?
Changing more than one variable at a time. This drives me absolutely bonkers! How can you determine the impact of a change on a system when more than one thing's been changed?
Related to this, making changes that are not warranted by observations. Why add faster/more CPUs if the process isn't CPU bound?
通用解决方案。
仅仅因为给定的模式/技术在一种情况下表现更好并不意味着它在另一种情况下也表现更好。
.Net 中过度使用 StringBuilder 就是一个常见的例子。
General solutions.
Just because a given pattern/technology performs better in one circumstance does not mean it does in another.
StringBuilder overuse in .Net is a frequent example of this one.
有一次,一位前客户打电话给我,询问我有关加快他们的应用程序速度的任何建议。
他似乎希望我说“检查 X,然后检查 Y,然后检查 Z”之类的话,换句话说,提供专家的猜测。
我回答说你必须诊断问题。 我的猜测可能比其他人的猜测更少,但它们仍然会是错误的,因此令人失望。
我认为他不明白。
Once I had a former client call me asking for any advice I had on speeding up their apps.
He seemed to expect me to say things like "check X, then check Y, then check Z", in other words, to provide expert guesses.
I replied that you have to diagnose the problem. My guesses might be wrong less often than someone else's, but they would still be wrong, and therefore disappointing.
I don't think he understood.
一些开发人员认为,有时快速但不正确的解决方案比缓慢但正确的解决方案更可取。 因此他们会忽略生产中“永远不会发生”或“无关紧要”的各种边界条件或情况。
这绝不是一个好主意。 解决方案始终需要“正确”。
您可能需要根据情况调整“正确”的定义。 重要的是,您准确地知道/定义了任何条件下您想要的结果,并且代码给出了这些结果。
Some developers believe a fast-but-incorrect solution is sometimes preferable to a slow-but-correct one. So they will ignore various boundary conditions or situations that "will never happen" or "won't matter" in production.
This is never a good idea. Solutions always need to be "correct".
You may need to adjust your definition of "correct" depending upon the situation. What is important is that you know/define exactly what you want the result to be for any condition, and that the code gives those results.
迈克尔·杰克逊 (Michael A Jackson) 给出了两条优化性能的规则:
如果人们担心性能,请告诉他们要现实 - 什么是良好的性能以及如何测试它? 那么,如果您的代码没有达到他们的标准,至少代码编写者和应用程序用户都同意这一点。
如果人们担心重写僵化代码的非性能成本(例如,时间消耗),那么请提出您的估计并证明它可以在时间表中完成。 假设可以的话。
Michael A Jackson gives two rules for optimizing performance:
If people are worried about performance, tell 'em to make it real - what is good performance and how do you test for it? Then if your code doesn't perform up to their standards, at least it's something the code writer and the application user agree on.
If people are worried about non-performance costs of rewriting ossified code (for example, the time sink) then present your estimates and demonstrate that it can be done in the schedule. Assuming it can.
我相信,“接近金属”的超精益代码比优雅的领域模型性能更高,这是一个常见的神话。
这显然已被 DirectX 的创建者/首席开发人员揭穿,他用 C# 重新编写了 C++ 版本,并进行了大量改进。 [需要来源]
I believe it is a common myth that super lean code "close to the metal" is more performant than an elegant domain model.
This was apparently de-bunked by the creator/lead developer of DirectX, who re-wrote the c++ version in C# with massive improvements. [source required]
当您提前知道数组应该有多大并且可以预先分配它时,可以使用(例如)C++ STL 中的 Push_back()、D 中的 ~= 等附加到数组。
Appending to an array using (for example) push_back() in C++ STL, ~= in D, etc. when you know how big the array is supposed to be ahead of time and can pre-allocate it.