Performance de código em .NET: implementando testes com BenchmarkDotNet
Ao implementarmos aplicações iremos invariavelmente nos deparar com várias possibilidades de implementações, funcionalidades, recursos e mesmo frameworks para atender a uma demanda específica. Tais fatos são inerentes à própria atividade de desenvolvimento de software, cabendo a profissionais desta área encontrar meios para comparar as alternativas mais viáveis. Pesarão neste momento aspectos como o esforço de codificação, questões financeiras, a maturidade dos envolvidos em um projeto e, até mesmo, ganhos de performance em frações de tempo.
No que se refere à performance, é importante encontrar um meio de realizar comparações (benchmarkings) entre as opções existentes. E, obviamente, sem que tudo isso resulte em grandes malabarismos em termos de codificação.
A boa notícia dentro da plataforma .NET é que já contamos com uma alternativa para a geração de benchmarkings sofisticados, porém com um esforço relativamente pequeno: trata-se do projeto BenchmarkDotNet.
O BenchmarkDotNet é uma iniciativa open source mantida pela .NET Foundation e utilizada até mesmo por equipes de produto dentro da própria Microsoft! Empregando análises estatísticas, este projeto é capaz de retornar métricas como o tempo médio de execução de um código, o desvio padrão e a margem de erro do cálculo efetuado.
E aproveito esse espaço para um convite…
Dia 27/09 (segunda) às 21:00 — horário de Brasília — teremos mais um evento online e gratuito no canal Canal .NET.
Ao longo desta live abordarei dicas, truques e alternativas úteis para o desenvolvimento Back-End e de APIs REST com .NET 5, ASP.NET Core, Azure Functions. Ao longo da apresentação será coberto o uso de diferentes frameworks, serviços na nuvem, mensageria, bancos de dados e boas práticas de forma a facilitar e tornar mais dinâmica a implementação de soluções baseadas na plataforma .NET no seu dia a dia.
Teremos também algumas novidades do .NET 6 demonstradas na prática!
Para participar faça sua inscrição no link a seguir, a transmissão acontecerá via YouTube:
O exemplo de uso do BenchmarkDotNet demonstrado neste artigo foi disponibilizado no GitHub:
https://github.com/renatogroffe/DotNet5-BenchmarkDotNet-Testes-JSON_Serialization-V2
Para demonstrar a utilização do BenchmarkDotNet efetuarei uma comparação entre o mecanismo de serialização da biblioteca Newtonsoft e recursos que integram o namespace System.Text.Json. Para isto foram adicionados ao projeto os packages BenchmarkDotNet e Newtonsoft.Json:
A classe Regiao servirá de base para os testes com mecanismos de serialização:
Na próxima listagem temos o código da classe NewtonsoftVsSystemTextJson, em que foram definidos os métodos para testes:
- O método Setup foi marcado com o atributo GlobalSetup, indicando a utilização do mesmo para fins de inicialização (criação de um array de objetos do tipo Regiao);
- Já os métodos SerializeWithNewtonsoft (utilizando a serialização da biblioteca Newtonsoft) e SerializeWithSystemTextJson (com a serialização via recursos do namespace System.Text.Json) estão marcados com o atributo Benchmark, especificando com isto os testes a serem executados para a geração das análises.
Por fim, chegamos à implementação da classe Program. O método Run invocado a partir do tipo BenchmarkSwitcher no método Main permitirá que os testes sejam executados, tornando possível inclusive a passagem de parâmetros através da linha de comando:
A execução dos testes será por meio do comando dotnet run, utilizando configurações de Release e com o parâmetro --filter indicando quais testes a considerar (* para todos):
dotnet run -c Release --filter *
Que ao ser acionado mostrará que todos os benchmarkings serão executados:
E assim teremos um resultado similar ao da imagem a seguir (com o cálculo do número de iterações/execuções gerado automaticamente pelo BenchmarkDotNet por default, embora seja possível especificar um número na linha de comando). Podemos observar aqui a presença de valores estatísticos como média (Mean), mediana (Median), desvio padrão (StdDev) e margem de erro (Error):
Em um novo teste podemos informar como fitro *WithSystemTextJson, de maneira que apenas o benchmarking que avalia o uso do namespasce System.Text.Json seja disparado (as strings para filtros se baseiam na hierarquia namespace + classe + método):
dotnet run -c Release --filter *WithSystemTextJson
E com isto apenas o método SerializeWithSystemTextJson foi considerado no resultado final:
Essas execuções também geram arquivos com os resultados, com os mesmos disponibilizados no diretório BenchmarkDotNet.Artifacts:
Na próxima imagem estão alguns desses arquivos (gerados em formatos texto, HTML, CSV e Markdown):
O arquivo HTML traria uma visão similar à encontrada na próxima imagem, por exemplo: