2012-04-18 16 views
6

Estoy a punto de escribir un conjunto de métodos en una aplicación de servidor que toma los mensajes recibidos del socket TCP, los codifica/decodifica, y los encripta/descifra.System.Reflection vs Generics - performance

mensajes Teniendo en cuenta se definen como tipos especiales, cada uno con su propio conjunto de propiedades y longitud total, tengo que optar por una de las siguientes soluciones: 1. métodos hacen que utilizan los genéricos, tales como Encode<T>(...) where T : IMessage y luego implementar codificador/decodificador para cada tipo de mensaje y tiene ResolveEncoder<T> que elegiría el codificador para el mensaje deseado o 2. Realice un método que utilice cualquier tipo de mensaje, siempre que implemente IMessage, como Encode(IMessage message), y use System.Reflection para determinar todo Necesito saber sobre eso.

Estoy más interesado en hacer la solución n. ° 2, ya que hace que mi código sea 30 veces más pequeño. Sin embargo, me preocupa si el reflejo constante de las propiedades afectará el rendimiento. Ahora que tengo un tiempo limitado para terminar el proyecto, realmente no puedo "experimentar".

Agradecería cualquier experiencia personal o vínculos con los puntos de referencia sobre cómo el rendimiento cae con cualquiera de las soluciones.

+1

La reflexión definitivamente disminuirá el rendimiento. Publique su código genérico para ver si tal vez se puede reducir de una manera que no haya pensado, que será más tolerable. – SimpleVar

+2

No optimice prematuramente. Sí, la reflexión es más lenta que el código nativo, pero si no es el cuello de botella REAL, entonces es irrelevante. Es posible utilizar Reflection with Generics en algunos escenarios para almacenar en caché los métodos reflejados en la primera ejecución para mejorar el rendimiento. – cfeduke

Respuesta

4

La reflexión puede ser lo suficientemente rápida. Pero necesitan ser implementados correctamente.

Rendimiento Reflexión -

Funciones rápido y ligero

typeof 
Object.GetType 
typeof == Object.GetType 
Type equivalence APIs (including typehandle operator overloads) 
get_Module 
get_MemberType 
Some of the IsXX predicate APIs 
New token/handle resolution APIs in the .NET Framework 2.0 

Funciones costosos

GetXX APIs (MethodInfo, PropertyInfo, FieldInfo, and so on) 
GetCustomAttributes 
Type.InvokeMember 
Invoke APIs (MethodInfo.Invoke, FieldInfo.GetValue, and so on) 
get_Name (Name property) 
Activator.CreateInstance 

Source - Dodge Common Performance Pitfalls to Craft Speedy Applications

MethodInfo puede acelerar - Improving performance reflection , what alternatives should I consider

buenos enlaces:
How costly is .NET reflection?
http://www.codeproject.com/Articles/18450/HyperDescriptor-Accelerated-dynamic-property-acces

+0

Enlaces muy útiles, ¡gracias! –

0

Los genéricos están limitados en tiempo de compilación y serán tan eficaces como los tipos normales. La reflexión tendrá un costo enorme.

+2

La reflexión tendrá un costo enorme: no es una regla básica. –

6

Ahora que tengo tiempo limitado para terminar el proyecto, realmente no puedo "experimentar".

Entonces su restricción real no es el rendimiento, sino que puede codificar y probar y depurar en la restricción de tiempo dada. Suponiendo que su afirmación de que la versión de reflexión es 30 veces más pequeña, parece que es a lo que debe inclinarse.

Sin embargo, cinco puntos:

  1. dudo que 30x es la estimación correcta, es probable que sea mucho más pequeño.
  2. La reflexión será menos eficaz que el uso de genéricos. No hay dudas sobre eso.
  3. "Sin embargo, me preocupa si el reflejo constante de las propiedades afectará el rendimiento". Puede mitigar algo de esto al almacenar en caché agresivamente el PropertyInfo sy lo que no cargue por reflexión.
  4. Puede hacer algo similar a la reflexión utilizando árboles de expresiones, y verá un aumento de rendimiento significativo al hacerlo. Aquí hay un blog post que lo empujará en la dirección correcta sobre este tema. Sin embargo, si aún no está familiarizado con el trabajo con árboles de expresiones, dada su limitación de tiempo, es un riesgo asumir la codificación, las pruebas y la depuración utilizando un concepto que no conoce.
  5. ¿Está seguro de que esto es incluso un cuello de botella de rendimiento? No me sorprendería si la latencia de la red domina aquí, y estás optimizando prematuramente.
+0

Realmente es una reducción de 30x. Intenté publicar una respuesta a mi tema con un código, pero stackoverflow no me deja en las próximas 6 horas. No creo que los árboles de expresión me ayuden mucho. Voy solo a 1 nivel en profundidad, por lo que no hay una optimización habitual que se haga con árboles binarios (lo que reduce el doble paso sobre la misma propiedad al escanearlo y más adelante los niños). ¿O estoy equivocado en eso? Estaba pensando que TCP podría ser un cuello de botella más grande, pero aún no puedo estimar eso tampoco ... –

+2

Sí, estás equivocado sobre los árboles de expresión. Mire, si la latencia de la red es el cuello de botella como ya dije en mi respuesta, entonces no estoy seguro de por qué está perdiendo el sueño por esto dado que su restricción real es la fecha límite de un proyecto. Codifíquelo de manera que sepa cómo y cumpla con su fecha límite. Si el rendimiento no importa porque esto no es un cuello de botella, ¿por qué te preocupa el rendimiento? ** Además, nunca debes estar adivinando cuando trabajas en el rendimiento de todos modos. ** – jason

0

Si el rendimiento es su preocupación, considere la generación de DynamicMethod para cada tipo de mensaje. El código que generará el método dinámico será el mismo para todos los tipos de mensajes (al igual que lo sería con la reflexión), pero una vez que se genere el método dinámico, no tendrá ningún impacto de la reflexión. La desventaja es que el código es más difícil de escribir y más difícil de depurar, por lo que esta podría no ser la mejor opción, si tiene presión de tiempo.

0

Un enfoque que a menudo es útil es usar Reflection con clases estáticas genéricas. Este es el enfoque adoptado por cosas como EqualityComparer<T>.Default. Esencialmente, lo que sucede es que para cualquier tipo particular T, la primera referencia a EqualityComparer<T>.Default tendrá que utilizar la reflexión para determinar si el tipo T implementa IComparable<T> y construir ya sea una implementación de IEqualityComparer<T> que opera en una no restringida T y utiliza Object.Equals, o bien una que restricciones T a IEqualityComparer<T> y utiliza IEqualityComparer<T>.Equals. Usar la reflexión para ese propósito es algo lento, pero solo necesita hacerse una vez para cualquier tipo particular T. Una vez que se construya la implementación, se almacenará en un campo estático de EqualityComparer<T>, por lo que cualquier solicitud futura para obtener el comparador predeterminado para ese tipo podrá simplemente usar la implementación IEqualityComparer<T> construida anteriormente.

Este enfoque combina la velocidad de los genéricos con la flexibilidad para hacer cosas para las cuales los genéricos por sí solos no son suficientes.

0

código actual hace esto:

protected void Decode<T>(Action<BinaryReader> action, byte[] buffer) {...} 

Crea flujo MemoryStream. Crea el lector BinaryReader en la transmisión. Y luego realiza la acción (lector).

Supongamos ahora que tenemos HelloMessage.cs que solo tiene dos propiedades, un Id de byte y un Descriptor int. El método de decodificación del decodificador HelloMessageCodec.cs clase tiene este aspecto:

public HelloMessage Decode(byte[] buffer) 
{ 
    var message = new HelloMessage(); 
    Decode<HelloMessage>(reader => 
     { message.Id = reader.ReadByte(); 
      message.Descriptor = reader.ReadInt32(); 
     }, buffer); 
    return message; 
} 

Esto es realmente muy bien hecho, sin embargo, hay unos 15 tipos de mensajes, y cada tipo tiene sus propias xxxMessageCodec.cs con decodificar y codificar las implementaciones , cada una de las cuales tiene propiedades pasadas manualmente dentro del delegado de Acción.

Ahora tengo que hacer el cifrado, lo que significa que, si sigo este patrón, tendré que crear 15 encriptadores de mensajes diferentes.

Ya he vuelto a trabajar todo el código codec para la solución # 2 (con reflejo), y en lugar de tener 15 x 2 funciones, solo tengo 2 funciones + no niveles subyacentes solo dos funciones que procesan mensajes por sus propiedades e invocan ReadByte, ReadInt32, ... dependiendo del tipo de propiedad.

Por lo tanto, he hecho el trabajo, pero no tengo tiempo suficiente para probar el rendimiento ya que tengo que seguir trabajando en el cifrado. Tengo que elegir una de las dos soluciones para continuar, y tengo problemas para dormir si la solución más pequeña (la del reflejo) me echaría de lado cara a cara :)