22

多くのフィールドを持つクラスで単純な 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 ビットを強制) でアプリケーションを実行すると、コードは最初に約 1 秒かかります(一時シリアル化クラスの生成、およびすべて...)、その後は 0 に近くなります。

64 ビット システムでアプリケーションを実行すると、コードは最初は 1 分かかり、その後は 0 に近くなります。

XmlSerializer の最初の実行中に、大きなクラスの 64 ビット システムで、システムがこれほど長時間ハングする可能性があるのは何でしょうか?

現時点では、一時クラスの生成/削除、xml 名テーブルの初期化、CAS、Windows サーチ、アンチウイルス、またはサンタ クロースのせいにする必要があるかどうかはわかりません...

スポイラー

ここに私のテストがあります。私の (可能性のある) 分析ミスによって脱線したくない場合は、これを読まないでください。

  • Visual Studio デバッガーからコードを実行すると、64 ビット システムでもコードが高速に実行されます。
  • (まったく文書化されていない) system.diagnostic スイッチ "XmlSerialization.Compile" を追加すると、システムがシリアライゼーション一時クラスを削除するのを防ぐことができ、64 ビット システムでもコードが高速に実行されます。
  • ランタイムによって作成された一時的な FooXmlSerializer クラス (プロジェクトの .cs を含む) を取得し、XmlSerializer の代わりに使用すると、64 ビット システムでもコードが高速に実行されます。
  • 私のプロジェクトに.csを含め、sgen.exeで同じFooXmlSerializerクラスを作成し、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

編集プロセスモニターで実行を分析したことを忘れていましたが、アプリやcsc.exe、またはフレームワーク関連のものから長時間かかるタスクは見られません。システムは、ウイルス対策、explorer.exe、Windows Search のインデックス作成 (既にそれらをオフにしようとしています) など、他の処理を実行するだけです (または、何か不足しています)。

4

4 に答える 4

9

これが関連しているかどうかはわかりませんが、XSLT に問題があり、64 ビット JITter に関する Microsoft の興味深いコメントを見つけました。

問題の根源は 2 つのことに関連しています。まず、x64 JIT コンパイラには、二次関数的にスケーリングするいくつかのアルゴリズムがあります。残念ながら、そのうちの 1 つがデバッグ情報ジェネレーターです。したがって、非常に大きなメソッドの場合、実際には制御不能になります。

[...]

多項式スケーリングを持つ 64 ビット JIT の一部のアルゴリズム。私たちは実際に 32 ビット JIT コンパイラを x64 に移植する作業を行っていますが、ランタイムの次のサイド バイ サイド リリースまで日の目を見ることはありません(「2.0 と 4.0 をサイド バイ サイドで実行する」のように)。 、しかし 3.0/3.5/3.5SP1 は「インプレース」リリースでした。これを「提案」に切り替えたので、JIT スループット作業項目に添付したままにして、新しく移植された JIT はすぐに出荷できます。

繰り返しますが、これはまったく別の問題に関するものですが、64 ビット JITter コメントは普遍的であるように私には思えます。

于 2010-11-10T02:00:34.193 に答える
6

UPDATE:

I was able to reproduce this, investigation shows that most time was spent in JIT-compiler:

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

You can easily proof that without any profiler tool.

  • Generate *.XmlSerializers.dll via sgen for x86 and x64 targets
  • Generate native images via ngen.

You can notice x64 generation will be much more slower in compare with x86 assembly

The exact reason hides in x64 JIT-internals (BTW it completely different from x86) and unfortunately I don't have enough spare time to find it.

To avoid such performance loss you can generate serializer's assembly via sgen, reference it and compile to native image via ngen during application setup on end user PC.

于 2010-11-09T19:48:32.463 に答える
3

「XmlSerialization.compile」を明確にするために、これが起こっていることです:

64 ビットで .config ファイルなしでコードを実行すると、遅くなります。

アプリケーションの .config ファイルに次のセクションを追加すると、

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

結果は次のとおりです。

  • シリアライザー用の.cs ファイル、DLL、およびPDBファイルは一時フォルダーに残されます。
  • シリアライザーはすぐに起動します。32 ビットよりも遅いですが、完全に許容範囲内です (60 秒ではなく 1 ~ 2 秒)。

おそらくデバッグモードでDLLを作成すると(利用可能なPDBファイルがあるため)、JITコンパイラの動作が変更され、再び高速になります...

于 2010-11-10T08:32:39.687 に答える
0

Microsoft は、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 コンパイラには、2 次スケーリングするアルゴリズムがいくつかあります。... 2005 年に 64 ビット フレームワークが最初にリリースされて以来、これは何度も見られたものです。」と

「この問題は a) 既知であり、b) 対処するのはそれほど簡単ではありません。これは 64 ビット JIT の設計上の問題です。64 ビット JIT 実装を置き換える初期段階にあるため、最終的に解決される予定です。残念ながら、CLR 4.0 のタイムフレームにはありません。」

于 2013-02-21T06:39:04.860 に答える