Tenho um programa que exige desempenho rápido. Em um de seus loops internos, preciso testar o tipo de um objeto para ver se ele herda de uma determinada interface.
Uma maneira de fazer isso seria com a funcionalidade de verificação de tipo integrada do CLR. O método mais elegante provavelmente sendo a palavra-chave 'é':
if (obj is ISpecialType)
Outra abordagem seria dar à classe base minha própria função virtual GetType () que retorna um valor de enum predefinido (no meu caso, na verdade, eu só preciso de um bool). Esse método seria rápido, mas menos elegante.
Ouvi dizer que existe uma instrução IL especificamente para a palavra-chave 'é', mas isso não significa que ela executa rapidamente quando traduzida para o assembly nativo. Alguém pode compartilhar alguns insights sobre o desempenho de 'é' em relação ao outro método?
ATUALIZAÇÃO: Obrigado por todas as respostas informadas! Parece que alguns pontos úteis estão espalhados entre as respostas: O ponto de Andrew sobre 'é' executar um elenco automaticamente é essencial, mas os dados de desempenho coletados por Binary Worrier e Ian também são extremamente úteis. Seria ótimo se uma das respostas fosse editada para incluir todas essas informações.
fonte
Respostas:
O uso
is
pode prejudicar o desempenho se, depois de verificar o tipo, você lançar para aquele tipo.is
na verdade, converte o objeto para o tipo que você está verificando, portanto, qualquer conversão subsequente é redundante.Se você vai lançar de qualquer maneira, aqui está uma abordagem melhor:
fonte
as
executa basicamente a mesma operação queis
(ou seja, a verificação de tipo). A única diferença é que ele retorna emnull
vez defalse
.Estou com Ian , você provavelmente não quer fazer isso.
No entanto, só para você saber, há muito pouca diferença entre os dois, mais de 10.000.000 de iterações
Eu pessoalmente não resolveria esse problema dessa maneira, mas se fosse forçado a escolher um método seria a verificação de IS embutida, a diferença de desempenho não vale a pena considerar a sobrecarga de codificação.
Minhas classes base e derivadas
JubJub: Conforme solicitado, mais informações sobre os testes.
Eu executei os dois testes de um aplicativo de console (uma compilação de depuração), cada teste se parece com o seguinte
Correndo na liberação, obtenho uma diferença de 60 - 70 ms, como Ian.
Atualização adicional - 25 de outubro de 2012
Depois de alguns anos longe, percebi algo sobre isso, o compilador pode escolher omitir
bool b = a is MyClassB
no lançamento porque b não é usado em lugar nenhum.Este código. . .
. . . mostra consistentemente o
is
verificação chegando em aproximadamente 57 milissegundos e a comparação de enum chegando em 29 milissegundos.NB eu ainda prefiro o
is
cheque, a diferença é muito pequena para me preocuparfonte
is
operadora esteja causando, e que ouvir falar de projetar e codificar em torno dais
operadora custará uma fortuna em qualidade de código e, no final das contas, terá desempenho autodestrutivo também. Neste caso, mantenho minha declaração. O operador 'é' nunca será o problema com o desempenho do seu tempo de execução.Ok, então eu estava conversando sobre isso com alguém e decidi testar mais. Pelo que eu posso dizer, o desempenho de
as
eis
é muito bom, em comparação com o teste de seu próprio membro ou função para armazenar informações de tipo.Eu usei
Stopwatch
, que acabei de descobrir que pode não ser a abordagem mais confiável, então também tenteiUtcNow
. Mais tarde, também tentei a abordagem de tempo do processador, que parece semelhante aUtcNow
incluir tempos de criação imprevisíveis. Eu também tentei tornar a classe base não abstrata sem virtuais, mas não pareceu ter um efeito significativo.Eu executei isso em um Quad Q6600 com 16 GB de RAM. Mesmo com iterações de 50mil, os números ainda oscilam em torno de +/- 50 ou mais milissegundos, então eu não interpretaria muito as pequenas diferenças.
Foi interessante ver que o x64 foi criado mais rápido, mas executado como / é mais lento que o x86
x64 Modo de liberação:
Cronômetro:
As: 561ms
Is: 597ms
Propriedade básica: 539ms
Campo base: 555ms
Campo RO base: 552ms
Teste GetEnumType ()
virtual : 556ms Teste IsB virtual (): 588ms
Tempo de criação: 10416ms
UtcNow:
As: 499ms
Is: 532ms
Propriedade de base: 479ms
Campo de base: 502ms
Campo de RO base: 491ms
Virtual GetEnumType (): 502ms
Virtual bool IsB (): 522ms
Tempo de criação: 285ms (Este número parece não confiável com UtcNow. Eu também recebo 109ms e 806ms.)
x86 Modo de liberação:
Cronômetro:
As: 391ms
Is: 423ms
Propriedade básica: 369ms
Campo base: 321ms
Campo RO de base: 339ms
Teste GetEnumType ()
virtual : 361ms Teste IsB virtual (): 365ms
Tempo de criação: 14106ms
UtcNow:
As: 348ms
Is: 375ms
Propriedade de base: 329ms
Campo de base: 286ms
Campo de RO base: 309ms
Virtual GetEnumType (): 321ms
Virtual bool IsB (): 332ms
Tempo de Criação: 544ms (Este número parece não confiável com UtcNow.)
Aqui está a maior parte do código:
fonte
Andrew está correto. Na verdade, com a análise de código, isso é relatado pelo Visual Studio como um elenco desnecessário.
Uma ideia (sem saber o que você está fazendo é meio que um tiro no escuro), mas sempre fui aconselhado a evitar checar assim e, em vez disso, ter outra aula. Então, ao invés de fazer algumas verificações e ter ações diferentes dependendo do tipo, faça a classe saber como se processar ...
por exemplo, Obj pode ser ISpecialType ou IType;
ambos têm um método DoStuff () definido. Para IType, ele pode apenas retornar ou fazer coisas personalizadas, enquanto ISpecialType pode fazer outras coisas.
Isso remove completamente qualquer projeção, torna o código mais limpo e mais fácil de manter, e a classe sabe como fazer suas próprias tarefas.
fonte
Eu fiz uma comparação de desempenho em duas possibilidades de comparação de tipo
O resultado é: Usar "é" é cerca de 10 vezes mais rápido !!!
Resultado:
Hora para comparação de tipo: 00: 00: 00.456
Hora da comparação de is: 00: 00: 00.042
Meu código:
fonte
O ponto que Andrew Hare fez sobre a perda de desempenho quando você executa a
is
verificação e, em seguida, o elenco era válido, mas no C # 7.0 podemos fazer é verificar a correspondência de padrão de bruxa para evitar elenco adicional mais tarde:Além disso, se você precisar verificar entre vários tipos, as construções de correspondência de padrões C # 7.0 agora permitem que você faça
switch
nos tipos:Você pode ler mais sobre correspondência de padrões em C # na documentação aqui .
fonte
OneOf<T...>
mas elas têm grandes deficiências) .Caso alguém esteja se perguntando, fiz testes no motor Unity 2017.1, com scripting runtime versão .NET4.6 (Experimantal) em um notebook com CPU i5-4200U. Resultados:
Average Relative To Local Call LocalCall 117.33 1.00 is 241.67 2.06 Enum 139.33 1.19 VCall 294.33 2.51 GetType 276.00 2.35
Artigo completo: http://www.ennoble-studios.com/tuts/unity-c-performance-comparison-is-vs-enum-vs-virtual-call.html
fonte
Sempre fui aconselhado a evitar checar assim e, em vez disso, ter outra aula. Então, ao invés de fazer algumas verificações e ter ações diferentes dependendo do tipo, faça a classe saber como se processar ...
por exemplo, Obj pode ser ISpecialType ou IType;
ambos têm um método DoStuff () definido. Para IType, ele pode apenas retornar ou fazer coisas personalizadas, enquanto ISpecialType pode fazer outras coisas.
Isso remove completamente qualquer projeção, torna o código mais limpo e mais fácil de manter, e a classe sabe como fazer suas próprias tarefas.
fonte