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: 6846|Respuesta: 2

[Fuente] [Gira]. Optimización del rendimiento .NET - Collections. Se recomienda Pooled

[Copiar enlace]
Publicado el 29-5-2022 13:45:22 | | | |
Breve introducción

La optimización del rendimiento es cómo asegurar que el mismo número de solicitudes se procese con menos recursos, que generalmente son CPU o memoria, y por supuesto, manejos de E/S del sistema operativo, tráfico de red, uso de disco, etc. Pero la mayoría de las veces, reducimos el uso de CPU y memoria.
El contenido compartido antes tiene algunas limitaciones, es difícil transformarlo directamente, hoy quiero compartir con vosotros un método sencillo: solo hay que reemplazar unos pocos tipos de colecciones para lograr el efecto de mejorar el rendimiento y reducir la huella de memoria.
Hoy quiero compartir con vosotros una biblioteca de clase, estaLa biblioteca de clases se llama Collections.Pooled, Como se puede ver por el nombre, es a través de memoria agrupada para lograr el propósito de reducir la huella de memoria y la GC, y veremos directamente cómo es su rendimiento, y también te llevaremos a ver el código fuente, por qué aporta estas mejoras de rendimiento.

Colecciones. Agrupadas

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

La biblioteca se basa en clases de System.Collections.Generic, que han sido modificadas para aprovechar las nuevas bibliotecas de clases System.Span <T>y System.Buffers.ArrayPool <T>con el propósito de reducir la asignación de memoria, mejorar el rendimiento y permitir una mayor interoperabilidad con APIs modernas.
Collections.Pooled soporta .NET Standnd 2.0 (.NET Framework 4.6.1+), así como soporte para . NET Core 2.1+. Se ha portado un extenso conjunto de pruebas unitarias y benchmarks desde CoreFX.

Número total de pruebas: 27.501. Vía: 27501. Fracaso: 0. Saltar: 0.
La prueba fue un éxito.
Tiempo de ejecución de la prueba: 9,9019 segundos

Cómo usarlo

Puedes instalar fácilmente esta biblioteca a través de Nuget, NuGet Version.

En la biblioteca Collections.Pooled, implementa versiones agrupadas para los tipos de colección que usamos comúnmente, como se muestra en la comparación con los tipos nativos .NET.

. .NET nativoColecciones. Agrupadascomentario
Lista<T>PooledList<T>Clases genéricas de colección
Diccionario<TKey, TValue>PooledDictionary<TKey, TValue>Clase genérica de diccionario
HashSet<T>PooledSet<T>Clases genéricas de colecciones hash
Pila<T>Pila<T>Pilas genéricas
Cola<T>PooledQueue<T>Cohorte genérica

Al usar, solo necesitamos añadir el correspondiente . .NET con la versión Collections.Pooled, como se muestra en el código a continuación:

Sin embargo, hay que señalar que el tipo Pooled implementa la interfaz IDispose, que devuelve la memoria utilizada al pool a través del método Dispose(), por lo que necesitamos llamar a su método Dispose() después de usar el objeto de colección Pooled. O puedes usar directamente la palabra clave using var.

Nota: Utiliza el objeto de colección dentro de Collections.PooledLo mejor es necesitar liberarlo manualmente, pero no importa si no lo liberas, el GC eventualmente lo reciclará, pero no se puede devolver al pool y no logrará el efecto de ahorrar memoria.
Dado que reutiliza espacio de memoria, al devolver espacio de memoria al pool, necesita procesar los elementos de la colección y proporciona una enumeración llamada ClearMode para su uso, definida de la siguiente manera:




Por defecto, puedes usar el valor por defecto Auto, y si hay requisitos especiales de rendimiento, puedes usar Never después de conocer los riesgos.
Para los tipos de referencia y los tipos de valor que contienen tipos de referencia, debemos vaciar la referencia del array al devolver el espacio de memoria al pool; si no se limpia, el GC no podrá liberar esta parte del espacio de memoria (porque la referencia del elemento siempre ha estado en manos del pool); si es un tipo de valor puro, entonces no puede vaciarse; en este artículo describo la diferencia de almacenamiento entre los tipos de referencia y los arrays de struct (tipo valor); los tipos de valor puro no tienen reciclaje de cabeceras de objeto y no requieren intervención de GC.


. Optimización del rendimiento .NET - Utilizar clases alternativas de estructura:El inicio de sesión del hipervínculo es visible.

Comparación de rendimiento

No hice Benchmark solo, y los resultados de la puntuación de los proyectos de código abierto que usé directamente fueron 0 para el uso de memoria de muchos proyectos, lo que se debía a que la memoria agrupada no tenía asignación extra.

PooledList<T>

Repasa en bucle los 2048 elementos añadidos al conjunto en Benchmark, . .NET native <T>List requiere 110us (según los resultados reales del benchmark, los milisegundos en la figura deberían ser un error administrativo) y 263KB de memoria, mientras que PooledList <T>solo necesita 36us y 0KB de memoria.




PooledDictionary<TKey, TValue>

Añade 10_0000 elementos al diccionario en un bucle en Benchmark, . .NET Native Dictionary<TKey, TValue> requiere 11 ms y 13MB de memoria, mientras que PooledDictionary<TKey, TValue> solo requiere 7 ms y 0MB de memoria.




PooledSet<T>

Haz un bucle a través de la colección hash en Benchmark y añade 10_0000 elementos, . El HashSet nativo .<T>NET requiere 5348ms y 2MB, mientras que el PooledSet <T>solo requiere 4723ms y 0MB de memoria.




PooledStack<T>

Haz un bucle a través de la pila en Benchmark para añadir 10_0000 elementos, . El PooledStack nativo <T>.NET requiere 1079ms y 2MB, mientras que PooledStack <T>solo requiere 633ms y 0MB de memoria.




PooledQueue<T>

Haz un bucle a través de los bucles en Benchmark para añadir 10_0000 elementos a la cola, . <T>La PooledQueue nativa .NET requiere 681 ms y 1MB, mientras que la PooledQueue <T>solo requiere 408 ms y 0MB de memoria.




La escena no se libera manualmente

Además, mencionamos antes que el tipo de colección agrupada debe ser publicado, pero no importa si no se publica, porque el GC se reciclará.


Los resultados del Benchmark son los siguientes:



La conclusión puede extraerse de los resultados de los Benchmarks anteriores.

Liberar la colección de tipos Pooled a tiempo apenas activa GC y asigna memoria; del gráfico anterior solo asigna 56 bytes de memoria.
Incluso si la colección de tipos Pooled no se libera, porque asigna memoria del pool, seguirá reutilizando memoria durante la operación de expansión ReSize y saltará el paso de inicialización de la asignación de memoria de GC, que es relativamente rápido.
La más lenta es usar el tipo de colección normal, cada operación de expansión ReSize debe solicitar nuevo espacio de memoria, y el GC también debe recuperar el espacio de memoria anterior.


Análisis de principios

Si has leído mi anterior entrada en el blog, deberías establecer el tamaño inicial para los tipos de colección y analizar el principio de implementación del diccionario C#, puedes saber que los desarrolladores de BCL de .NET utilizan las estructuras de datos subyacentes de estos tipos básicos de colecciones como arrays para acceso aleatorio de alto rendimiento, tomemos <T>List como ejemplo.

Crea un nuevo array para almacenar los elementos añadidos.
Si no hay suficiente espacio en el array, la operación de expansión se activa para solicitar el doble del tamaño del espacio.
El código constructor es el siguiente, y puedes ver que es un array genérico creado directamente:


Así que si quieres agrupar memoria, solo necesitas cambiar el lugar donde se usa la nueva aplicación de palabras clave en la biblioteca de clases para usar la aplicación en pool. Aquí lo comparto contigo. NET BCL es un tipo llamado ArrayPool, que proporciona un conjunto de recursos de array con instancias genéricas reutilizables, que pueden usarse para reducir la presión sobre GC y mejorar el rendimiento en caso de creación y destrucción frecuente de arrays.

La capa subyacente de nuestro tipo Pooled es usar ArrayPool para compartir pools de recursos, y por su constructor podemos ver que usa ArrayPool por <T>defecto. Compartido para asignar objetos de array, y por supuesto también puedes crear tu propio ArrayPool para usarlo.


Además, al realizar una operación de ajuste de capacidad (expansión), el antiguo array se devuelve al pool de hilos y el nuevo array también se adquiere del pool.

Además, el autor utiliza Span para optimizar APIs como Add e Insert y así ofrecer un mejor rendimiento en acceso aleatorio. Además, se ha añadido la API de la serie TryXXX, para que puedas usarla de forma más cómoda. Por ejemplo, la <T>clase List <T>tiene hasta 170 modificaciones en comparación con PooledList.



resumen

En nuestro uso real en línea, podemos reemplazar el tipo de colección nativo por el tipo de colección proporcionado por Pooled, lo cual es muy útil para reducir el consumo de memoria y la latencia de P95.
Además, aunque olvides lanzarlo, el rendimiento no será mucho peor que usando el tipo de colección nativo. Por supuesto, el mejor hábito es liberarlo a tiempo.


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




Anterior:RecyclableMemoryStream ofrece streaming de .NET de alto rendimiento
Próximo:[Combate práctico] El servidor construye LibreSpeed para probar la velocidad de la red
Publicado el 29-5-2022 17:12:36 |
Apréndelo
Publicado el 20-6-2022 09:09:22 |
Aprende la mezcla
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