Estou pensando em usar Lazy<T>
propriedades para ajudar a melhorar o desempenho do meu próprio código (e para aprender um pouco mais sobre ele). Eu vim aqui procurando respostas sobre quando usá-lo, mas parece que em todos os lugares que vou, há frases como:
Use a inicialização lenta para adiar a criação de um objeto grande ou com muitos recursos, ou a execução de uma tarefa com muitos recursos, principalmente quando essa criação ou execução pode não ocorrer durante a vida útil do programa.
da classe Lazy <T> do MSDN
Fico um pouco confuso porque não sei onde traçar a linha. Por exemplo, considero a interpolação linear como uma computação bastante rápida, mas se não for necessário, a inicialização lenta pode me ajudar a evitar fazê-lo e vale a pena?
No final, decidi tentar meu próprio teste e pensei em compartilhar os resultados aqui. Infelizmente, não sou realmente especialista em fazer esse tipo de teste e, por isso, fico feliz em receber comentários que sugerem melhorias.
Descrição
No meu caso, eu estava particularmente interessado em ver se o Lazy Properties poderia ajudar a melhorar uma parte do meu código que interpolou bastante (a maior parte não sendo usada) e, por isso, criei um teste que comparou três abordagens.
Criei uma classe de teste separada com 20 propriedades de teste (vamos chamá-las de propriedades t) para cada abordagem.
- Classe GetInterp: executa interpolação linear toda vez que uma propriedade t é obtida.
- Classe InitInterp: Inicializa as propriedades t executando a interpolação linear para cada uma no construtor. O get apenas retorna um duplo.
- Classe InitLazy: define as propriedades t como propriedades Lazy, para que a interpolação linear seja executada uma vez quando a propriedade for obtida pela primeira vez. Obtimentos subsequentes devem apenas retornar um dobro já calculado.
Os resultados do teste são medidos em ms e são a média de 50 instanciações ou 20 propriedades obtidas. Cada teste foi então executado 5 vezes.
Resultados do teste 1: Instanciação (média de 50 instanciações)
Class 1 2 3 4 5 Avg %
------------------------------------------------------------------------
GetInterp 0.005668 0.005722 0.006704 0.006652 0.005572 0.0060636 6.72
InitInterp 0.08481 0.084908 0.099328 0.098626 0.083774 0.0902892 100.00
InitLazy 0.058436 0.05891 0.068046 0.068108 0.060648 0.0628296 69.59
Resultados do Teste 2: Primeira obtenção (média de 20 propriedades)
Class 1 2 3 4 5 Avg %
------------------------------------------------------------------------
GetInterp 0.263 0.268725 0.31373 0.263745 0.279675 0.277775 54.38
InitInterp 0.16316 0.161845 0.18675 0.163535 0.173625 0.169783 33.24
InitLazy 0.46932 0.55299 0.54726 0.47878 0.505635 0.510797 100.00
Resultados do Teste 3: Segunda obtenção (média de 20 propriedades)
Class 1 2 3 4 5 Avg %
------------------------------------------------------------------------
GetInterp 0.08184 0.129325 0.112035 0.097575 0.098695 0.103894 85.30
InitInterp 0.102755 0.128865 0.111335 0.10137 0.106045 0.110074 90.37
InitLazy 0.19603 0.105715 0.107975 0.10034 0.098935 0.121799 100.00
Observações
GetInterp
é mais rápido para instanciar como esperado, porque não está fazendo nada. InitLazy
é mais rápido instanciar do que InitInterp
sugerir que a sobrecarga na configuração de propriedades preguiçosas é mais rápida que meu cálculo de interpolação linear. No entanto, estou um pouco confuso aqui porque InitInterp
deveria estar fazendo 20 interpolações lineares (para configurar suas propriedades t), mas são necessários apenas 0,09 ms para instanciar (teste 1), em comparação com o GetInterp
que leva 0,28 ms para fazer apenas uma interpolação linear na primeira vez (teste 2) e 0,1 ms para fazê-lo na segunda vez (teste 3).
Demora InitLazy
quase duas vezes mais do que GetInterp
para obter uma propriedade pela primeira vez, enquanto InitInterp
é a mais rápida, porque preencheu suas propriedades durante a instanciação. (Pelo menos é o que deveria ter sido feito, mas por que o resultado da instanciação foi muito mais rápido que uma única interpolação linear? Quando exatamente essas interpolações são realizadas?)
Infelizmente, parece que há alguma otimização automática de código em meus testes. Deve levar GetInterp
o mesmo tempo para obter uma propriedade na primeira vez que ocorre na segunda vez, mas está sendo exibido duas vezes mais rápido. Parece que essa otimização também está afetando as outras classes, já que elas estão demorando quase o mesmo tempo para o teste 3. No entanto, essas otimizações também podem ocorrer no meu próprio código de produção, o que também pode ser uma consideração importante.
Conclusões
Embora alguns resultados sejam os esperados, também existem resultados inesperados muito interessantes, provavelmente devido a otimizações de código. Mesmo para as classes que parecem estar fazendo muito trabalho no construtor, os resultados da instanciação mostram que eles ainda podem ser muito rápidos de criar, em comparação com a obtenção de uma propriedade dupla. Embora os especialistas nesse campo possam comentar e investigar mais detalhadamente, meu sentimento pessoal é que preciso fazer esse teste novamente, mas no meu código de produção, a fim de examinar que tipo de otimização também pode estar ocorrendo lá. No entanto, espero que esse InitInterp
seja o caminho a seguir.
get { if (foo == null) foo = new Foo(); return foo; }
. E existem zilhões de possíveis lugares para usá-lo ...