如今,优化似乎是一种迷失的艺术。所有程序员都没有从代码中挤出每一盎司的效率吗?经常在雪地里行走五英里的时候这样做?
本着回归丢失艺术的精神,您对简单(或可能是复杂)更改优化C#/ .NET代码有哪些提示?因为它是如此广泛,取决于一个人想要完成什么,它有助于提供你的提示的背景。例如:
StringBuilder
。请参阅底部的链接,了解相关信息。string.Compare
比较两个字符串,而不是像string1.ToLower() == string2.ToLower()
到目前为止,普遍的共识似乎是衡量关键。这种方式忽略了这一点:测量不会告诉你什么是错的,或者如果遇到瓶颈会怎么做。我遇到了字符串串联瓶颈,并且不知道该怎么办,所以这些提示很有用。
我甚至发布这个问题的意思是找到一个共同瓶颈的地方,以及在遇到它们之前如何避免这些瓶颈。它甚至不一定是任何人应该盲目遵循的即插即用代码,而是更多关于获得对性能应该被考虑的理解,至少在某种程度上,以及需要注意的一些常见陷阱。
我可以看到,知道为什么提示有用以及应该应用的位置可能会有用。对于StringBuilder
提示,我找到了很久以前在here on Jon Skeet's site处所做的帮助。
答案 0 :(得分:106)
如今,优化似乎是一种迷失的艺术。
每天都有一次制造显微镜作为艺术品。光学原理知之甚少。部件没有标准化。管子,齿轮和镜片必须由高技能工人手工制作。
目前,显微镜是作为工程学科生产的。物理学的基本原理得到了很好的理解,现成的部件可以广泛使用,显微镜制造工程师可以做出明智的选择,如何最好地优化他们的仪器以完成其设计的任务。
表演分析是一种“失落的艺术”,是一件非常非常好的事情。那种艺术被实践为作为一种艺术。应该优化它的优化:通过仔细应用可靠的工程原理来解决工程问题。
多年来,我被问过几十次我的“技巧和窍门”列表,人们可以使用它来优化他们的vbscript /他们的jscript /他们的活动服务器页面/他们的VB /他们的C#代码。我总是反对这一点。 强调“技巧和窍门”正是处理绩效的错误方法。这种方式导致代码难以理解,难以推理,难以维护,通常不会明显快于相应的直截了当的代码。
接近性能的正确方法是将其视为一个工程问题,就像任何其他问题一样:
这与您解决任何其他工程问题相同,例如添加功能 - 为功能设置以客户为中心的目标,跟踪实现可靠实施的进度,通过仔细的调试分析找到问题,继续迭代,直到你发货或失败。 效果是一项功能。
对复杂的现代系统进行性能分析需要遵守规则并专注于坚实的工程原理,而不是一揽子,这些技巧几乎适用于琐碎或不切实际的情况。我从来没有通过应用提示和技巧解决现实世界的性能问题。
答案 1 :(得分:45)
获得一个好的探查者。
在没有优秀的分析器的情况下,即使尝试优化C#(实际上是任何代码),也不要打扰。实际上,同时拥有一个采样和跟踪分析器实际上有很大帮助。
如果没有一个好的分析器,你可能会创建错误的优化,最重要的是,首先优化那些不是性能问题的例程。
分析的前三个步骤应该始终是1)测量,2)测量,然后3)测量....
答案 2 :(得分:21)
优化指南:
随着处理器继续加快,大多数应用程序的主要瓶颈不是CPU,而是带宽:片外存储器的带宽,磁盘带宽和带宽带宽。
从远端开始:使用YSlow查看为什么您的网站对最终用户来说速度慢,然后向后移动并修复数据库访问不太宽(列)而不是太深(行)。
在非常罕见的情况下,值得采取任何措施来优化CPU使用率,请注意不要对内存使用产生负面影响:我已经看到“优化”,开发人员试图使用内存来缓存结果以节省CPU周期。最终效果是减少可用内存以缓存页面和数据库结果,这使得应用程序运行得更慢! (参见关于测量的规则。)
我也看到过'哑'未优化算法击败'聪明'优化算法的情况。永远不要低估编译器编写者和芯片设计人员如何将“低效”循环代码转换为可以完全在片上内存中运行流水线的超高效代码。你的“聪明”的基于树的算法带有一个你认为“有效”的向后计数的内包循环,可以被打败,因为它在执行过程中无法留在片上内存中。 (参见关于测量的规则。)
答案 3 :(得分:16)
使用ORM时,请注意N + 1选择。
List<Order> _orders = _repository.GetOrders(DateTime.Now);
foreach(var order in _orders)
{
Print(order.Customer.Name);
}
如果客户没有急切加载,这可能导致数次往返数据库。
答案 4 :(得分:13)
答案 5 :(得分:9)
好的,我必须抛弃我最喜欢的:如果任务足够长时间进行人工交互,请在调试器中使用手动中断。
Vs以上。一个分析器,它为您提供了一个调用堆栈和变量值,您可以使用它们来真正了解正在发生的事情。
这样做10到20次,你就可以很好地了解优化可能会带来什么变化。
答案 6 :(得分:9)
如果您将方法识别为瓶颈,但您不知道该怎么做,那么您基本上就会陷入困境。
所以我列出一些事情。所有这些都是而不是银子弹,你仍然需要分析你的代码。我只是为你可以做的事情提出建议,有时可以提供帮助。特别是前三个很重要。
答案 7 :(得分:8)
人们对真正重要的事情有着有趣的想法。 Stack Overflow充满了一些问题,例如++i
比i++
更“高效”。 Here's an example of real performance tuning,它与任何语言的程序基本相同。如果代码只是以某种方式编写“因为它更快”,那就是猜测。
当然,你并不是故意编写愚蠢的代码,但如果猜测有效,就不需要分析器和分析技术。
答案 8 :(得分:6)
事实是,没有完美的优化代码。但是,您可以在已知的CPU类型(和计数),已知平台(Microsoft?{{3}上的已知系统(或系统集)上优化代码的特定部分 }?),已知的框架/ Mono版本,已知的CLI版本,已知的编译器版本(错误,规范更改,调整),已知的总内存和可用内存量,已知的程序集原点({{3磁盘?远程?),具有来自其他进程的已知后台系统活动。
在现实世界中,使用分析器,查看重要部分;通常明显的事情是涉及I / O的任何事情,涉及线程的任何事情(再次,这在版本之间发生巨大变化),以及涉及循环和查找的任何事情,但你可能会惊讶于“明显不好”的代码实际上不是一个问题,什么“明显好”的代码是一个巨大的罪魁祸首。
答案 9 :(得分:5)
告诉编译器要做什么,而不是如何来做。例如,foreach (var item in list)
优于for (int i = 0; i < list.Count; i++)
,m = list.Max(i => i.value);
优于list.Sort(i => i.value); m = list[list.Count - 1];
。
告诉系统你想做什么,它可以找出最好的方法。 LINQ很好,因为在您需要它之前不会计算结果。如果您只使用第一个结果,则不必计算其余结果。
最终(这适用于所有编程)最小化循环并最小化您在循环中执行的操作。更重要的是最小化循环内的循环次数。 O(n)算法和O(n ^ 2)算法之间有什么区别? O(n ^ 2)算法在循环内部有一个循环。
答案 10 :(得分:2)
我并没有真正尝试优化我的代码,但有时我会经历并使用像反射器之类的东西来将我的程序恢复到源代码。有趣的是,我将错误与反射器输出的内容进行比较。有时我发现我以更复杂的形式做的事情被简化了。可能无法优化事物,但可以帮助我看到更简单的问题解决方案。