‘Aplicaciones Dinámicas’ con .NET Reflection y su Rendimiento

febrero 11, 2009 06:38 by Admin

Enviado por Johnatan Flores Carmona el 19 de enero de 2009 

Saludos a todos, antes que nada quisiera comentar que felicito a los propietarios de este grupo así cómo a los usuarios por esta nueva etapa ahora en 'Live'. He sido miembro de este grupo desde ya algunos años y aunque mi participación ha sido casí nula me gustaria pensar "Año Nuevo, Grupo Nuevo, Actitud Nueva" así que para empezar con la nueva actitud, me gustaría compartir con ustedes algo que he tenido que a prender por las malas: El maravillos mundo de .NET Reflection........

Problema:

Actualmente en la institución donde laboro me asignaron a un proyecto que consiste en construir un ‘Bridge’ para que la aplicación ‘X’ pudiera comunicarse con la aplicación ‘Y’. Uno de los muchos requerimientos que solicitaron fue: ‘Actualmente debes conectar ‘X’ con ‘Y’, pero este bridge debe servir posteriormente para comunicar a ‘W’ con ‘Z’, ‘Y’ con ‘W’, etc. por lo que debe ser una aplicación que su funcionalidad deba ser configurable desde base de datos. ¿Cuál fue la solución a estos requerimientos?  R:= .NET Reflection. 

Solución con Reflection:

Implementaciones: Cada uno de las aplicaciones (W, X, Y, Z) tiene sus propias ‘reglas’ de comunicación, por lo que para comunicar XàY se requiere una ‘implementación’ diferente que para comunicar a WàZ y otra totalmente diferente para YàW.

Interfaz de implementación: Todas las implementaciones deberán exponer un método llamado ‘Ejecutar’ el cuál servirá para llevar a cabo los procesos necesarios para cada una de las implementaciones.

Fabrica de implementaciones: Se requiere un método que nos permita obtener la implementación correcta en base a los parámetros de entrada (posiblemente un ID o un nombre).

Diccionario de implementaciones: Requerimos un diccionario que nos permitirá conocer el nombre de la clase que deseamos utilizar para realizar la implementación correcta (en nuestro caso una base de datos o un archivo xml).

Prueba: El método Main que realiza la prueba (hipotetica) de esta solución sería de la siguiente manera:

    class TestRendimientoReflection

    {

        static void Main(string[] args)

        {

            

            //Codigo para medir el tiempo de ejecucion

            EjecutarTest(totalIteraciones);

        }

        static void EjecutarTest(int totalIteraciones)

        {

            Random generadorIdentificadores = new Random();

            for (int i = 0; i < totalIteraciones; i++)

            {

                int idClase = generadorIdentificadores.Next(1, 11);

                IImplementacion clase = FabricaImplementacion.CrearImplementacion(idClase);

                Console.WriteLine(clase.Ejecutar());

            }

        }

    }

Solución sin Reflection:

Implementaciones: (Igual que con Reflection)

Interfaz de implementación: (Igual que con Reflection)

Prueba: El método Main que realiza la prueba de esta solución (hipotetica) sería de la siguiente manera:

    class TestRendimientoSinReflection

    {

        static void Main(string[] args)

        {

            

            //Codigo para medir el tiempo de ejecucion 

            EjecutarTest(totalIteraciones);

        }

        static void EjecutarTest(int totalIteraciones)

        {

            Random generadorIdentificadores = new Random();

            for (int i = 0; i < totalIteraciones; i++)

            {

                int idClase = generadorIdentificadores.Next(1, 11);

                IImplementacion clase = null;

                switch (idClase)

                {

                    case 1:

                        clase = new Implementacion01();

                        break;

                    case 2:

                        clase = new Implementacion02();

                        break;

                    //Todos los case necesarios

                    default:

                        clase = new Implementacion10();

                        break;

                }

                Console.WriteLine(clase.Ejecutar());

            }

        }

    }

Rendimiento:

Cuando se me cuestionó sobre si la utilización de Reflection afectaría el rendimiento que podría tener la aplicación tuve que investigar sobre dicho tema y encontré la siguiente referencia: .Net Reflection and Performance

En este enlace menciona que los procesos de obtener y asignar valores a propiedades con reflection resultó de 2.5 a 3 veces más lento que hacerlo directamente; además de que la invocación de métodos fue 3.5 a 4 veces más lento con reflection que realizar una llamada directa al método.

Estos resultados claramente no son satisfactorios para una aplicación que posiblemente tendría que atender cientos o miles de peticiones, pero también no es nuestro caso la asignación o lectura de propiedades ni mucho menos la ejecución de métodos utilizando reflection. Los datos que necesitábamos conocer era el rendimiento al instanciar clases utilizando reflection.

A continuación los datos que fueron obtenidos:

Rendimiento de Reflection
  Prueba con Reflection Prueba sin Reflection
Total iteraciones 10,000 10,000
Total de Pruebas 20 20
Promedio (ms): 3,813.44 3,706.60
Media (ms): 3,793.24 3,592.44
Mediana (ms): 3,914.25 3,867.38

 Con estos datos se puede decir que el rendimiento de instanciar clases utilizando reflection es de 0.01 a 0.06 veces más lento que creando las instancias directamente. 

Beneficios:

A continuación mencionaré algunas métricas que son utilizadas en el desarrollo de software que están ligadas al código fuente y cómo el uso de Reflection podria ayudar a mejorarlas:

Mantenibilidad: Cómo se puede observar, ambos códigos (con Reflection y sin él) son fácilmente mantenibles ya que si existe un cambio en los procesos de una implementación, sólo tendríamos que modificar la clase correspondiente.

Complejidad Ciclomática: Esta métrica se ve mejorada al utilizar reflection ya que cómo podemos observar no necesitaremos la sentencia ‘switch / case’ para la creación de los objetos con las implementaciones deseadas.

Líneas de código: Las líneas de código también se ven reducidas considerablemente al hacer uso de Reflection.  }

Bajo acoplamiento: Debido a que la construcción se basa en un 'diccionario' de implementaciones, si se deseara que los procesos requeridos para comunicar X con Y ya no es la clase 'ImplementacionXY y en su lugar se utilizaría NuevaImplementacionXY; utilizando Reflection sólo se tendría que modificar el diccionario y no todas las porciones de código que hicieran referencia a esa clase (cómo sería con la instanciación directa). 

Conclusiones:

Utilizar .NET Reflection indiscriminadamente en nuestras aplicaciones hará que el rendimiento de ejecución sea bastante pobre; sin embargo, si se planea bien las arquitectura y las estrategias para utilizar .NET Reflection, el costo de utilizar esta poderosa API no es alto si se consideran los beneficios que se pueden obtener.

Si se desea conocer las buenas practicas de programación al utilizar reflection, pueden leer este excelente artículo: Dodge Common Performance Pitfalls to Craft Speedy Applications

Código Fuente:


Actualmente calificado con 5.0 por 1 personas

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Publicaciones relacionadas