Подтвердить что ты не робот

Запуск XmlSerializer ОГРОМНАЯ потеря производительности на 64-битных системах

Я испытываю действительно сильную потерю производительности HUGE при вызове простого 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-битных системах (или с 32-битным принудительным с помощью corflags.exe), код занимает примерно ONE SECOND в первый раз (генерация класса сериализации temp и все...), затем он близок к 0.

Запуск приложения в 64-битных системах, код принимает ONE MINUTE в первый раз, затем он близок к 0.

Что может зависеть система в течение такого длительного времени, во время первого выполнения XmlSerializer, для большого класса в 64-битной системе?

В настоящий момент я не уверен, что я должен обвинять создание/удаление классов temp, инициализацию таблицы имен xml, CAS, Windows Search, AntiVirus или Santa Claus...

СПОЙЛЕРЫ

Вот мои тесты, не читайте это, если вы не хотите, чтобы меня отвлекали мои (возможные) ошибки анализа.

  • Запуск кода из отладчика Visual Studio заставляет код запускать FAST даже в 64-битных системах.
  • Добавление (полностью недокументированного) system.diagnostic-переключателя "XmlSerialization.Compile", который не позволяет системе удалять временные классы сериализации, заставляет код запускать FAST даже в 64-разрядных системах.
  • Взятие временного класса FooXmlSerializer, созданного средой выполнения, включая .cs в моем проекте и использование его вместо XmlSerializer, заставляет код запускать FAST даже в 64-битных системах.
  • Создание того же класса FooXmlSerializer с sgen.exe, включая .cs в моем проекте и использование его вместо XmlSerializer, заставляет код запускать FAST даже в 64-битных системах.
  • Создание того же класса FooXmlSerializer с sgen.exe, ссылающегося на сборку Foo.XmlSerializers.dll в моем проекте и использование его вместо XmlSerializer, заставляет код запускать SLOW даже в 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 или что-то связанное с Framework, Система просто делает другие вещи (или я что-то упускаю), например, антивирус, explorer.exe, индексирование Windows Search (уже пытался отключить их)

4b9b3361

Ответ 1

Я не знаю, связано ли это вообще, но у меня возникла проблема с XSLT и нашел те довольно интересные комментарии от Microsoft 64-бит JITter:

Корень проблемы связан с двумя вещами: во-первых, компилятор x64 JIT имеет несколько алгоритмов, которые квадратично масштабируются. К сожалению, один из них - генератор информации об отладке. Поэтому для очень больших методов он действительно выходит из-под контроля.

[...]

некоторые алгоритмы в 64-битном JIT, которые имеют полиномиальное масштабирование. Мы фактически работаем над портированием 32-битного JIT-компилятора на x64, но это не увидит свет дня, пока следующий бок-о-бок релиз среды выполнения (как в "2.0 и 4.0" бок о бок, но 3.0/3.5/3.5SP1 были "на месте" релизами). Я переключил это на "предложение", чтобы я мог привязать его к рабочему элементу JIT-пропускной способности, чтобы убедиться, что это исправлено, когда новый портированный JIT готов к отправке.

Опять же, речь идет о совершенно другой проблеме, но мне кажется, что комментарии к 64-битным JITter универсальны.

Ответ 2

UPDATE

Я смог воспроизвести это, исследование показывает, что большинство времени было потрачено на JIT-компилятор:

JittingStarted: "Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderFoo", "Read2_Foo", "класс экземпляра SerializersTester.Foo"

Вы можете легко доказать, что без какого-либо инструмента профилирования.

  • Сгенерировать *.XmlSerializers.dll через sgen для целей x86 и x64
  • Создание собственных изображений через ngen.

Вы можете заметить, что генерация x64 будет намного медленнее по сравнению с сборкой x86

Точная причина скрывается в x64 JIT-внутренностях (BTW полностью отличается от x86), и, к сожалению, мне не хватает свободного времени, чтобы ее найти.

Чтобы избежать такой потери производительности, вы можете генерировать сериализаторную сборку через sgen, ссылаться на нее и компилировать исходное изображение через ngen во время установки приложения на ПК конечного пользователя.

Ответ 3

Чтобы прояснить "XmlSerialization.compile", это то, что происходит:

Если мы запускаем код без файла .config на 64-битном, это замедляется.

Если мы добавим следующий раздел в файл .config для приложения

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

В результате получается следующее:

  • Файл
  • .cs, DLL и PDB для сериализатора остаются в папке temp
  • Сериализатор запускается быстро, он все же медленнее, чем на 32 бит, но окончательно приемлемый (1-2 секунды вместо 60).

Возможно создание DLL в режиме отладки (потому что есть доступные файлы PDB) изменить поведение JIT-компилятора, сделав его быстрым снова...

Ответ 4

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-компилятор имеет несколько алгоритмов, которые квадратично масштабируются.... это было то, что мы видели много раз с тех пор, как 64-битная среда была впервые выпущена в 2005 году". и

"Эта проблема а) известна и b) не совсем тривиальна для решения. Это проблема дизайна с 64-разрядным JIT. Мы находимся на ранних стадиях замены нашей 64-битной реализации JIT, так что это в конечном итоге получить адрес, но не в таймфрейме CLR 4.0, к сожалению."