C# — Value Types 60% mais rápido que References Types

Elvis Fernandes Dias
3 min readFeb 27, 2021

--

Na maioria das vezes que queremos representar algo do mundo real no código, partimos para uma abordagem utilizando classes. Não é comum encontrarmos essas representações como estruturas, mesmo quando são objetos anêmicos, sem nenhum tipo de comportamento. Mas será que vale a pena investirmos um pouco mais de tempo para definir qual a maneira mais eficiente de escrevermos nosso código, ao invés de seguirmos o padrão? Pois digo que sim, vale bastante a pena. Vamos ver o cenário abaixo.

Temos a necessidade de implementar o preenchimento de uma lista com alguns objetos, e depois realizar algumas consultas nessa lista. Os objetos são pequenos e sem comportamento algum. Este é um cenário bem comum de problemas que encontramos por aí. Vamos ao código para demonstrar os resultados e depois esclarecemos o porque das coisas.

Esse artigo será dividido em duas partes, a primeira com as implementações e os resultados obtidos, e posteriormente, num segundo artigo explicarei o motivo da diferença de comportamento entre as implementações.

Implementação com class

Vamos pensar que o objeto do cenário acima seja a classe Dimensao representada abaixo:

Vamos popular uma lista com alguns objetos e realizar a busca de um item que sabemos que não existe na lista e vamos mensurar essa execução.

Utilizando o pacote BenchmarkDotNet para realizar a medição, temos os resultados abaixo:

Implementação com struct

Agora ao invés de utilizarmos uma classe vamos utilizar uma estrutura, struct.

Vamos medir a inserção dos itens na lista e a consulta.

Resultado:

Podemos observar que já tivemos um ganho significativo. Saímos de um tempo de execução de 427 milissegundos, para 161 milissegundos. Mas podemos ver que o consumo de memória aumentou um pouco, de 155 MB para 187 MB. Isso se deve ao fato das struct serem armazenadas na Stack, mas o método Equals recebe um object, portanto temos o famoso boxing/unboxing para a conversão de um objeto valor para um objeto referência. Mas podemos contornar esse problema utilizando a interface IEquatable<T>.

Implementação com estrutura e IEquatable<T>

Implementando a interface IEquatable<T> a estrutura ficaria assim:

Na inserção e busca não teríamos diferença:

Medindo novamente, temos ganhos no tempo de execução e também na alocação de memória.

Observamos uma melhora significativa de tempo de execução, de 427 milissegundos para 136 milissegundos, e na alocação de memória, de 155MB para 96 MB.

Portanto, vale muito a pena analisarmos a melhor maneira de representarmos nossos objetos. Nem sempre a utilização do struct será a melhor opção, devemos sempre analisar os cenários e realizar as medições para essa conclusão.

Para entendermos melhor o motivo da diferença de desempenho e como podemos definir entre class ou struct, no próximo artigo vou abordar como o .net realiza o gerenciamento de memória pois isso é o core para entendermos a diferença de comportamento entre as implementações acima.

O código completo destes exemplos estão disponíveis em meu GitHub.

Muito obrigado e até mais ;)

--

--

Elvis Fernandes Dias

.Net Developer Graduado em Ciência da Computação e Pós Graduado em Engenharia de Software