L'unité est-elle lente? Attention LINQ

On dit souvent que l'unité est lente. Mais combien? Je développe une application Pixel Studio, c'est un éditeur de pixel art. Pour lui, j'ai écrit mon implémentation du format GIF. L'opération la plus longue est le codage GIF, à savoir l'algorithme de compression LZW. Voyons comment Unity gère cela.

image

Premièrement, je vais immédiatement clarifier pourquoi j'ai dû écrire ma propre bibliothèque. Oui, tout est simple, la bibliothèque System.Windows.Media.Imaging, qui contient le merveilleux GifBitmapEncoder, ne peut pas être connectée à Unity. J'ai donc lu des articles sur le hub sur le GIF, pris les spécifications et fait mon GIF avec le blackjack.

Bien sûr, j'ai commencé à écrire et à déboguer la bibliothèque dans Visual Studio, dans une application console. Je vais omettre ce moment, c'était ennuyeux et long à déboguer, cela a pris 3 jours. Dans de tels algorithmes, je n'avais pas d'expérience, faisant habituellement des jeux. D'accord, la bibliothèque est prête. Par exemple, un test GIF "lourd" en 200 images et une résolution de 256x256 est encodé en 15 secondes (Ryzen 7, bien sûr, sur mon matériel). Beaucoup, je pensais, et avec la main du gentleman, j'ai fait le processus de compression parallèle (comme Unity devrait supporter les threads). Le GIF de test est devenu codé en 5 secondes. Super!

Il suffit de copier tout le code source dans Unity pour vérifier si cela fonctionne. Le codage GIF de test prend 120 secondes. En mode multi-thread (oui, les threads dans Unity fonctionnent, l'essentiel est de ne pas toucher l'interface utilisateur) encoder un gif prend 180 secondes. Facepalm.

Google - il s'avère que le problème est courant. Selon des rapports similaires, le code algorithmique dans Unity s'exécute 10 à 20 fois plus lentement. Cela serait lié à une autre implémentation de la collecte des ordures et à la surcharge de l'éditeur brumeux. Dans les assemblages (Windows, Android), la situation est similaire. Dans l'assembly .exe, par exemple, il fonctionne légèrement plus rapidement, de 20%.

Question aux lecteurs - est-ce que je fais quelque chose de mal ou y a-t-il un problème?

Lien vers l'implémentation de GIF, qui sont intéressés: GitHub . La bibliothèque a été écrite sous C # version 6 et .NET 3.5 pour être compatible avec les anciennes versions de Unity. Le projet peut être basculé vers .NET 4.0, puis ThreadPool fonctionnera beaucoup plus rapidement.

UPD: Merci pour les commentaires! Tout s'est avéré un peu déroutant: les deux threads avec des verrous et une implémentation de courbe. J'ai donc préparé un cas simple - l'opération consistant à exécuter un tableau dans LINQ. A savoir, une telle opération est effectuée pendant la compression LZW (seulement il y a une vérification des clés dans le dictionnaire).

image

Nous exécutons dans l'application console - 5 ms.
Fonctionnant sous Unity - 2100 ms.

UPD: Une partie du problème se trouve dans LINQ. Par exemple, appeler Contains est plusieurs fois plus lent que Array.IndexOf. LINQ est génial, il fait gagner du temps et rend le code plus beau. Mais pas dans des tâches appliquées telles que le travail avec de grands tableaux d'objets. Cela s'applique à une implémentation LINQ spécifique dans Unity.

UPD: Optimisation de l'algorithme de compression LZW, suppression des clés de chaîne dans le dictionnaire. LINQ supprimé pour les grandes collections lorsque cela est possible. Je n'ai pas touché à l'implémentation du multithreading. Et tout a volé, même sur Android. Bien sûr, la différence entre la vitesse de l'application console reste, mais elle n'est pas si importante.

Un merci spécial à WNeZRoS pour son aide dans l'optimisation du code et à tous les participants à la discussion! Bien que la cause des freins dans LINQ reste non résolue.

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


All Articles