serialize serializar net example deserialize deserializar .net 64bit xmlserializer

.net - serializar - Inicio de XmlSerializer gran pérdida de rendimiento en sistemas de 64 bits



xmlserializer c# example (4)

Estoy experimentando una gran pérdida de rendimiento al llamar a un simple XmlSerializer.Deserizlize () en una clase con muchos campos.

NOTA : Estoy escribiendo el código sin Visual Studio, en casa, por lo que puede tener algunos errores.

Mi clase serializable es plana y tiene cientos de campos:

[Serializable] class Foo { public Foo() { } [XmlElement(ElementName = "Field1")] public string Field1; // [...] 500 Fields defined in the same way [XmlElement(ElementName = "Field500")] public string Field500; }

Mi aplicación deserializa una cadena de entrada (incluso pequeña):

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

Al ejecutar la aplicación en sistemas de 32 bits (o con 32 bits forzados con corflags.exe), el código toma alrededor de UN SEGUNDO la primera vez (generación de clase de serialización temporal y todo ...), luego está cerca de 0.

Al ejecutar la aplicación en sistemas de 64 bits, el código toma UN MINUTO la primera vez, luego es cerca de 0.

¿Qué podría colgar el sistema durante tanto tiempo , durante la primera ejecución de un XmlSerializer, para una clase grande, en un sistema de 64 bits?

En este momento no estoy seguro si tengo que culpar a la generación / eliminación de clases temporales, la inicialización de la tabla de nombres xml, CAS, Windows Search, AntiVirus o Santa Claus ...

SPOILERS

Aquí están mis pruebas, no lea esto si no quiere desviarse por mis (posibles) errores de análisis.

  • Ejecutar el código desde el depurador de Visual Studio hace que el código se ejecute FAST incluso en sistemas de 64 bits
  • Al agregar el conmutador de diagnóstico del sistema (totalmente no documentado) "XmlSerialization.Compile", que evita que el sistema elimine las clases temporales de serialización, hace que el código se ejecute RÁPIDO incluso en sistemas de 64 bits
  • Tomar la clase temporal FooXmlSerializer creada por el tiempo de ejecución, incluido el .cs en mi proyecto, y usarlo en lugar del XmlSerializer, hace que el código se ejecute FAST incluso en sistemas de 64 bits
  • Crear la misma clase FooXmlSerializer con sgen.exe, incluido el .cs en mi proyecto, y usarlo en lugar del XmlSerializer, hace que el código se ejecute RÁPIDO incluso en sistemas de 64 bits
  • La creación de la misma clase FooXmlSerializer con sgen.exe, haciendo referencia al ensamblaje Foo.XmlSerializers.dll en mi proyecto y usándolo en lugar del XmlSerializer, hace que el código se ejecute LENTO incluso en sistemas de 64 bits ( esto me molesta mucho )
  • La pérdida de rendimiento solo ocurre si la entrada para deserializar realmente contiene un campo de la clase grande ( esto también me molesta mucho )

Para explicar con más detalle el último punto, si tengo una clase:

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

La deserialización es lenta solo cuando se pasa un niño Foo. Incluso si ya he realizado una deserialización:

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

EDITAR Olvidé decir que analicé la ejecución con Process Monitor y no veo que la tarea tarde mucho tiempo en mi aplicación o en csc.exe, ni nada relacionado con Framework. El sistema solo hace otras cosas (o me estoy perdiendo algo), como antivirus, explorer.exe, indexación de Windows Search (ya he intentado apagarlas)


Microsoft ha sabido de esto desde el lanzamiento de .NET de 64 bits:

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

De MSFT: "el compilador JIT x64 tiene unos pocos algoritmos que se escalan de forma cuadrada ... ha sido algo que hemos visto varias veces desde el lanzamiento del framework de 64 bits en 2005." y

"Este problema es a) conocido, yb) no es realmente trivial de tratar. Es un problema de diseño con el JIT de 64 bits. Estamos en las etapas iniciales de reemplazo de nuestra implementación de JIT de 64 bits, por lo que eventualmente obtendrá una dirección, pero no en el marco temporal de CLR 4.0, desafortunadamente ".


No sé si esto está relacionado en absoluto, pero tuve un problema con XSLT y encontré esos comentarios bastante interesantes de Microsoft sobre el JITter de 64 bits:

La raíz del problema está relacionada con dos cosas: primero, el compilador JIT x64 tiene algunos algoritmos que se escalan de forma cuadrática. Uno de ellos es el generador de información de depuración, desafortunadamente. Así que para métodos muy grandes, realmente se sale de control.

[...]

algunos algoritmos en el JIT de 64 bits que tienen escala polinomial. En realidad, estamos trabajando en portar el compilador JIT de 32 bits a x64, pero eso no verá la luz del día hasta la próxima versión lado a lado del tiempo de ejecución (como en "2.0 y 4.0 ejecutados lado a lado , pero 3.0 / 3.5 / 3.5SP1 fueron lanzamientos "en el lugar". Cambié esto a una "sugerencia" para poder mantenerlo adjunto al elemento de trabajo JIT-throughput para asegurarme de que esto se solucione cuando el nuevo El JIT portado está listo para ser enviado.

Nuevamente, se trata de un tema completamente diferente, pero me parece que los comentarios de JITter de 64 bits son universales.


Para aclarar la "XmlSerialization.compile" esto es lo que está sucediendo:

Si ejecutamos el código sin un archivo .config en 64 bits, es lento.

Si agregamos la siguiente sección al archivo .config para la aplicación

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

El resultado es el siguiente:

  • Los archivos .cs, DLL y PDB para el serializador se dejan en la carpeta temporal
  • el serializador comienza rápidamente, sigue siendo más lento que en 32 bits pero definitivamente aceptable (1-2 segundos en lugar de 60)

Tal vez la creación de la DLL en modo de depuración (porque hay archivos PDB disponibles) cambia el comportamiento del compilador JIT para que vuelva a ser rápido ...


ACTUALIZACIÓN :

Pude reproducir esto, la investigación muestra que la mayor parte del tiempo se gastó en el compilador JIT:

JittingStarted: "Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderFoo", "Read2_Foo", "clase de instancia SerializersTester.Foo"

Puede probar fácilmente que sin ninguna herramienta de perfilador.

  • Genere * .XmlSerializers.dll a través de sgen para objetivos x86 y x64
  • Generar imágenes nativas a través de ngen.

Puede notar que la generación x64 será mucho más lenta en comparación con el ensamblaje x86

La razón exacta se esconde en x64 JIT-internals (BTW es completamente diferente de x86) y desafortunadamente no tengo suficiente tiempo libre para encontrarlo.

Para evitar dicha pérdida de rendimiento, puede generar el ensamblaje del serializador a través de sgen, hacer referencia a él y compilarlo a la imagen nativa a través de ngen durante la configuración de la aplicación en la PC del usuario final.