XmlSerializer在64位系统上启动巨大的性能损失

时间:2010-11-09 18:53:09

标签: .net 64-bit xmlserializer

在对包含大量字段的类调用简单的XmlSerializer.Deserizlize()时,我遇到了非常巨大性能损失。

注意:我在家里没有使用Visual Studio编写代码,因此可能会出现一些错误。

我的可序列化类是扁平的,有数百个字段:

[Serializable]
class Foo
{
    public Foo() { }

    [XmlElement(ElementName = "Field1")]
    public string Field1;

    // [...] 500 Fields defined in the same way

    [XmlElement(ElementName = "Field500")]
    public string Field500;
}

我的应用程序反序列化输入字符串(甚至很小):

 StringReader sr = new StringReader(@"<Foo><Field1>foo</Field1></Foo>");
 XmlSerializer serializer = new XmlSerializer(typeof(Foo));
 object o = serializer.Deserialize(sr);

在32位系统中运行应用程序(或使用corflags.exe强制使用32位),代码首次执行 ONE SECOND (临时序列化类生成,所有...),然后它接近于0.

在64位系统中运行应用程序,代码第一次一分钟,然后接近0。

在第一次执行XmlSerializer期间,对于64位系统中的大型类,可能会将系统挂起这么长时间

现在我不确定是否必须责怪临时类生成/删除,xml名称表初始化,CAS,Windows搜索,AntiVirus或圣诞老人......

SPOILERS

以下是我的测试,如果您不想被我(可能的)analysys错误所牵制,请不要阅读此内容。

  • 从Visual Studio调试器运行代码使代码即使在64位系统中运行也很快
  • 添加(完全未记录的)system.diagnostic开关“XmlSerialization.Compile”,防止系统删除序列化临时类,使代码即使在64位系统中运行也很快
  • 获取运行时创建的临时FooXmlSerializer类,包括项目中的.cs,并使用它而不是XmlSerializer,使代码在64位系统中运行快速
  • 使用sgen.exe创建相同的FooXmlSerializer类,包括我项目中的.cs,并使用它而不是XmlSerializer,使代码即使在64位系统中运行也很快
  • 使用sgen.exe创建相同的FooXmlSerializer类,在我的项目中引用Foo.XmlSerializers.dll程序集,并使用它而不是XmlSerializer,使代码在64位系统中运行缓慢(这会让我感到烦恼很多
  • 如果反序列化的输入实际上包含一个大类的字段(这也让我很烦恼),那么性能损失只会发生。

为了进一步解释最后一点,如果我有一个班级:

[Serializable]
class Bar
{
    public Bar() { }

    [XmlElement(ElementName = "Foo")]
    public Foo Foo; // my class with 500 fields
}

只有传递Foo子项时,反序列化才会很慢。即使我已经执行了反序列化:

 StringReader sr = new StringReader(@"<Bar></Bar>");
 XmlSerializer serializer = new XmlSerializer(typeof(Bar));
 object o = serializer.Deserialize(sr); // FAST

 StringReader sr = new StringReader(@"<Bar><Foo><Field1>foo</Field1></Foo></Bar>");
 XmlSerializer serializer = new XmlSerializer(typeof(Bar));
 object o = serializer.Deserialize(sr); // SLOW

编辑我忘了说我用Process Monitor分析了执行情况,我没有看到任何需要花费很长时间从我的应用程序或csc.exe或任何与框架相关的任务。系统只做其他事情(或者我遗漏了一些东西),比如防病毒,explorer.exe,Windows搜索索引(已经尝试关闭它们)

4 个答案:

答案 0 :(得分:9)

我不知道这是否相关,但我遇到了XSLT的问题,并且微软发现those rather interesting comments有关64位JITter的信息:

  

问题的根源与两件事有关:首先,x64 JIT编译器有一些二次缩放的算法。其中一个是调试信息生成器,不幸的是。因此,对于非常大的方法,它确实失控。

     

[...]

     

64位JIT中的一些具有多项式缩放的算法。我们实际上正在努力将32位JIT编译器移植到x64,但是直到下一个并行释放运行时才能看到它的亮点(如“2.0&amp; amp; ; 4.0并排运行,但3.0 / 3.5 / 3.5SP1是'就地'版本。。我已将其转换为“建议”,因此我可以将其附加到JIT吞吐量工作项确保在新移植的JIT准备出货时修复此问题。

同样,这是一个完全不同的问题,但在我看来,64位JITter评论是通用的。

答案 1 :(得分:6)

<强>更新

我能够重现这一点,调查显示大部分时间花在了JIT编译器上:

  

JittingStarted:“Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderFoo”,“Read2_Foo”,“instance class SerializersTester.Foo”

您可以轻松证明没有任何分析器工具。

  • 通过sgen为x86和x64目标生成* .XmlSerializers.dll
  • 通过ngen生成原生图像。
  

您可以注意到与x86程序集相比,x64生成速度要慢得多

确切的原因隐藏在x64 JIT内部(BTW与x86完全不同),遗憾的是我没有足够的空余时间来找到它。

为了避免这种性能损失,您可以通过sgen生成序列化程序的程序集,引用它并在最终用户PC上的应用程序设置期间通过ngen编译为本机映像。

答案 2 :(得分:3)

澄清“XmlSerialization.compile”这就是它发生的事情:

如果我们在64位上运行没有.config文件的代码,那就慢了。

如果我们将以下部分添加到应用程序的.config文件中

<configuration>
   <system.diagnostics>
     <switches>
        <add name="XmlSerialization.Compilation" value="4"/>
     </switches>
   </system.diagnostics>
</configuration>

结果如下:

  • .cs文件,DLL和序列化程序的 PDB 文件保留在临时文件夹中
  • 序列化器开始快速,它仍然比32位慢,但最终可接受(1-2秒而不是60)

也许在调试模式下创建DLL(因为有可用的PDB文件)改变了JIT编译器的行为,使其再次快速...

答案 3 :(得分:0)

自从64位.NET发布以来,微软已经知道了这一点:

http://connect.microsoft.com/VisualStudio/feedback/details/508748/memory-consumption-alot-higher-on-x64-for-xslcompiledtransform-transform-then-on-x86

来自MSFT: “x64 JIT编译器有一些算法可以进行二次缩放....自从2005年第一次发布64位框架以来,我们已经看过很多次了。” 和

“这个问题是a)已知,并且b)不是很容易解决。这是64位JIT的设计问题。我们正处于更换64位JIT实现的早期阶段,因此它将< em>最终获取地址,但遗憾的是,不是在CLR 4.0时间范围内。“