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

Я испытываю действительно ОГРОМНОЕ снижение производительности при вызове простого XmlSerializer.Deserizlize () в классе с большим количеством полей.

ПРИМЕЧАНИЕ : I 'я пишу код дома без 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) код занимает ОДИН ВТОРОЙ в первый раз (временный класс сериализации)поколение, и все ...), затем оно близко к 0.

При запуске приложения в 64-битных системах код занимает ОДИН МИНУТ в первый раз, затем он близок к 0.

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

Прямо сейчас я не уверен, должен ли ясоздание / удаление lame временного класса, инициализация таблицы имен xml, CAS, поиск Windows, антивирус или Санта-Клаус ...

SPOILERS

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

  • Запуск кода из отладчика Visual Studio делает код быстрым даже в 64-битных системах
  • Добавление (полностью недокументированного) переключателя system.diagnostic "XmlSerialization.Compile", который не позволяет системе удалять временные классы сериализации, заставляет код работать FAST даже в 64-разрядных системах
  • Взятие временного кодаКласс FooXmlSerializer, созданный средой выполнения, включая .cs в моем проекте и использующий его вместо XmlSerializer, заставляет код запускаться FAST даже в 64-разрядных системах
  • Создание того же класса FooXmlSerializer с sgen.exe, включая.cs в моем проекте и использование его вместо XmlSerializer делает код быстрым даже в 64-битных системах
  • Создание того же класса FooXmlSerializer с помощью sgen.exe, обращение к сборке Foo.XmlSerializers.dll в моем проекте и использование его вместо XmlSerializer заставляет код работать медленно даже в 64-разрядных системах (lot )
  • Потеря производительности происходит только в том случае, если вход для десериализации фактически содержит поле большого класса (, это также вызывает у меня много проблем )

Чтобы пояснить последний пункт, если у меня есть класс:

[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

EDIT Я забыл сказать, что проанализировал выполнение с помощью Process Monitor, и не вижу ни одной задачи, отнимающей много времени умое приложение или из csc.exe, или что-нибудь, связанное с Framework.Система просто делает другие вещи (или я что-то упускаю), такие как антивирус, explorer.exe, индексация поиска Windows (уже пытались их отключить)

Ответы [ 4 ]

9 голосов
/ 10 ноября 2010

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

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

[...]

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

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

6 голосов
/ 09 ноября 2010

ОБНОВЛЕНИЕ :

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

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

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

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

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

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

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

3 голосов
/ 10 ноября 2010

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

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

Если мы добавим следующий раздел в файл .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, сделав его снова быстрым ...

0 голосов
/ 21 февраля 2013

Microsoft знает об этом с момента выпуска 64-разрядной версии .NET:

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

Из MSFT: «JIT-компилятор x64 имеет несколько алгоритмов, которые имеют квадратичное масштабирование.... это было то, что мы видели несколько раз, начиная с 64-битной среды, впервые выпущенной в 2005 году ".и

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

...