¿La unidad es lenta? Precaución LINQ

A menudo se dice que la Unidad es lenta. Pero cuanto? Estoy desarrollando una aplicación Pixel Studio, este es un editor de pixel art. Para él, escribí mi implementación del formato GIF. La operación que consume más tiempo es la codificación GIF, es decir, el algoritmo de compresión LZW. Veamos cómo Unity maneja esto.

imagen

En primer lugar, aclararé de inmediato por qué tuve que escribir mi propia biblioteca. Sí, todo es simple, la biblioteca System.Windows.Media.Imaging, que contiene el maravilloso GifBitmapEncoder, no se puede conectar a Unity. Por lo tanto, leí artículos en el centro sobre GIF, tomé la especificación e hice mi GIF con blackjack.

Por supuesto, comencé a escribir y depurar la biblioteca en Visual Studio, en una aplicación de consola. Omitiré este momento, fue aburrido y largo de depurar, tomó 3 días. En tales algoritmos, no tenía experiencia, generalmente haciendo juegos. De acuerdo, la biblioteca está lista. Por ejemplo, un GIF "pesado" de prueba en 200 cuadros y una resolución de 256x256 se codifica en 15 segundos (Ryzen 7, por supuesto, en mi hardware). Pensé mucho, y con la mano del caballero hice que el proceso de compresión fuera paralelo (como Unity debería soportar hilos). El GIF de prueba se codificó en 5 segundos. Genial

Simplemente copiando todo el código fuente en Unity para verificar si funciona. La codificación GIF de prueba tarda 120 segundos. En el modo de subprocesos múltiples (sí, los subprocesos en Unity funcionan, lo principal es no tocar la interfaz de usuario) la codificación de un gif tarda 180 segundos. Facepalm.

Google: resulta que el problema es común. Según informes similares, el código algorítmico en Unity se ejecuta entre 10 y 20 veces más lento. Esto supuestamente está relacionado con otra implementación de recolección de basura y el brumoso Editor Overhead. En ensamblajes (Windows, Android) la situación es similar. En el ensamblado .exe, por ejemplo, funciona un poco más rápido, en un 20%.

Pregunta a los lectores: ¿estoy haciendo algo mal o hay algún problema?

Enlace a la implementación de GIF, quienes están interesados: GitHub . La biblioteca se escribió en C # versión 6 y .NET 3.5 para ser compatible con versiones anteriores de Unity. El proyecto se puede cambiar a .NET 4.0, luego ThreadPool funcionará mucho más rápido.

UPD: ¡Gracias por los comentarios! Todo resultó un poco confuso: ambos hilos con bloqueos y una implementación de curva. Así que preparé un caso simple: la operación de ejecutar una matriz en LINQ. Es decir, dicha operación se realiza durante la compresión LZW (solo hay una comprobación de clave en el diccionario).

imagen

Ejecutamos en la aplicación de consola - 5 ms.
Ejecutando en Unity - 2100 ms.

UPD: Parte del problema se encuentra en LINQ. Por ejemplo, llamar a Contains es varias veces más lento que Array.IndexOf. LINQ es genial, ahorra tiempo y hace que el código sea más hermoso. Pero no en tareas aplicadas como trabajar con grandes conjuntos de objetos. Esto se aplica a una implementación específica de LINQ en Unity.

UPD: optimizado el algoritmo de compresión LZW, eliminó las claves de cadena en el diccionario. Se eliminó LINQ para colecciones grandes cuando fue posible. No toqué la implementación de subprocesos múltiples. Y todo voló, incluso en Android. Por supuesto, la diferencia entre la velocidad de la aplicación de consola permanece, pero no es tan significativa.

¡Un agradecimiento especial a WNeZRoS por su ayuda en la optimización del código y a todos los participantes en la discusión! Aunque la causa de los frenos en LINQ sigue sin resolverse.

Source: https://habr.com/ru/post/es415923/


All Articles