Abbiamo parlato di caspol.exe, di sn.exe e di
gacutil.exe. Un altro tool incluso in .NET è ngen.exe.
Quest'ultimo permette di compilare un assembly in codice nativo, allo scopo di
(poterne) migliorarne le performance durante la sua esecuzione. Normalmente,
come ben sappiamo, tutte le volte che generiamo un assembly con .NET, il file
EXE o la DLL sono scritti in IL (intermediate
language), il quale viene a sua volta tradotto in codice x86 tramite il
meccanismo di compilazione JIT (just-in-time).
Quando un assembly viene eseguito,
quindi, il framework deve intervenire, per compilarlo e finalmente eseguirlo.
Ovviamente, le prestazioni potrebbero risentirne, dato che c'è tutta una fase che il framework
deve completare prima di poter vedere qualcosa sullo schermo (parlando
banalmente di un'applicazione Windows Forms). Questo è almeno parzialmente
risolvibile con il tool ngen.exe. Non ho mai fatto un prova reale
sul campo, quindi non so dirvi che tipo di guadagno c'è veramente. Però intanto
vediamo come usare ngen, poi magari ne parleremo.
Compilare un assembly con
ngen.exe
ngen.exe è sicuramente il tool con la sintassi più semplice
(escludendo dir, cd, del e qualche altro comando DOS...
dai...sto scherzando). Per generare l'immagine nativa di un assembly
basta infatti dare un comando simile al seguente:
ngen ReadPerformance.exe
Basta semplicemente specificare il nome dell'assembly
(attenzione, senza path, bisogna essere nella directory giusta) su
cui si vuole lavorare. Nulla di più. Già, ok, ma cosa abbiamo veramente fatto
con questo comando? Inizialmente, da ignorante, mi aspettavo di vedere il mio
EXE modificato, cambiato in qualche modo. Pensavo avvenisse una sorta di
compilazione che cambiasse radicalmente il mio assembly: poi, ho visto che la
dimensione del file, e la sua data/ora di ultima modifica erano rimaste le
stesse. Dov'è finito il mio assembly compilato in
native code?
Per capire meglio l'uso e lo scopo di ngen.exe ho dato più di una lettura a questa pagina su MSDN. C'è
una riga, proprio all'inizio, che mi è sfuggita diverse volte e che alla fine mi ha risolto la
questione: "The native image cache is a reserved area of the global assembly
cache". Ah, adesso ho
capito. Il mio assembly è finito nella GAC. Effettivamente, se provate a dare
un'occhiata alla directory C:\Winnt\Assembly ecco il nostro assembly,
denominato ReadPerformance, indicato come
Native Images. Quindi, se noi provassimo a lanciare il nostro assembly dalla
solita directory bin\Debug, in realtà il runtime di .NET si accorgebbe che
nella GAC c'è un assembly in native-code, e quindi
preferirebbe l'esecuzione di quest'ultimo.
Un'annotazione (che mi è costata
qualche punticino negli ultimi test che ho fatto): ricordiamoci che per poter
inserire un assembly nella GAC, dobbiamo avere diritti amministrativi sul PC
(quindi, quando usiamo gacutil.exe o, appunto,
ngen.exe).
Ci sono i pro, ma ci sono anche i contro
Ok, abbiamo
capito che ngen.exe può essere molto utile per velocizzare l'esecuzione di una
nostra applicazione. Ma non è tutto oro quello che luccica. Ci sono diversi fattori che possono rendere un assembly in
native-code non valida. Cosa succede in questo caso? Nulla di drammatico:
semplicemente, il runtime di .NET si accorge che la native-image è invalida, quindi ritorna
al buon vecchio assembly in IL, e passa alla compilazione JIT. Quali
sono questi fattori che possono invalidare un assembly? Li riporto da MSDN
senza tradurre:
- the version of the .NET Framework
- the CPU type
- the version of the operating system
- the exact identity of the assembly (recompilation
changes identity)
- the exact identity of all assemblies that the
assembly references (recompilation changes identity)
- security factors
Se anche uno solo di questi fattori interviene, allora la nostra native image
non è più valida, e l'assembly viene eseguito dal runtime in JIT. Voglio fare
qualche commento.
Il punto (2) è chiaro. La native image contiene istruzioni in linguaggio
macchina, specifiche quindi di una certa famiglia di processori. Se la native
image viene eseguita su un sistema con un altro tipo di processore, con un altro
set di istruzioni, l'assembly non può essere eseguito in native code.
I punti
(4) e (5) dicono espressamente che ogni volta che ricompilo un assembly, la sua
identity cambia, e di conseguenza anche la sua native image non è più valida. Di
conseguenza, è molto meglio usare ngen.exe solo quando abbiamo
compilato l'assembly pronto per il rilascio.
Il punto (6) parla di security.
Ad esempio, se generiamo una native image con il Code Access
Security disattivato, l'assembly in native code diventerà non
valido quando riattiveremo il CAS. Francamente, non vedo perchè uno debba dare
il comando:
caspol -security off
Non dite a nessuno che ve l'ho fatto vedere, mi è scappato per sbaglio un CTRL+V dalla mia command-line. Comunque sia, quelli elencati prima sono
i fattori che possono influire sulla validità di una native image generata con
ngen.exe.
Come ho detto prima, .NET si accorge da solo di questo problema, non dice nulla,
semplicemente ritorna ad eseguire l'assembly tradizionale. Può essere però che ci
accorgiamo di una perdita di prestazioni, dovuta al fatto che
l'assembly gira in JIT.
Usare ngen.exe su un assembly senza strong-name
Dunque,
abbiamo detto che ngen.exe posiziona il nostro assembly in native code nella
GAC. Abbiamo detto che per poter finire nella GAC, un assembly deve avere uno
strong name. Allora, mi sono chiesto, com'è possibile
che sono riuscito a generare una native image di un assembly senza strong
name?
Lo so, questo è un blog e non un forum. Difatti, ho inserito questo
post sul forum di UGIdotNET, ma non ho ancora avuto risposte. Se qualcuno sa
illuminarmi, per favore, si faccia avanti.