Encontrei este artigo sobre Lazy
: Preguiça no C # 4.0 - Preguiçosa
Qual é a melhor prática para obter o melhor desempenho usando objetos Lazy? Alguém pode me indicar um uso prático em uma aplicação real? Em outras palavras, quando devo usá-lo?
c#
.net
lazy-evaluation
danyolgiax
fonte
fonte
get { if (foo == null) foo = new Foo(); return foo; }
. E existem zilhões de possíveis lugares para usá-lo ...get { if (foo == null) foo = new Foo(); return foo; }
não é seguro para threads, enquantoLazy<T>
é seguro para threads por padrão.Respostas:
Você costuma usá-lo quando deseja instanciar algo na primeira vez que é realmente usado. Isso atrasa o custo de criá-lo até se / quando for necessário, em vez de sempre incorrer no custo.
Geralmente, isso é preferível quando o objeto pode ou não ser usado e o custo de sua construção não é trivial.
fonte
Lazy<T>
. No entanto, para criar cada propriedade, estou fazendo uma interpolação linear (ou interpolação bilinear) que é bastante trivial, mas tem algum custo. (Você vai sugerir que eu ir e fazer minha própria experiência?)Você deve evitar o uso de Singletons, mas se precisar,
Lazy<T>
facilita a implementação de singletons preguiçosos e seguros para threads:fonte
Uma grande no mundo real exemplo de onde o carregamento lento vem a calhar é com do ORM (relação de objeto Mappers), como o Entity Framework e NHibernate.
Digamos que você tenha um cliente da entidade que possua propriedades para Nome, Número de telefone e Pedidos. Name e PhoneNumber são sequências regulares, mas Orders é uma propriedade de navegação que retorna uma lista de todos os pedidos que o cliente já fez.
Em geral, convém consultar todos os clientes e solicitar o nome e o número de telefone deles. Essa é uma tarefa muito rápida e simples, mas imagine que toda vez que você criou um cliente, ele automaticamente fez uma associação complexa para devolver milhares de pedidos. A pior parte é que você nem vai usar os pedidos, portanto é um completo desperdício de recursos!
Este é o local perfeito para carregamento lento, porque se a propriedade Order for preguiçosa, ela não buscará todos os pedidos do cliente, a menos que você realmente precise deles. Você pode enumerar os objetos Customer obtendo apenas seu Nome e Número de Telefone enquanto a propriedade Pedido estiver dormindo pacientemente, pronta para quando você precisar.
fonte
Db.Customers.Include("Orders")
. Isso fará com que a junção da ordem seja executada naquele momento, e não quando aCustomer.Orders
propriedade é usada pela primeira vez. O Lazy Loading também pode ser desativado através do DbContext.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: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.
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)
Resultados do Teste 2: Primeira obtenção (média de 20 propriedades)
Resultados do Teste 3: Segunda obtenção (média de 20 propriedades)
Observações
GetInterp
é mais rápido para instanciar como esperado, porque não está fazendo nada.InitLazy
é mais rápido instanciar do queInitInterp
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 porqueInitInterp
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 oGetInterp
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 queGetInterp
para obter uma propriedade pela primeira vez, enquantoInitInterp
é 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.fonte
lazy
tem que fazer alguma contabilidade extra,InitLazy
usaria mais memória do que as outras soluções. Ele também pode ter um impacto de desempenho menor em cada acesso, enquanto verifica se já possui um valor ou não; truques inteligentes poderiam remover essa sobrecarga, mas exigiria suporte especial na IL. (Haskell faz isso através de todos os valores preguiçoso uma chamada de função, uma vez que o valor é gerado, ele é substituído com uma função que retorna o valor de cada vez.)Apenas para apontar para o exemplo postado por Mathew
antes do Lazy nascer, teríamos feito assim:
fonte
Do MSDN:
Além da resposta de James Michael Hare, o Lazy fornece uma inicialização segura do seu valor. Dê uma olhada na entrada MSDN da enumeração LazyThreadSafetyMode que descreve vários tipos de modos de segurança de encadeamento para esta classe.
fonte
Você deve procurar neste exemplo para entender a arquitetura Lazy Loading
-> saída -> 0 1 2
mas se esse código não escrever "list.Value.Add (0);"
saída -> Valor não criado
fonte