Reflection: migliorare le performance

di Cristian Civera, in .NET 3.5,

La reflection è quello strumento che permette di interrogare a runtime i metadati di un assembly, analizzare i tipi, i membri ed invocare dinamicamente metodi, proprietà ed eventi. Il namespace System.Reflection c'è dal .NET Framework 1.0 e l'importanza di questo strumento si è fatta sempre più sentire, da una parte perché il framework è sempre più configurabile, dall'altra perché framework come NHibernate, Linq to SQL o Entity Framework lo pongono al centro di una questione importante: le performance.

Infatti la reflection è lenta. Tanto per dare un'idea con un semplice esempio, se dobbiamo creare un'istanza di una classe possiamo farlo chiamando il costruttore nel canonico modo oppure via reflection:

for (int x = 0; x < max; x++)
    p = new Product();
Type t = typeof(Product);
for (int x = 0; x < max; x++)
    p = Activator.CreateInstance(t);

Sulla mia macchina, con 10milioni di iterazioni ottengo questi tempi:

00:00:00.1892963
00:00:01.8751132

Ci mette ben 10 volte di più. Per non parlare di come impostare le proprietà:

for (int x = 0; x < max; x++)
    p.Description = "ciao";
PropertyInfo pi = typeof(Product).GetProperty("Description", BindingFlags.Public | BindingFlags.Instance);
for (int x = 0; x < max; x++)
    pi.SetValue(p, "ciao", null);

Escludendo i tempi di ricerca della proprietà con il metodo GetProperty, i dati sono:

00:00:00.1141661
00:00:16.4958957

Ovviamente questi sono dati indicativi ed è ovvio che il confronto con il codice compilato è ingiusto. Con la reflection abbiamo più dinamicità sui tipi e possiamo creare strumenti come gli ORM citati prima.

Come si possono quindi aumentare le performance? Una possibilità è quella di generare dinamicamente codice specifico per quel tipo e per certi membri. Infatti all'interno del namespace System.Reflection.Emit c'è tutto quello che serve per emettere IL al volo godendo delle stesse performance del codice compilato.
Una delle accuse che si fanno a NHibernate o LINQ to SQL è che sono lenti per via della reflection. In realtà, sia il primo (dalla versione v2) che il secondo, tolto il lavoro che fanno inizialmente di analisi delle classi e dei mapping, non usano reflection, ma a regime istanziano e impostano campi e proprietà con IL generato ad hoc. L'overhead semmai risiede nella gestione dei delegate e nel lavoro di mapping. In fondo se si pretende di lavorare indipendentemente dal database e instanziare tipi dinamicamente, non si può neanche pretendere di non pagare qualcosa per questa ulteriore stratificazione.

Comunque non è delle implementazioni degli ORM che mi interessa parlare, ma bensì di come generare codice IL. Lo mostrerò nel prossimo post dove farò vedere alcuni extension method che usano la System.Reflection.Emit e la classe LambaExpression per velocizzare le operazioni di reflection.

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Nella stessa categoria
I più letti del mese