2009-09-28 14 views
14

Recientemente realicé algunas pruebas de rendimiento y análisis de una aplicación ASP.NET usando estado de sesión fuera de proceso: esto es necesario cuando se usa el estado de sesión en una granja de servidores web para poder recuperar el estado cualquiera de los servidores web, por ejemplo si las solicitudes HTTP posteriores llegan a un servidor diferente porque las sesiones no son 'pegajosas' o el servidor original está inactivo, etc.Posibles soluciones a un rendimiento de serialización pobre

Lo que me sorprendió fue que cuando ejecuté los servidores web a plena carga y perfilé el uso de la CPU algo así como el 99% del tiempo de CPU se pasó serializando y deserializando el estado de la sesión. Posteriormente implementamos un servidor de estado 'caché' personalizado; esto siempre serializa el estado pero también mantiene el estado en la memoria, de modo que si usa sesiones adhesivas, el estado no tiene que deserializarse la mayor parte del tiempo. Este rendimiento mejorado del servidor por un factor de 2; Sin embargo, la serialización sigue representando el 98% o más del tiempo de CPU.

Obtuvimos algunas mejoras adicionales en la velocidad mediante el 'recorte' de referencias de objetos innecesarios entre los objetos en el estado de la sesión antes de la serialización, arreglando las referencias manualmente al finalizar la publicación. Esto mejoró la velocidad en otro 10-20% más o menos. El razonamiento aquí es que parte de la pérdida de rendimiento se debe a que la serialización integrada tiene que recorrer el gráfico de punteros de objeto, lo que se convierte en una tarea más compleja con más punteros.

Continuando con la investigación, escribimos rutinas de serialización personalizadas para algunas de nuestras clases en lugar de confiar en la serialización incorporada de .Net. Lo que encontramos fue que el rendimiento fue mejoró notablemente, por un factor de aproximadamente 50x. Parece que la mayor parte de la carga de la CPU está causada por la serialización .Net incorporada, que a su vez es lenta debido a la dependencia de usar Reflection para recorrer los punteros/gráficos del objeto y extraer datos de campo.

Es muy tentador aumentar nuestro rendimiento en 50x, reduciendo así los requisitos de hardware del servidor web por un factor grande (y los requisitos de potencia por un factor menor pero aún significativo). Las opciones actualmente son:

1) Escribir serialización personalizada. Este es un problema debido a la complejidad de la tarea y la sobrecarga de mantenimiento que genera, es decir, cualquier cambio al estado de clase requiere un cambio en las rutinas de serialización/deserialización.

2) Alguna solución de terceros. Tal vez algún producto que genere automáticamente código de guardar/cargar estado en tiempo de compilación, eliminando así la necesidad de utilizar Reflection.

Estaría muy interesado en saber si alguien sabe de una solución de un tercero, o se ha encontrado con este problema, ya que no he encontrado ninguna mención de ello en las búsquedas en Internet.

ACTUALIZACIÓN: Algunos han sugerido una especie de solución intermedia entre la serialización incorporada por defecto y las rutinas de serialización personalizadas puras. La idea es implementar la serialización personalizada para las clases que más afectan al rendimiento, p. superando ISerializable. Este es un enfoque interesante y prometedor; Sin embargo, todavía creo que hay espacio para un reemplazo completo de la serialización integrada sin tener que escribir y mantener ningún código personalizado; esto no se puede hacer en tiempo de ejecución porque Reflection es necesario para consultar objetos y acceder a datos privados. Pero teóricamente es posible posprocesar conjuntos ya construidos e inyectar nuevos métodos como un paso de construcción adicional. Algunos perfiladores utilizan este enfoque para inyectar código de creación de perfiles en ensamblados una vez que han sido compilados por el compilador de C#. También creo/leo en alguna parte que el .Net framework admite la inyección de métodos en clases, por lo que todo el juego con IL es potencialmente atendido.

+0

Bastante seguro de que los métodos de inyección de los que está hablando serían el uso de clases parciales. Creo que el principio es si dos clases en el mismo espacio de nombres tienen el mismo nombre y una está marcada como parcial. Se produce una dll que incorpora métodos y propiedades de ambas clases. – Robert

Respuesta

2

Lamentablemente, solo conozco la opción uno y la que puede comenzar a ser muy dolorosa para trabajar.

Pero solo hace lo que quiere, así que es lo más rápido posible.

Buena suerte.

+0

Estoy aceptando esto como la respuesta porque encontramos que todas las demás soluciones son al menos 10 veces más lentas que escribir código personalizado. – redcalx

1

Otra opción es deshabilitar agresivamente ViewState en todos los controles que no se tocan en las devoluciones del servidor.

+0

Gran parte del estado pertenece a nuestras propias clases, pero sí habrá estado a partir de controles estándar de ASP.NET integrados, por lo que definitivamente vale la pena vigilarlo. Gracias. – redcalx

1

Puede personalizar parcialmente su serialización implementando ISerializable. Si haces esto para los peores infractores, no aumentarás tanto el mantenimiento, pero aún obtendrás algo de aceleración.

+0

Quería agregar que en nuestras pruebas recientes descubrimos que esto no mejora el rendimiento. Parece que el golpe de rendimiento proviene del código que recorre el gráfico de objetos: ¡el manejo de datos real no es el lento! – redcalx

1

No es una solución de terceros: excelente biblioteca de código abierto

Simon de Hewitt, ver Optimizing Serialization in .NET - part 2.

Tengo using it in my application y obtuve una aceleración similar como usted, 20-40 veces.

Elimina la reflexión que es la causa de la desaceleración , pero para las listas solo admite algunos tipos nativos. Por lo tanto, para Genreric.List de sus propios tipos, debe haber código explícito en algún lugar u otro. P.ej. bucles explícitos o algo más inteligente que lo automatiza. En cualquier caso, es bastante simple y no debe ser un obstáculo para los enormes beneficios .

+0

Gracias, mucha información interesante aquí. A primera vista, parece estar posicionado a medio camino entre el uso de la serialización incorporada (lenta) y la personalizada pura. Se aprovecha de la serialización incorporada que también fue sugerida por una de las otras respuestas. – redcalx

0

Hemos encontrado problemas similares y hemos ideado varias formas de mejorar el rendimiento. Usamos algunos de estos en nuestro producto de mapeo de memoria Persistore (actualmente beta). En nuestro caso, simplemente podemos acceder a los datos persistentes "in situ" porque siempre están en un montón mapeado en la memoria.

Sin embargo, un 'truco' consiste en definir los datos de estado de sesión (si es posible) como marshalable clase/estructura y 'serialize' que el uso de soporte .NET de clasificación, esto puede ser muy rápido de hecho, pero no se encargará gráficos 'objeto'.

También admitimos una persistencia especial basada en serialización binaria, pero también extraemos y persistimos metadatos para que el código administrado pueda establecer/obtener campos dentro de un dato de memoria persistente en tiempo de ejecución sin la necesidad de deserializar el objeto completo, esto es útil en algunos entornos (por ejemplo, valores y actualizaciones de precios de acciones, etc.). Nuestra última versión beta admite serialización a través de una red, de LINQ tipos anónimos, esta es la primera vez que tengo conocimiento.

De todos modos, nos gustaría tener algunos nuevos clientes beta que están presionando ASP.NET y problemas de rendimiento web, nuestra última versión beta es muy impresionante (pero no está lista hasta la próxima semana).

Si alguien tiene curiosidad, contáctenos para obtener la información más reciente sobre el producto.

Hugh Moran

PS: Sitio Web no está actualizado, el producto va mucho más allá de lo que se describe allí.

Cuestiones relacionadas