Este artículo es un artículo espejo de traducción automática, por favor haga clic aquí para saltar al artículo original.

Vista: 6317|Respuesta: 3

[Fuente] [Gira]. Optimizaciones de rendimiento de la red - recorren rápidamente colecciones de listas

[Copiar enlace]
Publicado el 28-8-2022 20:51:16 | | | |
Breve introducción

System.Collections.Generic.List <T>es una clase de colección genérica en .NET, que puede almacenar cualquier tipo de datos gracias a su comodidad y API enriquecedora, ampliamente utilizada en nuestra vida diaria y puede considerarse la clase de colección más utilizada.

En la escritura de código, a menudo necesitamos iterar a través de una colección de Listas <T>para obtener los elementos que contiene para algún procesamiento empresarial. Normalmente, no hay muchos elementos dentro de un conjunto y es muy rápido de recorrer. Pero para algunos procesamientos de big data, estadística, computación en tiempo real, etc<T>., ¿cómo recorrer rápidamente la colección de listas de decenas de miles o cientos de miles de datos? Eso es lo que necesito compartir con vosotros hoy.

Modo de desplazamiento

Veamos el rendimiento de diferentes métodos de recorrido y construyamos el siguiente benchmark de rendimiento, utilizando diferentes recorridos de colecciones de órdenes de magnitud para ver el rendimiento de distintos métodos. El fragmento de código se ve así:

Usa la instrucción foreach

Foreach es la forma más común en la que recorremos colecciones, es una implementación en Syntax Sugar del patrón iterador, y también se utiliza como referencia para esta época.

Como la sentencia foreach es un syntax sugar, el compilador acaba llamando a GetEnumerator() y MoveNext() con un bucle while para implementar la funcionalidad. El código compilado se ve así:



La implementación del método MoveNext() asegurará que no haya otros hilos modificando la colección en la iteración, y si la modificación ocurre, lanzará una excepción InvalidOperationException, y tendrá una comprobación de desbordamiento para comprobar si el índice actual es legítimo, y también debe asignar el elemento correspondiente al enumerador. Atributo actual,Así que, de hecho, su rendimiento no es el mejor, el fragmento de código se ve así:



Veamos cómo se comporta en diferentes tamaños de conjunto, y los resultados son los siguientes:



Se puede ver que, en el caso de diferentes tamaños, se requiere la relación de crecimiento lineal del proceso que consume tiempo; incluso si recorre 100w de datos sin lógica de procesamiento, tarda al menos 1 segundo.

Utiliza el método ForEach de List

Otra forma común es usar List<T>. ForEach(), que permite pasar un <T>delegado de Acción, que llamará al delegado de Acción mientras itera por el <T>elemento.

Es un <T>método de implementación interna de List, por lo que puede acceder directamente a arrays privados y evitar comprobaciones de desbordamiento. En teoría, debería ser rápido; Pero en nuestro escenario solo hay un método vacío, que puede no comportarse bien con una llamada completamente en línea al método foreach. A continuación se muestra el código fuente del método ForEach que muestra que no tiene comprobación de desbordamiento, pero que aún conserva la comprobación concurrente del número de versión.



Además, dado que es necesario pasar un delegado al método ForEach en el código de llamada, comprobará si el objeto delegado en la clase de generación de cierre está vacío cada vez, y si no, nueva <T>Acción(), como se muestra a continuación:



Veamos cómo se compara con la palabra clave foreach en términos de rendimiento. La siguiente imagen muestra los resultados del benchmark:



A juzgar por los resultados de la prueba, es un 40% más lento que usar directamente la palabra clave foreach; parece que si no es necesario, es mejor opción usarlo directamente para cada uno, ¿entonces hay alguna forma más rápida?

para el recorrido de bucles

Volviendo a nuestra forma más antigua, que es usar la palabra clave for para recorrer la colección. Debería ser el método de recorrido con mejor rendimiento en este momento, porque no requiere código redundante como los anteriores (aunque el indexador también se comprueba para evitar desbordamientos), y obviamente no comprueba el número de versión, así que en un entorno multihilo la colección cambia y no se lanzará ninguna excepción al usar for. El código de prueba se ve así:

Veamos cómo sale.



Parece que esto es lo que esperamos.Usar el bucle for directamente es un 60% más rápido que foreach, un conjunto que antes tardaba 1 segundo en recorrerse, ahora solo tarda 400 milisegundos. ¿Entonces hay una forma más rápida?

Usar ColeccionesMarshal

Después de .NET5, la comunidad dotnet implementó la clase CollectionsMarshal para mejorar el rendimiento de las operaciones de colección. Esta clase implementa cómo acceder a arrays nativos de tipos de colecciones (si has visto mi [. .NET Performance Optimization - Deberías establecer el tamaño inicial para tipos de colección], sabes que la implementación subyacente de muchas estructuras de datos son los arrays). Así que puede saltarse todo tipo de detecciones y acceder directamente al array original, que debería ser el más rápido. El código se ve así:

Puedes ver que el código generado por el compilador es muy eficiente.



El acceso directo al array subyacente es muy peligroso, debes saber qué hacer con cada línea de código y tener suficientes pruebas. Los resultados de los benchmarks son los siguientes:



UauUsar CollectionsMarshal es un 79% más rápido que usar foreach, pero debería ser la razón de la optimización de JIT, no hay gran diferencia entre usar foreach y el bucle clave for Span.

resumen

Hoy os he hablado sobre cómo recorrer rápidamente la colección de Listas, y en la mayoría de los casos se recomienda usar la palabra clave foreach, que incluye tanto comprobación de desbordamiento como control multihilo de números de versión, lo que puede facilitar la escritura del código correcto.

Si necesitas un alto rendimiento y volúmenes de datos grandes, se recomienda usar para y CollectionsMarshal.AsSpan directamente para recorrer la colección.

Enlace al código fuente de este artículo:

El inicio de sesión del hipervínculo es visible.

Enlace original:El inicio de sesión del hipervínculo es visible.





Anterior:Explicación detallada de la arquitectura de mensajes AMQP de RabbitMQ
Próximo:Cabezas de cristal de cable de red T568A y T568B estándar y diferencias
Publicado el 4-9-2022 a las 22:15:52 |
Aprende a aprender
Publicado el 8-9-2022 a las 10:33:05 |
Aprende a aprender
Publicado el 27-6-2023 22:39:13 |
Hola 12306 ¿Puedes enviarme un mensaje privado con los datos
Renuncia:
Todo el software, materiales de programación o artículos publicados por Code Farmer Network son únicamente para fines de aprendizaje e investigación; El contenido anterior no se utilizará con fines comerciales o ilegales; de lo contrario, los usuarios asumirán todas las consecuencias. La información de este sitio proviene de Internet, y las disputas de derechos de autor no tienen nada que ver con este sitio. Debes eliminar completamente el contenido anterior de tu ordenador en un plazo de 24 horas desde la descarga. Si te gusta el programa, por favor apoya el software genuino, compra el registro y obtén mejores servicios genuinos. Si hay alguna infracción, por favor contáctanos por correo electrónico.

Mail To:help@itsvse.com