Quão caro é o reflexo do .NET?

Eu sempre ouço como o reflexo ruim é usar. Enquanto eu geralmente evito a reflection e raramente encontro situações em que é impossível resolver o meu problema sem ele, eu estava pensando …

Para aqueles que usaram a reflection nas aplicações, você mediu os acertos de desempenho e, é realmente tão ruim?

Isto é. Mas isso depende do que você está tentando fazer.

Eu uso a reflection para carregar dinamicamente assemblies (plugins) e seu desempenho “penalidade” não é um problema, já que a operação é algo que eu faço durante a boot do aplicativo.

No entanto, se você estiver refletindo em uma série de loops nesteds com reflexos em cada um, eu diria que você deve revisitar seu código 🙂

Para operações de “um par de horas”, a reflection é perfeitamente aceitável e você não notará nenhum atraso ou problema com ela. É um mecanismo muito poderoso e até usado pelo .NET, então não vejo por que você não deveria tentar.

Em sua palestra The Performance of Everyday Things , Jeff Richter mostra que chamar um método por reflection é cerca de 1000 vezes mais lento do que chamá-lo normalmente.

Dica do Jeff: se você precisar chamar o método várias vezes, use a reflection uma vez para encontrá-lo, atribua-o a um delegado e chame o delegado.

O desempenho de reflection dependerá da implementação (as chamadas repetitivas devem ser armazenadas em cache, por exemplo: entity.GetType().GetProperty("PropName") ). Como a maior parte da reflection que vejo diariamente é usada para preencher entidades de leitores de dados ou outras estruturas de tipo de repository, decidi fazer benchmark de desempenho especificamente na reflection quando ele é usado para obter ou definir propriedades de objects.

Eu criei um teste que acho justo, pois ele armazena em cache todas as chamadas repetidas e apenas as vezes a chamada Real SetValue ou GetValue. Todo o código-fonte para o teste de desempenho está em bitbucket em: https://bitbucket.org/grenade/accessortest . O escrutínio é bem-vindo e incentivado.

A conclusão a que cheguei é que não é prático e não fornece melhorias de desempenho visíveis para remover a reflection em uma camada de access a dados que está retornando menos de 100.000 linhas por vez quando a implementação da reflection é bem feita.

Gráfico do tempo (y) em relação ao número de entidades preenchidas (x)

O gráfico acima demonstra a saída do meu pequeno benchmark e mostra que os mecanismos que superam a reflection, só o fazem notavelmente após a marca de 100.000 ciclos. A maioria dos DALs só retorna várias centenas ou talvez milhares de linhas por vez e nesses níveis a reflection funciona bem.

Minha experiência mais pertinente foi escrever código para comparar quaisquer duas entidades de dados do mesmo tipo em um grande propriedade de modelo de object. Peguei, tentei, corri como um cachorro, obviamente.

Fiquei desanimado, e durante a noite percebi que, sem alterar a lógica, eu poderia usar o mesmo algoritmo para gerar automaticamente methods para fazer a comparação, mas acessando estaticamente as propriedades. Não demorou muito tempo para adaptar o código para essa finalidade e tive a capacidade de fazer uma comparação profunda de propriedade de entidades com código estático que poderia ser atualizado com o clique de um botão sempre que o modelo de object fosse alterado.

Meu ponto é: Em conversas com colegas desde que eu tenho várias vezes apontado que seu uso de reflection poderia ser para autogerar o código para compilar ao invés de executar operações de tempo de execução e isso geralmente vale a pena considerar.

Se você não estiver em um loop, não se preocupe com isso.

Não massivamente. Eu nunca tive um problema com isso no desenvolvimento de desktop, a menos que, como afirma Martin, você esteja usando em um local bobo. Eu ouvi muitas pessoas têm medos totalmente irracionais sobre o seu desempenho no desenvolvimento de desktop.

No Compact Framework (no qual normalmente estou), é praticamente anátema e deve ser evitado como a peste na maioria dos casos. Eu ainda posso usá-lo com pouca frequência, mas eu tenho que ter muito cuidado com a sua aplicação, que é muito menos divertida. 🙁

É ruim o suficiente que você tenha que se preocupar mesmo com a reflection feita internamente pelas bibliotecas .NET em busca de código crítico para o desempenho.

O exemplo a seguir é obsoleto – verdade na época (2008), mas há muito tempo corrigido nas versões mais recentes do CLR. Em geral, a reflection ainda é algo um tanto custoso!

Caso em questão: você nunca deve usar um membro declarado como “Objeto” em uma instrução de bloqueio (C #) / SyncLock (VB.NET) em código de alto desempenho. Por quê? Porque o CLR não pode bloquear em um tipo de valor, o que significa que ele tem que fazer uma verificação de tipo de reflection em tempo de execução para ver se o seu object é realmente um tipo de valor em vez de um tipo de referência.

Como todas as coisas na programação, você precisa equilibrar o custo de desempenho com qualquer benefício obtido. A reflection é uma ferramenta inestimável quando usada com cuidado. Eu criei uma biblioteca de mapeamento O / R em C # que usou reflection para fazer as ligações. Isso funcionou fantasticamente bem. A maior parte do código de reflection foi executada apenas uma vez, portanto, qualquer impacto no desempenho foi muito pequeno, mas os benefícios foram grandes. Se eu estivesse escrevendo um novo algoritmo de ordenação, eu provavelmente não usaria a reflection, pois ela provavelmente seria mal dimensionada.

Compreendo que não respondi exatamente a sua pergunta aqui. Meu ponto é que isso realmente não importa. Use a reflection, quando apropriado. É apenas outro recurso de idioma que você precisa aprender como e quando usar.

O reflexo pode ter um impacto perceptível no desempenho se você usá-lo para a criação freqüente de objects. Eu desenvolvi uma aplicação baseada em Composite UI Application Block, que depende muito da reflection. Houve uma notável degradação do desempenho relacionada à criação de objects via reflection.

No entanto, na maioria dos casos, não há problemas com o uso da reflection. Se sua única necessidade é inspecionar alguma assembly eu recomendaria Mono.Cecil que é muito leve e rápido

A reflection é dispendiosa por causa das muitas verificações que o tempo de execução deve fazer sempre que você faz uma solicitação para um método que corresponda a uma lista de parâmetros. Em algum lugar lá no fundo, existe um código que faz um loop em todos os methods para um tipo, verifica sua visibilidade, verifica o tipo de retorno e também verifica o tipo de cada parâmetro. Tudo isso custa tempo.

Quando você executa esse método internamente, há algum código que faz coisas como verificar se você passou por uma lista de parâmetros compatível antes de executar o método de destino real.

Se possível, é sempre recomendável que um armazene em cache o identificador do método, se ele for continuamente reutilizá-lo no futuro. Como todas as boas dicas de programação, muitas vezes faz sentido evitar se repetir. Nesse caso, seria um desperdício procurar continuamente o método com determinados parâmetros e executá-lo a cada vez.

Dê uma olhada na fonte e dê uma olhada no que está sendo feito.

Como em tudo, trata-se de avaliar a situação. No DotNetNuke há um componente central chamado FillObject que usa reflection para preencher objects de datarows.

Esse é um cenário bastante comum e há um artigo no MSDN, Usando Reflexão para Vincular Objetos de Negócios aos Controles de Formulário do ASP.NET, que abrange os problemas de desempenho.

Desempenho à parte, uma coisa que eu não gosto em usar reflection nesse cenário em particular é que ele tende a reduzir a capacidade de entender o código rapidamente, o que para mim não parece valer o esforço quando você considera que também perde a compilation tempo de segurança em oposição a conjuntos de dados fortemente tipados ou algo como LINQ to SQL .

O reflexo não diminui drasticamente o desempenho do seu aplicativo. Você pode ser capaz de fazer certas coisas mais rapidamente, não usando reflection, mas se o Reflection for a maneira mais fácil de obter alguma funcionalidade, então use-a. Você sempre pode refatorar seu código para longe do Reflection se ele se tornar um problema no perf.

Eu acho que você vai achar que a resposta é, depende. Não é um grande problema se você quiser colocá-lo em seu aplicativo de lista de tarefas. É um grande negócio se você quiser colocá-lo na biblioteca de persistência do Facebook.