我最近使用进程外会话状态对ASP.NET应用程序进行了一些性能测试和分析 - 在Web场上使用会话状态时这是必要的,以便可以在任何Web服务器上检索状态,例如如果后续HTTP请求到达不同的服务器,因为会话不是“粘性”或原始服务器已关闭等等。
让我感到惊讶的是,当我满负荷运行Web服务器并分析CPU使用率时,99%的CPU时间用于序列化和反序列化会话状态。随后我们实现了一个定制的“缓存”状态服务器;这总是序列化状态,但也保持状态在内存中,这样如果你使用粘性会话,状态不必在大多数时间反序列化。这使服务器吞吐量提高了2倍;但是,序列化仍占CPU时间的98%或更多。
我们通过在序列化之前“修剪”会话状态中对象之间不必要的对象引用来获得速度的进一步改进 - 在反序列化时手动修复引用。这种速度提高了10-20%左右。这里的原因是一些性能损失是由于内置的序列化必须遍历对象指针的图形,这将成为一个更复杂的任务与更多的指针。
继续调查我们为一些类编写了自定义的序列化例程,而不是依赖.Net的内置序列化。我们发现,性能大大提高,大约 50x 。似乎CPU负载的大部分是由内置的.Net序列化引起的,而后者由于依赖于使用Reflection来遍历对象指针/图形并提取字段数据而变慢。
将我们的性能提升50倍是非常诱人的,因此大幅降低了Web服务器硬件需求(并且功耗要求较低但仍然很重要)。目前的选项是:
1)编写自定义序列化。这是一个问题,因为任务的复杂性和它产生的维护开销,也就是说,对类状态的任何更改都需要更改序列化/反序列化例程。
2)一些第三方解决方案。也许某些产品在构建时自动生成状态保存/加载代码,因此无需使用Reflection。
我很想知道是否有人知道第三方解决方案,或者遇到过这个问题,因为我没有从互联网搜索中找到任何提及。
更新: 有些人建议在默认的内置序列化和纯自定义序列化例程之间采用一种中途解决方案。我们的想法是为最能影响性能的类实现自定义序列化,例如:重写ISerializable。这是一种有趣且有前景的方法;但是我仍然认为可以完全替换内置序列化而无需编写和维护任何自定义代码 - 这在运行时无法完成,因为查询对象和访问私有数据需要Reflection。但理论上可以对已经构建的程序集进行后处理,并将新方法作为额外的构建步骤注入。一些分析器使用此方法在C#编译器构建之后将分析代码注入到程序集中。此外,我/我/我在某处读到.Net框架支持将方法注入到类中 - 因此可能需要处理所有与IL的混乱。
答案 0 :(得分:2)
不幸的是,我只知道选项一和tbh可能会开始变得非常痛苦。
但它只会发生你想要的东西,所以它会尽快得到它。
祝你好运。答案 1 :(得分:1)
另一个选择是在服务器端回发未触及的所有控件上积极禁用ViewState。
答案 2 :(得分:1)
您可以通过实施ISerializable
来部分自定义序列化。如果你为最严重的罪犯做这件事,你不会增加维护,但仍会获得一些加速。
答案 3 :(得分:1)
有第三方解决方案:
Simon Hewitt的优秀开源库,请参阅Optimizing Serialization in .NET - part 2。
我using it in my application并获得了类似的加速 就像你一样,20-40次。
它消除了引起反射的原因 减速,但对于列表,它只支持一些本机类型。 因此,对于您自己类型的Genreric.List,需要有 显式代码在某处或另一端。例如。显式循环或 更智能的东西,使自动化。无论如何它是 非常简单,不应该是巨大的障碍 好处。
答案 4 :(得分:0)
我们遇到过类似的问题,并设计了几种提高性能的方法。我们在Persistore内存映射产品中使用其中一些(目前为beta)。在我们的例子中,我们可以简单地“原位”访问持久化数据,因为它始终位于内存映射堆中。
然而,一个“技巧”是将会话状态数据(如果可能)定义为 marshalable 类/ struct和使用.NET编组支持的'serialize',这可能非常快,但是不会处理'对象'图。
我们还支持基于二进制序列化的特殊持久性,但我们还提取和持久化元数据,以便托管代码可以在运行时在内存持久化数据中设置/获取字段,而无需反序列化整个对象,这在一些设置(例如证券和股票价格更新等)。我们最新的测试版支持LINQ 匿名类型的网络序列化,据我所知,这是第一个。
无论如何,我们有兴趣让一些新的测试版客户推动ASP.NET和网络性能问题,我们最新的测试版非常令人印象深刻(但直到nex周才准备好。)
如果有人好奇,请联系我们获取产品的最新信息。
休·莫兰 PS:网站已经过时,产品远远超出了那里的描述。