top of page

C# struct x class benchmark

  • Foto do escritor: Gerson Reis
    Gerson Reis
  • 28 de jan. de 2021
  • 4 min de leitura

Atualizado: 6 de jun. de 2022



Olá, escrevi este post de forma bem breve para relatar um desafio que como um desenvolvedor tenho no dia a dia. Vou levar em consideração que você tenha alguma intimidade com c# e gerenciamento de memória pois não me aprofundei em detalhes, vou focar mais na dica simples e rápida e junto a ela tentar de alguma forma representar o ganho com alguns códigos e imagens. Bom, recentemente tive uma situação no o time em que trabalho, um integrante precisava reduzir o processamento de uma azure function, resumidamente o que ela faz é: obtém alguns dados no cosmosDB e em alguns casos faz uma leitura/análise e retorna os mesmos. O que acontece é que precisamos baixar o custo desse processamento, apesar de a tarefa não estar atribuída a mim tentei buscar alternativas e sugerir a pessoa que estava com a missão.


Curiosamente acreditei que talvez fazer o uso da struct que muitas vezes é esquecida pelos devs(inclusive por mim) poderia nos ajudar em alguma coisa, e sim ela ajuda! Fiz um teste usando uma ferramenta de benchmark para ver a diferença e deixar o resultado palpável através de números.


Segue o código que escrevi para testar e na sequência os resultados:



public struct CarroStruct

{

public CarroStruct(string name, int value)

{

Name = name;

this.value = value;

}


public string Name { get; set; }

public int value { get; set; }

}


public class CarroClass

{

public CarroClass(string name, int value)

{

Name = name;

this.value = value;

}

public string Name { get; set; }

public int value { get; set; }

}


[MemoryDiagnoser]

public class DataTableMonitor

{

[Benchmark]

public bool TestStruct()

{

CarroStruct[] cars = new CarroStruct[10]

{

new CarroStruct("red", 2020),

new CarroStruct("blue", 2015),

new CarroStruct("orange", 2014),

new CarroStruct("black", 2015),

new CarroStruct("orange", 2019),

new CarroStruct("red", 2010),

new CarroStruct("blue", 2019),

new CarroStruct("blue", 2016),

new CarroStruct("red", 2012),

new CarroStruct("black", 2017)

};


foreach (var item in cars) { }


return true;

}


[Benchmark]

public bool TestClass()

{

CarroClass[] cars = new CarroClass[10]

{

new CarroClass("red", 2020),

new CarroClass("blue", 2015),

new CarroClass("orange", 2014),

new CarroClass("black", 2015),

new CarroClass("orange", 2019),

new CarroClass("red", 2010),

new CarroClass("blue", 2019),

new CarroClass("blue", 2016),

new CarroClass("red", 2012),

new CarroClass("black", 2017)

};


foreach (var item in cars) { }


return true;

}


Acima temos uma strcut, uma classe e na sequencia dois testes para rodar com benchmark, cada um teste cria uma coleção de struct e lê a mesma com um foreach e o outro testa faz a mesma coisa mas no lugar da struct é uma classe. Simples e direto.



Logo na sequencia já coloquei o teste pra funcionar e obtive os seguintes resultados:




Se você não soube interpretar os dados lhe recomendo ler este artigo aqui antes, pois nele fiz alguns exemplos e mostrei uma forma simples de ver métricas básicas com o BenchmarkDotNet.


Baixamos para menos de a metade o consumo de recursos, agora apenas 184 dos 424 que utilizavamos.


Incrível não? Algo realmente muito simples e um recurso que está aí para ser utilizado, lembre-se de não exagerar e querer usar em tudo porque ela tem seus contras também.


Seguindo...


Quando usamos coleções de estruturas de dados os recursos ficam um pouco mais reduzidos quando comparados a uma coleção de objetos.


O que muda?


O que muda é que quando temos uma coleção de objetos, essa lista/array tem apenas a referência do objeto na memória e isso nos permite ler um a um e alterar os valores do mesmo caso esse seja o objetivo. Temos liberdade de alterar os valores do item enquanto interagimos com a lista e na memoria as coisas se organizam mais ou menos assim:



Já quando temos uma coleção de estruturas de dados, a estrutura em si está dentro da coleção e é tudo uma coisa só dentro do heap. O formato que o c# trabalhar não nos permite mudar o valor de uma coleção quando a mesma está alocada para leitura, ou seja: Se você estiver dentro de um simples foreach na sua coleção de struct não conseguirás mudar o valor de nenhuma delas pois o foreach está utilizando a estrutura como um todo.


então uma coleção de struct fica algo parecido com isto:



Então tentar alterar o dados de uma struct que pertence a uma coleção no mesmo momento em que você está percorrendo a mesma, seria igual quando você está fazendo um foreach ou um for em um ICollection<T> e dentro deste "for" em determinada condição você tenta fazer um “collection.Remove(item)”. A tecnologia não vai permitir isto.


Exemplo da situação:



ICollection<CarroClass> cars = new List<CarroClass>();

cars.Add(new CarroClass("red", 2020));

foreach (var item in cars)

{

cars.Remove(item);

}


Podes escrever o mesmo e executar para entenderes melhor o que acontece. Resumidamente: Para algumas situações use struct, mas para utiliza-las entenda como ela se comporta na memoria, não só ela mas como todos os outros recursos é importante entender como se comportam e quais "efeitos colaterais" cada um cria no ambiente da sua aplicação, é isso que vai lhe diferenciar dos outros 95% dos desenvolvedores.


Espero de alguma forma ter agregado algo para seu dia, se você tem algum ponto a melhorar neste post por favor me avise, estamos aqui para evoluir juntos o que está escrito aqui é o que sei até o momento mas acredito que sempre tenho a aprender muito e mudar minha opinião sobre muitas coisas, caso achares que podes me ajudar ficarei grato!


Keep learning and até a proxima.


Imagens retiradas deste post.



Comments


  • Github
  • LinkedIn
  • Instagram
  • Facebook

THE DEV TRACK

© 2020 by The Dev Track

Contato

Pergunte alguma coisa

Obrigado por colaborar!

bottom of page