Archive for January, 2008
Optimización de Código - Utilizando Generics
Habitualmente en el proceso de desarrollo de nuestros procesos de desarrollo de software hacemos uso de lo comúnmente llamado Colecciones, es decir arreglos de diferente para solucionar determinados problemas.
Normalmente esto no representa mayor problema hasta que nos encontramos con entornos reales de trabajo, aplicaciones cliente servidor o paginas web accesadas de manera simultanea por decenas o miles de usuarios, o como caso mas crítico un videojuego donde uno o dos usuarios (o a veces decenas o miles de ellos…)realizan millones de operaciones por segundo en nuestros procesadores gráficos y CPU’s.
Las colecciones en estos casos se empiezan a volver un problema, porque?. Bien habitualmente muchos de los elementos que utilizamos en el desarrollo son tipos valor como por ejemplo long (Int64), int (Int32), float(Single), double (Double), estructuras (struct) o enumeraciones (enum) los cuales utilizamos ampliamente con el fin de agilizar el rendimiento de nuestra aplicación, pero lastimosamente en el proceso de incluir estos tipos de dato en una colección estos son encajonados (en adelante se hablará boxing) en un tipo object; Esto se debe a que las colecciones para poder almacenar cualquier valor dentro de si almacenan tipos object.
Gracias a Dios o en lo que sea que crean uds.) el CLR 2.0(conocido por la mayoría como .net Framework 2.0) soporta el uso de Generics los cuales no son mas que una cualidad que permite la implementación de código sin tener totalmente definido el/los tipo(s) de dato que esos metodos usan… pero bueno la idea de este hilo no es hacer un curso de generics, así que en resumen gracias a los generics existen un conjunto de colecciones genericas que se traducen en colecciones a las cuales se les puede indicar que tipo de dato almacenaran (entre otras cosas) lo cual se convierte en la eliminación del proceso de boxing y unboxing para poder manipularlas.
Como acotación adicional los desarrolladores de C++ conocen los generics de la STL (Standard Template Library) por lo cual no suelen llamarlos generics sino templates.
Utilizar generics SI es mucho más rápido
Bien, se preguntaran el porque del titulo… así que antes de entrar en materia les contare…
En varios textos de programación en C# y java los autores suelen utilizar unos recuadros con casos reales o notas al margen, y aunque no se el numero exacto si se que ya son varias veces que he visto mencionar que en la practica las colecciones que usan generics no se muestran mucho mas rápidos que las que utilizan el un/boxing tradicional… lo cual es mentira y me pregunto… en que proyectos han trabajado ellos? tal ves en proyecos web y seguramente no en los grandes proyectos web… lo que sin lugar a dudas es cierto es que un desarrollador que tenga experiencia en el montaje de grandes portales, de aplicaciones cliente servidor con gran numero de usuarios o programadores de videojuego o de aplicaciones científicas los desmentirían por completo. Y este post es para eso para desmentir esa falsa creencia y mostrar la realidad: Las colecciones genericas son mucho más eficientes que las no genéricas.
Demostración
Lo primero que haremos sera crear una pequeña aplicación por consola en C# en la cual declararemos esta constante y la siguiente función:
private static long Boxing_Unboxing() { long suma = 0; ArrayList arrayEnteros = new ArrayList(); for (int i = 0; i < tamano; i++) arrayEnteros.Add(i); for (int i = 0; i < tamano; i++) suma = suma + (int)arrayEnteros[i]; return suma; }
No requiere mucha explicación, esta función efectúa el llenado (boxing) de una colección con un numero determinado de elementos y seguidamente suma todos estos elementos en un acumulado (ahí hace unboxing) el cual retorna.
Ahora adicionaremos esta función que hace exactamente lo mismo pero utilizando colecciones genericas:
private static long Generics() { long suma = 0; List<int> arrayEnteros = new List</int><int>(); for (int i = 0; i < tamano; i++) arrayEnteros.Add(i); for (int i = 0; i < tamano; i++) suma = suma + arrayEnteros[i]; return suma; }
Ahora en el método main crearemos el código necesario para invocar las dos funciones anteriores, adicionalmente mediremos el tiempo de ejecución de las mismas para lo cual utilizaremos System.Environment.TickCount y finalmente incluiremos código para medir el consumo de memoria de la aplicación para lo cual haremos uso de dos metodos de la clase estática GC, GC.GetTotalMemory para saber el consumo total de memoria en un momento dado y GC.Collect para liberar los recursos que ya no estan en uso. El código queda así:
const int tamano = 30000000; static void Main(string[] args) { int tiempo =0; tiempo = System.Environment.TickCount; long suma = Boxing_Unboxing(); tiempo = System.Environment.TickCount - tiempo; Console.WriteLine("Resultado: "+suma.ToString()+" Tiempo Un/Boxing\t:"+tiempo.ToString().PadLeft(10,' ') + " Memoria: "+GC.GetTotalMemory(false).ToString().PadLeft(7,' ')); GC.Collect(); tiempo = System.Environment.TickCount; suma = Generics(); tiempo = System.Environment.TickCount - tiempo; Console.WriteLine("Resultado: " + suma.ToString() + " Tiempo Generics\t:" +tiempo.ToString().PadLeft(10, ' ') +" Memoria: " + GC.GetTotalMemory(false).ToString().PadLeft(7, ' ')); GC.Collect(); Console.ReadLine(); }
Una vez ejecutado obtendremos los siguientes valores (o muy parecidos dependiendo del computador de cada cual):
Resultado: 199999990000000 Tiempo Un/Boxing : 4695 Memoria: 883595136
Resultado: 199999990000000 Tiempo Generics : 764 Memoria: 202279736
Como ven en los resultados, generics se muestran aproximadamente 6 veces más rápido que la técnica tradicional y fue casi 5 veces mas efectivo en el uso de la memoria.
Como desconozco que hardware tienen ustedes, pueden hacer pruebas modificando el valor de la constante tamano de acuerdo alas características de su procesador y memoria, esto porque si exceden el limite de almacenamiento en memoria de sus máquinas el sistema operativo iniciara el proceso de swap a disco poniendose la cosa muuuy lenta… ahí obtendrán diferencias de tiempo mucho mayores incluso proporcionalmente.
Hasta la próxima.
5 comments
