c# - Genéricos vs. Array Listas
performance generics (12)
El sistema en el que trabajo aquí fue escrito antes de .net 2.0 y no tenía el beneficio de los genéricos. Eventualmente se actualizó a 2.0, pero ninguno de los códigos fue refactorizado debido a restricciones de tiempo. Hay varios lugares donde el código usa ArraysLists, etc. que almacenan cosas como objetos.
Desde la perspectiva del rendimiento, ¿qué tan importante es cambiar el código para usar genéricos? Sé desde una perspectiva de rendimiento, boxeo y desempaquetado, etc., que es ineficiente, pero ¿cuánto de la ganancia de rendimiento habrá realmente por cambiarla? ¿Los genéricos son algo para usar de manera progresiva, o hay suficiente cambio en el rendimiento que se debe hacer un esfuerzo de conciencia para actualizar el código anterior?
¿Qué tiene que ver el autoboxing / unboxing con los genéricos? Esto es solo un problema de seguridad de tipo. Con una colección no genérica, debe volver a enviarla explícitamente al tipo real de un objeto. Con genéricos, puede omitir este paso. No creo que haya una diferencia de rendimiento en un sentido u otro.
Depende de cuánto hay en tu código. Si vincula o muestra grandes listas en la interfaz de usuario, probablemente verá un gran aumento en el rendimiento.
Si su ArrayList solo se rocía aquí y allá, entonces probablemente no sería un gran problema simplemente limpiarlo, pero tampoco afectaría mucho el rendimiento general.
Si está utilizando mucho ArrayLists en todo su código y sería una gran tarea reemplazarlos (algo que puede afectar sus horarios), entonces podría adoptar un enfoque de si lo toca.
Sin embargo, lo principal es que los genéricos son mucho más fáciles de leer y son más estables en la aplicación debido a la gran capacidad de tipeo que obtienen de ellos. Verá ganancias no solo por el rendimiento, sino también por el mantenimiento y la estabilidad del código. Si puedes hacerlo rápido, yo diría que lo hagas.
Si puede obtener la aprobación del propietario del producto, le recomiendo que lo limpie. Amas tu código más después.
Depende, la mejor respuesta es perfilar tu código y ver. Me gusta AQTime pero existen varios paquetes para esto.
En general, si una ArrayList se usa MUCHO, puede valer la pena cambiarla a una versión genérica. Realmente, es más probable que ni siquiera puedas medir la diferencia de rendimiento. El boxeo y el desempaquetado son pasos adicionales, pero las computadoras modernas son tan rápidas que casi no hacen diferencia. Como ArrayList es realmente una matriz normal con un buen contenedor, probablemente verá mucho más rendimiento obtenido de una mejor selección de estructura de datos (ArrayList.Remove es O (n)!) Que con la conversión a genéricos.
Edit: Outlaw Programmer tiene un buen punto, seguirás boxeando y desempacando con genéricos, simplemente sucede implícitamente. Sin embargo, todo el código para verificar las excepciones y los nulos del casting y las palabras clave "is / as" ayudaría un poco.
Estos son los resultados que obtuve de un simple análisis de una cadena de un archivo de 100KB 100.000 veces. La lista genérica (de char) tomó 612.293 segundos para ir 100.000 veces a través del archivo. ArrayList tardó 2.880.415 segundos en recorrer 100,000 veces a través del archivo. Esto significa que en este escenario (ya que su kilometraje variará), la Lista genérica (de carbón) es 4.7 veces más rápida.
Aquí está el código que ejecuté 100.000 veces:
Public Sub Run(ByVal strToProcess As String) Implements IPerfStub.Run
Dim genList As New ArrayList
For Each ch As Char In strToProcess.ToCharArray
genList.Add(ch)
Next
Dim dummy As New System.Text.StringBuilder()
For i As Integer = 0 To genList.Count - 1
dummy.Append(genList(i))
Next
End Sub
Public Sub Run(ByVal strToProcess As String) Implements IPerfStub.Run
Dim genList As New List(Of Char)
For Each ch As Char In strToProcess.ToCharArray
genList.Add(ch)
Next
Dim dummy As New System.Text.StringBuilder()
For i As Integer = 0 To genList.Count - 1
dummy.Append(genList(i))
Next
End Sub
La única forma de saberlo con certeza es perfilar su código con una herramienta como dotTrace.
http://www.jetbrains.com/profiler/
Es posible que el boxeo / unboxing sea trivial en su aplicación particular y no valga la pena refactorizar. En el futuro, aún debe considerar el uso de genéricos debido a la seguridad del tipo de tiempo de compilación.
Las mayores ganancias las encontrará en las fases de Mantenimiento. Los genéricos son mucho más fáciles de manejar y actualizar, sin tener que lidiar con problemas de conversión y conversión. Si este es el código que continuamente visita, entonces, por supuesto, haga el esfuerzo. Si este es un código que no ha sido tocado en años, realmente no me molestaría.
Los genéricos, ya sean Java o .NET, deberían usarse para el diseño y tipo de seguridad, no para el rendimiento. El Autoboxing es diferente de los genéricos (esencialmente objeto implícito a las conversiones primitivas), y como mencionaste, NO debes usarlos en lugar de un primitivo si va a haber muchas operaciones aritméticas u otras operaciones que causarán un golpe de rendimiento de las repetidas creación / destrucción de objetos implícitos.
En general, sugeriría seguir adelante y solo actualizar el código existente si es necesario limpiarlo para fines de diseño / seguridad, no para el rendimiento.
Mi antigua compañía realmente consideró este problema. El enfoque que tomamos fue: si es fácil de refactorizar, hágalo; si no (es decir, tocará demasiadas clases), déjelo para más adelante. Realmente depende de si tiene tiempo para hacerlo o si hay elementos más importantes para codificar (es decir, funciones que debe implementar para los clientes).
Por otra parte, si no está trabajando en algo para un cliente, adelante y dedique un tiempo a la refactorización. Mejorará la legibilidad del código para usted.
Si las entidades en ArrayLists son tipos de Objeto, ganarás un poco si no las lanzas al tipo correcto. Si son tipos de valor (estructuras o primitivas como Int32), el proceso de encajonar / desempaquetar agrega mucha sobrecarga, y las colecciones genéricas deberían ser mucho más rápidas.
Técnicamente, el rendimiento de los genéricos es, como dices, mejor. Sin embargo, a menos que el rendimiento sea sumamente importante Y ya hayas optimizado en otras áreas, es probable que obtengas MEJORES mejoras si pasas tu tiempo en otro lugar.
Yo sugeriría:
- usa genéricos en el futuro.
- si tiene pruebas de unidades sólidas, vuelva a configurar genéricos a medida que toca el código
- Pase otro tiempo haciendo refactorizaciones / mediciones que mejorarán significativamente el rendimiento (llamadas a la base de datos, estructuras de datos cambiantes, etc.) en lugar de unos pocos milisegundos aquí y allá.
Por supuesto, hay otras razones además del rendimiento para cambiar a los genéricos:
- menos propenso a errores, ya que tiene verificación de tipos en tiempo de compilación
- más legible, no es necesario lanzarlo por todos lados y es obvio qué tipo se almacena en una colección
- Si usa genéricos en el futuro, entonces es más limpio usarlos en todas partes
Generics tiene un rendimiento mucho mejor, especialmente si va a utilizar value-type (int, bool, struct, etc.) donde obtendrá una notable ganancia de rendimiento.
El uso de Arraylist con tipos de valor causa el boxeo / unboxing que, si se realiza varios cientos de veces, es sustancialmente más lento que utilizar la Lista genérica.
Al almacenar los tipos de valor como objeto, tendrá hasta cuatro memorias por artículo. Si bien esta cantidad no agotará su RAM, la memoria caché que es más pequeña podría contener menos elementos, lo que significa que al iterar una colección larga habría muchas copias de la memoria principal a la caché que ralentizarían su aplicación.
Escribí sobre aquí .
Usar genéricos también debería significar que su código será más sencillo y fácil de usar si desea aprovechar cosas como linq en las versiones posteriores de c #.