2008-09-18 8 views
15

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.Genéricos vs. Matriz Listas

Desde la perspectiva del rendimiento, ¿qué importancia tiene cambiar el código al uso de 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?

Respuesta

14

Técnicamente, el rendimiento de los genéricos es, como usted dice, 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.

que sugeriría:

  • genéricos de uso en el futuro.
  • si tiene pruebas unitarias sólidas continuación refactorizarán a los genéricos que se toca código
  • pasan otra vez haciendo refactorizaciones/medida que mejorará significativamente el rendimiento (las llamadas bases de datos, cambios en las estructuras de datos, etc.) en lugar de unos pocos milisegundos aquí y allá .

Por supuesto que hay razones distintas de actuación para cambiar a los genéricos:

  • menos propenso a errores, ya que tienes tiempo de compilación comprobación de tipos
  • más fácil de leer, que no es necesario para emitir por todo el lugar y es obvio qué tipo se almacena en una colección
  • si está usando medicamentos genéricos en el futuro, entonces es más limpio para usarlos en todas partes
4

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 es trivial en su aplicación particular y no valdría la pena refactorización. En el futuro, aún debe considerar el uso de genéricos debido a la seguridad del tipo de tiempo de compilación.

0

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.

1

Depende, la mejor respuesta es perfilar su código y ver. Me gusta AQTime pero existen varios paquetes para esto.

En general, si una ArrayList se utiliza 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.

Editar: Programador fuera de la ley tiene un buen punto, igual 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.

0

¿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.

+2

Investigue la documentación sobre genéricos y su utilidad al usar structs, y verá qué tiene que ver el boxeo con eso. (Sugerencia: no puede obtener un puntero de objeto a una variable de pila) – Guvante

0

Mi empresa anterior 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 o no 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.

3

Los genéricos, ya sean Java o .NET, se deben usar 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, le sugiero que siga adelante y solo actualice el código existente si necesita ser limpiado para fines de seguridad/diseño, no el rendimiento.

0

Depende de cuánto hay en su código. Si vincula o muestra grandes listas en la interfaz de usuario, probablemente verá un gran aumento en el rendimiento.

Si su ArrayList se rocía por aquí y por 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 if-you-touch-it-change-it enfoque.

Lo principal es, sin embargo, que los genéricos son mucho más fáciles de leer, y son más estables en la aplicación debido a la fuerte 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.

0

Si las entidades en ArrayLists son tipos de Objeto, ganará un poco si no las convierte al tipo correcto. Si son tipos de valor (estructuras o primitivas como Int32), entonces el proceso de encajonar/desempaquetar agrega mucha sobrecarga, y las colecciones genéricas deberían ser mucho más rápidas.

Here's an MSDN article on the subject

8

He aquí los resultados que obtuve de un simple análisis de una cadena de un archivo de 100 KB 100.000 veces. La lista genérica (de char) tomó 612.293 segundos para ir 100.000 veces a través del archivo. ArrayList tomó 2.880.415 segundos para ir 100.000 veces a través del archivo. Esto significa que en este escenario (como varía su millaje variará) la lista genérica (de carbonilla) es 4,7 veces más rápida.

Aquí está el código que corrió a través de 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 
0

genéricos tiene un rendimiento mucho mejor, especialmente si usted va a utilizar el valor de tipo (int, bool, estructura, etc.) donde se obtendrá una ganancia de rendimiento notable.

  1. Usando Arraylist con tipos de valor hace que el boxeo/unboxing, que si se hace es varios cientos de veces más lento que usando substantialy lista genérica.

  2. Al almacenar los tipos de valor como objeto, tendrá una memoria de hasta cuatro veces 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í acerca de here.

+1

La lógica de su aplicación era defectuosa. Específicamente, InsertItemsToList tiene el stopper. Start() mal ubicado. Necesita estar dentro del ciclo. Además, la diferencia de rendimiento al usar una Lista <> de objetos frente a una ArrayList de objetos es mínima, aproximadamente un 10% de aumento de velocidad (mis números fueron 1.982/1.81/1.233/1.089). Es cierto que el aumento de rendimiento para List <> frente a ArrayList es muy tangible para int, simplemente no está ahí para las clases. – JustLoren

+0

@JustLoren - Incluso sin el "error" menor, el cálculo es aproximadamente el mismo. Y escribí ambos en mi respuesta (arriba) y en la publicación del blog, la diferencia de rendimiento se debe principalmente al boxeo/desempaquetado de tipo valor –

0

El uso de genéricos también debería significar que su código será más simple y fácil de usar si desea aprovechar cosas como linq en las versiones posteriores de C#.