Quanto costa la riflessione .NET?

Sento costantemente quanto sia pessimo il riflesso da usare. Mentre generalmente evito di riflettere e raramente trovo situazioni in cui è imansible risolvere il mio problema senza di esso, mi chiedevo …

Per coloro che hanno utilizzato la riflessione nelle applicazioni, hai misurato i risultati delle prestazioni e, è davvero così brutto?

È. Ma dipende da cosa stai cercando di fare.

Uso la reflection per caricare in modo dinamico gli assembly (plugins) e le sue prestazioni “penalty” non sono un problema, poiché l’operazione è qualcosa che faccio durante l’avvio dell’applicazione.

Tuttavia, se stai riflettendo all’interno di una serie di cicli nidificati con chiamate di riflessione su ognuno, direi che dovresti rivisitare il codice 🙂

Per operazioni “un paio di volte”, la riflessione è perfettamente accettabile e non si noterà alcun ritardo o problema con esso. È un meccanismo molto potente ed è anche usato da .NET, quindi non vedo perché non dovresti provarlo.

Nel suo talk The Performance of Everyday Things , Jeff Richter mostra che chiamare un metodo per la riflessione è circa 1000 volte più lento rispetto a chiamarlo normalmente.

Suggerimento di Jeff: se è necessario chiamare il metodo più volte, utilizzare reflection una volta per trovarlo, quindi assegnarlo a un delegato e quindi chiamare il delegato.

Le prestazioni di riflessione dipenderanno dall’implementazione (le chiamate ripetitive dovrebbero essere memorizzate nella cache, ad esempio: entity.GetType().GetProperty("PropName") ). Poiché la maggior parte della riflessione che vedo su base giornaliera viene utilizzata per popolare quadro da lettori di dati o altre strutture di tipi di repository, ho deciso di eseguire il benchmark delle prestazioni in modo specifico sulla reflection quando viene utilizzato per ottenere o impostare le proprietà di un object.

Ho ideato un test che ritengo sia corretto poiché memorizza nella cache tutte le chiamate ripetute e solo le volte la chiamata effettiva SetValue o GetValue. Tutto il codice sorgente per il test delle prestazioni è in bitbucket all’indirizzo: https://bitbucket.org/grenade/accessortest . L’esame è benvenuto e incoraggiato.

La conclusione cui sono giunto è che non è pratico e non fornisce miglioramenti delle prestazioni apprezzabili per rimuovere la riflessione in un livello di accesso ai dati che restituisce meno di 100.000 righe in un momento in cui l’implementazione della riflessione viene eseguita correttamente.

Grafico del tempo (y) rispetto al numero di entità popolate (x)

Il grafico sopra mostra l’output del mio piccolo benchmark e mostra che i meccanismi che superano la riflessione, lo fanno solo in modo evidente dopo il segno di 100.000 cicli. La maggior parte dei DAL restituisce solo centinaia o forse migliaia di righe alla volta e a questi livelli la riflessione si comporta bene.

La mia esperienza più pertinente è stata la scrittura di codice per confrontare due quadro di dati dello stesso tipo in un modello di object di grandi dimensioni in base alla proprietà. Ho funzionato, l’ho provato, ho corso come un cane, ovviamente.

Ero scoraggiato, poi da un giorno all’altro ho capito che senza cambiare la logica, avrei potuto usare lo stesso algoritmo per generare automaticamente metodi per fare il confronto ma accedere staticamente alle proprietà. Non ci è voluto assolutamente tempo per adattare il codice per questo scopo e ho avuto la possibilità di fare un confronto approfondito a livello di proprietà delle entity framework con codice statico che poteva essere aggiornato con un clic di un pulsante ogni volta che il modello dell’object cambiava.

Il mio punto è: nelle conversazioni con i colleghi poiché ho più volte sottolineato che il loro uso della riflessione potrebbe essere quello di generare automaticamente il codice da compilare piuttosto che eseguire operazioni di runtime e questo è spesso degno di considerazione.

Se non sei in un ciclo, non ti preoccupare.

Non in modo massiccio Non ho mai avuto un problema con lo sviluppo del desktop a meno che, come afferma Martin, lo stai usando in una posizione stupida. Ho sentito molte persone avere paure assolutamente irrazionali sulle sue prestazioni nello sviluppo desktop.

Nel Compatto Quadro (che di solito sono in), però, è praticamente un anatema e nella maggior parte dei casi dovrebbe essere evitato come la peste. Posso ancora cavarmela usando raramente, ma devo essere molto attento con la sua applicazione che è molto meno divertente. 🙁

È già abbastanza brutto che tu debba preoccuparti anche delle riflessioni fatte internamente dalle librerie .NET per un codice critico delle prestazioni.

Il seguente esempio è obsoleto – true al momento (2008), ma molto tempo fa era stato risolto nelle versioni CLR più recenti. La riflessione in generale è ancora una cosa alquanto costosa, però!

Caso in questione: non si dovrebbe mai utilizzare un membro dichiarato come “Oggetto” in un’istruzione di blocco (C #) / SyncLock (VB.NET) nel codice ad alte prestazioni. Perché? Poiché il CLR non può bloccare un tipo di valore, il che significa che deve eseguire un controllo del tipo di riflessione in fase di esecuzione per verificare se il proprio object è effettivamente un tipo di valore anziché un tipo di riferimento.

Come per tutte le cose in programmazione, devi bilanciare i costi delle prestazioni con qualsiasi vantaggio ottenuto. La riflessione è uno strumento inestimabile se usato con cura. Ho creato una libreria di mapping O / R in C # che utilizzava il reflection per eseguire i binding. Questo ha funzionato benissimo. La maggior parte del codice di riflessione è stata eseguita solo una volta, quindi qualsiasi risultato in termini di prestazioni è stato piuttosto limitato, ma i benefici sono stati notevoli. Se stavo scrivendo un nuovo algoritmo di ordinamento fandangled, probabilmente non userei il reflection, dal momento che probabilmente si ridimensionerebbe.

Apprezzo che non abbia risposto esattamente alla tua domanda qui. Il mio punto è che non ha molta importanza. Utilizzare la riflessione, se appropriato. È solo un’altra funzione linguistica che devi imparare come e quando usarla.

La riflessione può avere un impatto notevole sulle prestazioni se la si utilizza per la creazione frequente di oggetti. Ho sviluppato un’applicazione basata su Composite UI Application Block che si basa molto sulla riflessione. C’è stato un notevole degrado delle prestazioni legato alla creazione di oggetti tramite la riflessione.

Tuttavia nella maggior parte dei casi non ci sono problemi con l’uso della riflessione. Se l’unica necessità è di ispezionare alcuni assemblaggi, consiglierei Mono.Cecil che è molto leggero e veloce

La riflessione è costosa a causa dei numerosi controlli che il runtime deve eseguire ogni volta che si effettua una richiesta per un metodo che corrisponde a un elenco di parametri. Da qualche parte nel profondo, il codice esiste che esegue il loop su tutti i metodi per un tipo, ne verifica la visibilità, controlla il tipo restituito e controlla anche il tipo di ogni singolo parametro. Tutta questa roba costa tempo.

Quando si esegue internamente quel metodo, c’è un codice che fa qualcosa come controllare che tu abbia passato un elenco di parametri compatibile prima di eseguire il vero metodo di destinazione.

Se ansible, si consiglia sempre di memorizzare nella cache l’handle del metodo se ne verrà continuamente riutilizzato in futuro. Come tutti i buoni consigli di programmazione, spesso ha senso evitare di ripetersi. In questo caso sarebbe inutile cercare continuamente il metodo con determinati parametri e poi eseguirlo ogni volta.

Colpisci la fonte e guarda cosa sta facendo.

Come tutto, si tratta di valutare la situazione. In DotNetNuke c’è un componente abbastanza core chiamato FillObject che usa la riflessione per popolare oggetti da datarows.

Questo è uno scenario abbastanza comune e c’è un articolo su MSDN, Using Reflection to Bind Business Objects ai Controlli modulo ASP.NET che copre i problemi di prestazioni.

Prestazioni a parte, una cosa che non mi piace dell’uso della riflessione in quel particolare scenario è che tende a ridurre la capacità di comprendere il codice a una rapida occhiata che per me non sembra valga la pena quando si considera di perdere anche la compilazione sicurezza temporale rispetto a dataset fortemente tipizzati o qualcosa come LINQ to SQL .

Reflection non rallenta drasticamente le prestazioni della tua app. Potresti essere in grado di fare certe cose più velocemente non usando la reflection, ma se Reflection è il modo più semplice per ottenere alcune funzionalità, allora usalo. Puoi sempre refactoring il codice via da Reflection se diventa un problema di perf.

Penso che troverai che la risposta è, dipende. Non è un grosso problema se vuoi metterlo nella tua applicazione elenco delle attività. È un grosso problema se vuoi metterlo nella libreria di persistenza di Facebook.