Optimización de Código – Utilizando Generics – C#
April 19th, 2010 by JuanK
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/Boxingt:"+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 Genericst:" +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):
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.
- 9 Comentarios »
- Publicado en la categoría 'C#'

Wordpress
9 comentarios to “Optimización de Código – Utilizando Generics – C# ”
January 21st, 2008 at 10:06 am
Me gusta tu sitio, manejas cosas muy interesantes
saludox
January 30th, 2008 at 5:57 am
Buenas, tenÃa una duda con respecto a esto…
¿Y no serÃa todavÃa mejor un array de toda la vida?
He hecho mi propia prueba añadiendo esta función a su código (y en el main el conteo de tiempo y memoria como usted lo hizo en los otros dos casos):
private static long arraydetoalavida()
{
long suma = 0;
int[] arrayEnteros = new int[tamano];
for(int i=0;i?
Gracias
January 30th, 2008 at 6:00 am
Oh el comentario anterior se cortó…
Bueno decÃa que usando int[] (se que poner código ha hecho que se corte el comentario) mis resultados son estos:
Tiempo Un/Boxing: 7453 Memoria: 494652236
Tiempo Generics: 1094 Memoria: 201774748
Tiempo int[]: 438 Memoria: 120448104
Veo que usar int[] es más rápido que arrayList y List… pero ¿cuales serÃan las deficiencias de usar un array (no arraylist) con respecto a List?
January 30th, 2008 at 8:23 am
Hola,
realmente hay una gran diferencia…
un array NO es redimensionable, una colección si es redimensionable, desde luego si nunca vas a cambiar el tamaño de un arreglo lo mejor es usar array, pero lo usual es que si debes cambiarle de tamaño.
February 19th, 2008 at 6:18 am
buen trabajo, la verdad que estoy de acuerdo contigo.
May 6th, 2009 at 4:02 am
Hola,
veo que el uso de memoria mejora al utilizar el Colletc, pero es recomendable?? COn esta instrucción fuerzas al paso del recolector de basura y pierdes las posibles optimizaciones que este usa en su paso por el rpograma. Es un uso abrasivo de la instrucción.
Un saludo
May 6th, 2009 at 8:51 am
No es recomendable, solo lo hice para hacer las mediciones. En condiciones normales nunca necesitaras o deberás usar algo asÃ.
June 30th, 2009 at 5:37 pm
muy bueno esta entrada, seria buenisimo si pudiers poner un buen ejemplo de uso de generics en capas ya en una consulta a la base de datos, con eso ayudarias a mucha gente incluyendome a saber como realmente usar generics.
Saludos
December 1st, 2011 at 7:51 am
Yo lo que entiendo es que al usar generics la resolución del boxing es en tiempo de compilación no de ejecución, de ahà la mejora del rendimiento.