A unidade é lenta? Cuidado LINQ

Costuma-se dizer que a unidade é lenta. Mas quanto? Estou desenvolvendo um aplicativo Pixel Studio, este é um editor de pixel art. Para ele, escrevi minha implementação do formato GIF. A operação que consome mais tempo é a codificação GIF, ou seja, o algoritmo de compactação LZW. Vamos ver como o Unity lida com isso.

imagem

Em primeiro lugar, vou esclarecer imediatamente por que tive que escrever minha própria biblioteca. Sim, tudo é simples, a biblioteca System.Windows.Media.Imaging, que contém o maravilhoso GifBitmapEncoder, não pode ser conectada ao Unity. Portanto, li artigos no hub sobre GIF, peguei a especificação e fiz meu GIF com blackjack.

Obviamente, comecei a escrever e depurar a biblioteca no Visual Studio, em um aplicativo de console. Omitirei esse momento, foi chato e demorado para depurar, levou 3 dias. Em tais algoritmos, eu não tinha experiência, geralmente fazendo jogos. Ok, a biblioteca está pronta. Por exemplo, um GIF "pesado" de teste em 200 quadros e uma resolução de 256x256 é codificado em 15 segundos (Ryzen 7, é claro, no meu hardware). Pensei bastante e, com a mão do cavalheiro, paralelizei o processo de compressão (como o Unity deveria suportar threads). O GIF de teste foi codificado em 5 segundos. Ótimo!

Basta copiar todo o código-fonte no Unity para verificar se funciona. A codificação GIF de teste leva 120 segundos. No modo multiencadeado (sim, os encadeamentos no Unity funcionam, o principal é não tocar na interface do usuário) a codificação de um gif leva 180 segundos. Facepalm.

Google - o problema, ao que parece, é comum. De acordo com relatórios semelhantes, o código algorítmico no Unity é executado 10 a 20 vezes mais lento. Supostamente, isso está conectado a outra implementação de coleta de lixo e ao Overhead do Editor nebuloso. Nas assembléias (Windows, Android) a situação é semelhante. No assembly .exe, por exemplo, ele funciona um pouco mais rápido, em 20%.

Pergunta aos leitores - estou fazendo algo errado ou há algum problema?

Link para a implementação do GIF, que está interessado: GitHub . A biblioteca foi escrita em C # versão 6 e .NET 3.5 para ser compatível com versões mais antigas do Unity. O projeto pode ser alternado para o .NET 4.0, e o ThreadPool funcionará muito mais rápido.

UPD: Obrigado pelos comentários! Tudo ficou um pouco confuso: ambos os threads com bloqueios e uma implementação de curva. Então, preparei um caso simples - a operação de executar uma matriz no LINQ. Nomeadamente, essa operação é executada durante a compactação LZW (somente há uma verificação de chave no dicionário).

imagem

Executamos no aplicativo de console - 5 ms.
Em execução no Unity - 2100 ms.

UPD: Parte do problema é encontrada no LINQ. Por exemplo, chamar Contains é várias vezes mais lento que Array.IndexOf. O LINQ é ótimo, economiza tempo e torna o código mais bonito. Mas não em tarefas aplicadas, como trabalhar com grandes matrizes de objetos. Isso se aplica a uma implementação LINQ específica no Unity.

UPD: Otimizou o algoritmo de compactação LZW, removeu as chaves de string no dicionário. Removido o LINQ para grandes coleções sempre que possível. Não toquei na implementação do multithreading. E tudo voou, mesmo no Android. Obviamente, a diferença entre a velocidade do aplicativo do console permanece, mas não é tão significativa.

Agradecimentos especiais ao WNeZRoS pela ajuda na otimização do código e a todos os participantes da discussão! Embora a causa dos freios no LINQ permaneça sem solução.

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


All Articles