Stack Overflow - 性能经验(第 2 部分)






4.60/5 (5投票s)
Stack Overflow - 性能经验(第 2 部分)
在第一部分中,我探讨了从 Stack Overflow(团队/产品)可以学到的一些更普遍的性能问题。在第二部分中,我将探讨一些关于编码性能的经验。
请不要将这些博客文章视为您应该应用到代码库中的通用技术推荐。它们是特定的优化,如果您想榨干 CPU 的最后一滴性能,可以使用它们。
另外,除非您已经测量和分析过,否则不要进行任何优化,否则您可能会优化错误的事情!
.NET 垃圾回收器之战
我第一次了解到 Stack Overflow(网站/公司)所做的性能工作,是在我阅读了他们关于与 .NET 垃圾回收器(GC)的战斗的帖子时。如果您还没读过,简而言之,他们遇到了页面加载时间突然飙升到几百毫秒的问题,而正常情况下是他们习惯的 10 毫秒以下。经过几天的调查,他们将问题归结为 GC 的行为。GC 暂停是一个真正的问题,即使 .NET 4.5 中提供的新模式也不能完全消除它们,请参阅我之前的调查以获取更多信息。
需要记住的一点是,要实现这一切,他们需要具备以下条件:
- 生产环境监控 - 这些问题只会在负载下出现,一旦应用程序运行一段时间后,它们在测试或开发环境中就很难重现。
- 多重测量 - 他们记录了 ASP.NET 和 IIS Web 服务器的响应时间,并能够交叉引用它们(参见下图)。
- 存储异常值 - 这些峰值很少发生,因此详细的指标是必需的,平均值会隐藏太多信息。
- 对 .NET GC 的良好了解 - 根据文章,他们花了 3 周时间来识别和修复这个问题“所以 Marc 和我踏上了一段为期 3 周的冒险,以解决内存压力。”
您可以阅读下面帖子中关于修复和后续工作的详细信息,但要点是他们消除了 .NET 垃圾回收器需要做的所有工作,从而消除了暂停。
Jil - 一个快速的 JSON(反)序列化器,带有许多有些疯狂的优化技巧
但如果你认为他们编写的基于 `struct` 的代码很疯狂,那么他们的 JSON 序列化库 Jil 则将事情提升到了一个新的水平。这一切都是为了追求最大化的性能,并且根据他们的基准测试,似乎是有效的!注意:`protobuf-net` 是一个二进制序列化库,但不支持 JSON,它仅作为基线包含。
例如,而不是写这样的代码
public T Serialise<T>(string json, bool isJSONP)
{
if (isJSONP)
{
// code to handle JSONP
}
else
{
// code to handle regular JSON
}
}
他们写这样的代码,这是一个经典的内存/速度权衡。
public ISerialiser GetSerialiser(bool isJSONP)
{
if (isJSONP)
return new SerialiseWithJSONP();
else
return new Serialiser();
}
public class SerialiserWithJSONP : ISerialiser
{
private T Serialiser<T>(string json)
{
// code to handle JSONP
}
}
public class Serialiser : ISerialiser
{
private T Serialise<T>(string json)
{
// code to handle regular JSON
}
}
这意味着在序列化过程中,不需要任何“功能开关”。他们只是在创建时发出不同版本的代码,并根据您指定的选项,将正确的版本交给您。当然,类(在本例中为 `SerialiserWithJSONP` 和 `Serialiser`)只会被动态创建一次,然后缓存起来供以后重用,因此动态代码生成的成本只支付一次。
通过这样做,代码可以很好地配合CPU 分支预测,因为它有一个 CPU 可以轻松处理的良好可预测的模式。它还有使方法变小的额外好处,这可能使它们成为.NET JITter 内联的候选。
有关更多优化示例,请参见下面的链接
Jil - 微小改进
在此基础上,他们测量一切以确保优化真正有效!这些测试都作为单元测试运行,可以轻松生成结果,例如,可以看看ReorderMembers。
注意:所有时间都以毫秒为单位,但测试运行了数千次,而不是每次调用。
功能名称 | Original | 改进 | Difference |
ReorderMembers |
2721 | 2712 | 9 |
SkipNumberFormatting |
166 | 163 | 3 |
UseCustomIntegerToString |
589 | 339 | 250 |
SkipDateTimeMathMethods |
108 | 100 | 8 |
UseCustomISODateFormatting |
399 | 269 | 130 |
UseFastLists |
277 | 267 | 10 |
UseFastArrays |
486 | 469 | 17 |
UseFastGuids |
744 | 304 | 440 |
AllocationlessDictionaries |
134 | 127 | 7 |
PropagateConstants |
77 | 35 | 42 |
AlwaysUseCharBufferForStrings |
63 | 56 | 7 |
UseHashWhenMatchingMembers |
141 | 131 | 10 |
DynamicDeserializer_UseFastNumberParsing |
94 | 51 | 43 |
DynamicDeserializer_UseFastIntegerConversion |
131 | 131 | 2 |
UseHashWhenMatchingEnums |
38 | 10 | 28 |
UseCustomWriteIntUnrolledSigned |
2182 | 1765 | 417 |
这与上届奥运会中英国自行车队取得巨大成功的“微小改进”方法非常相似。
当然,有体能和训练,但还有一些看似边缘的方面,比如以正确的姿势睡觉,在外出时使用相同的枕头,以及在不同的地方训练。您真的知道如何清洁双手吗?不留下指缝间的污垢?如果您能妥善处理这些事情,您就会少生一点病。“这些都是微不足道的事情,但如果您将它们结合起来,就会产生巨大的影响。”
摘要
总而言之,Stack Overflow 开发者们的代码和博客文章中有许多值得学习的地方。很高兴他们如此公开地分享了所有内容。此外,通过让一个高知名度的网站运行在 .NET 上,它也反驳了“.NET 本身很慢”的论调。
文章 Stack Overflow - 性能经验分享(第二部分) 最初出现在我的博客 性能即特性! 上。