65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.60/5 (5投票s)

2016年9月14日

CPOL

5分钟阅读

viewsIcon

10503

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 - 性能经验分享(第二部分) 最初出现在我的博客 性能即特性! 上。

© . All rights reserved.