Month: February 2011

Ancora un altro free ebook su Windows Phone 7

Per chi vuole iniziare a sviluppare su Windows Phone 7, è disponibile online e scaricabile gratuitamente ancora un altro free ebook.

5545.Free-EBook-Silverlight-for-Windows-Phone_58A2F338

Per il download…cliccate qui !

Inside “Extension Methods” … call vs callvirt

Capita sempre di dover utilizzare delle classi delle quali non abbiamo il codice sorgente (basti pensare a quelle del .Net Framework) ma sulle quali non possiamo eseguire determinate operazioni in assenza dei corrispondenti metodi. Non abbiamo quindi la possibilità di estenderle e dotarle del nuovo comportamento di cui abbiamo bisogno.

In questi casi, risolviamo il problema implementando tale metodo al di fuori della classe stessa e ad esempio aggiungendolo in una nuova classe statica.

Per esempio, supponiamo di aver bisogno del metodo IndexOf() sulla classe StringBuilder (di cui essa non è dotata). Possiamo realizzare una nuova classe statica all’interno della quale implementiamo tale metodo.

public static class StringBuilderExtensions
{
    public static int IndexOf(StringBuilder sb, char value)
    {
        for (int index = 0; index < sb.Length; index++)
        {
            if (sb[index] == value)
                return index;
        }
        return -1;
    }
}

Ovviamente, per invocare tale metodo dobbiamo definire un’istanza della classe StringBuildere passare tale oggetto al metodo statico implementato.

int index;
StringBuilder sb = new StringBuilder("Paolo");
index = StringBuilderExtensions.IndexOf(sb, 'o');
Per fortuna, attraverso l’utilizzo degli Extension Methods, abbiamo la possibilità di estendere in maniera naturale una classe ed invocare direttamente sull’istanza dell’oggetto il nuovo comportamento implementato.
L’implementazione di un extension method viene realizzata con la tecnica precedente ma con la differenza nell’aggiungere la parola chiave this davanti al primo parametro del nuovo metodo, per indicare che esso sarà l’oggetto su cui andrà ad agire.
public static class StribgBuilderExtensionMethods
{
    public static int IndexOf(this StringBuilder sb, char value)
    {
        for (int index = 0; index < sb.Length; index++)
        {
            if (sb[index] == value)
                return index;
        }
        return -1;
    }
}
Attraverso questa tecnica, l’Intellisense di Visual Studio ci mette a disposizione tale nuovo metodo, direttamente sull’istanza della classe StringBuilder.
4520.em_1_62641764

A questo punto, possiamo però dimostrare che le due implementazioni sono perfettamente identiche e che la feature degli extension methods è più che altro una facility per lo sviluppatore.

Infatti, invocare un extension method equivale esattamente ad invocare un metodo statico come nel caso precedente con la differenza che non dobbiamo eseguire esplicitamente il passaggio del parametro su cui il metodo andrà ad agire. Basta confrontare le due seguenti invocazioni :

index = StringBuilderExtensions.IndexOf(sb, 'o');
index = sb.IndexOf('o');
Sarà il compilatore a generare il codice IL che passa il parametro “sb” all’extension method.
Consideriamo il seguente esempio con il relativo codice IL :
static void Main(string[] args)
{
    int index;
    StringBuilder sb = new StringBuilder("Paolo");
    sb.Append("Embedded Life");
    index = StringBuilderExtensions.IndexOf(sb, 'o');
    index = sb.IndexOf('o');
}
3482.em_2_5927DC23
Come si può osservare nel codice IL generato, le due invocazioni di IndexOf() sono perfettamente identiche ed il fatto che siano invocazioni di metodi statici lo evidenzia anche l’utilizzo della call al posto della callvirt come nel caso del metodo Append() che è un metodo di istanza della classe StringBuilder.

Infatti, esiste una sostanziale differenza tra call e callvirt :

  • call : può essere usata per l’invocazione di metodi statici, di istanza e virtuali. Tipicamente soprattutto per i metodi statici, preferendo la callvirt agli altri tipi di metodi. Essa assume che l’oggetto su cui viene eseguita la chiamata non sia null e quindi non esegue questo tipo di check;
  • callvirt : utilizzata per l’invocazione di metodi virtuali e di istanza, quindi non viene mai utilizzata per i metodi statici. Essa esegue sempre un check per verificare che l’oggetto su cui viene eseguita l’invocazione non sia null ed in tal caso viene sollevata una NullReferenceException;

La differenza sul check di oggetto null o meno lo si può apprezzare con il seguente esempio :

static void Main(string[] args)
{
    int index;
    StringBuilder sb = null;
    sb.Append("Paolo");
    index = sb.IndexOf('o');
}

Nel caso dell’invocazione del metodo Append() attraverso la callvirt, il CLR solleverà l’eccezione proprio in corrispondenza della chiamata.

7242.em_3_262825EF

Viceversa, nel caso dell’invocazione dell’extension method IndexOf() attraverso la call, l’invocazione viene eseguita (non esiste il check sul fatto che l’oggetto è null) ma ovviamente l’eccezione viene sollevata all’interno del metodo quando si tenta di accedere all’oggetto.

1586.em_4_63D50DAB

BeforeFieldInit … un type attribute invisibile ma determinante !

L’inizializzazione dei campi statici di una classe può essere eseguita con le due seguenti diverse modalità oppure con un mix di esse :

  • Type Initializer : inizializzazione di un campo statico in corrispondenza della sua dichiarazione;
  • Type Constructor : costruttore statico della classe che viene invocato una sola volta (per application domain) sulla stessa;

Ebbene, la presenza o meno dell’uno o dell’altro può produrre sequenze di esecuzioni differenti da parte del CLR sulla nostra classe.

Consideriamo le due seguenti classi, che a prima vista risultano semanticamente uguali :

class FirstClass
{
// type initializer
public static int x = 1;
}

class SecondClass
{
public static int x;

// type constructor
static SecondClass()
{
x = 1;
}
}

Entrambe hanno il campo statico “x”  ma che viene inizializzato in maniera diversa. Con unType Initializer nel primo caso e con un costruttore statico (Type Constructor) nel secondo. Il risultato finale sarà ovviamente lo stesso ma, se utilizziamo il Reflector oppure ILDASM, constatiamo come il compilatore abbia tradotto in codice IL le due classi in maniera leggerment diversa :

3056.beforefieldinit_1_57F8FD6D

0486.beforefieldinit_2_thumb_542B32D0

Alla classe che non ha il costruttore statico è stato aggiunto l’attributo beforefieldinit.
Cosa comporta la presenza di questo attributo ?
E’ noto che, nell’ambito di una stessa classe, l’invocazione di un Type Initializer avviene sempre prima del Type Constructor ed inoltre, l’inizializzazione di un campo statico di una classe viene eseguita ovviamente prima che si acceda a tale campo. Ma quanto prima ? Siamo certi che avviene immediatamente prima ? O magari viene eseguita molto tempo prima ? Se tentassimo di accedere al campo “x”  di ciascuna delle due classi, esso sarà stato inizializzato immediatamente prima dell’accesso o molto tempo prima ?
Per rendercene conto, consideriamo le due seguenti classi :
class BeforeFieldInit
{
    // type initializer
    public static string x = WriteLine("BeforeFieldInit Type Initializer");

    // static method
    public static string WriteLine(string s)
    {
        Console.WriteLine(s);
        return s;
    }
}

class NotBeforeFieldInit
{
    public static string x;

    // type constructor
    static NotBeforeFieldInit()
    {
        x = WriteLine("NotBeforeFieldInit Static Constructor");
    }

    // static method
    public static string WriteLine(string s)
    {
        Console.WriteLine(s);
        return s;
    }
}

 

Entrambe hanno un metodo statico per la scrittura di una stringa su console ma, mentre la prima sarà compilata con l’attributo beforefieldinit, la seconda non sarà dotata di questo attributo.
Considerando la seguente applicazione delle due classi suddette ed osserviamone l’output :

static void Main(string[] args)
{
    string tmp;
    Console.WriteLine("Starting Main");
    tmp = BeforeFieldInit.x;
    tmp = NotBeforeFieldInit.x;
    Console.ReadLine();
}
1033.beforefieldinit_3_41763919

Nonostante l’accesso al campo “x” della classe BeforeFieldInit sia l’istruzione 5, il Type Initializer viene invocato addirittura prima dell’istruzione 4. Mentre il costruttore statico della classe NotBeforeFieldInit viene invocato immediatamente prima dell’accesso al campo.

Ciò fa capire che la presenza dell’attributo beforefieldinit (aggiunto dal compilatore quando una classe non è dotato esplicitamente di costruttore statico), indica al CLR di poter invocare l’inizializzazione dei campi statici anche tempo prima che ci sia un accesso su di essi mentre l’assenza dell’attributo ritarda l’inizializzazione immediatamente prima all’accesso.

Conseguenze sulle performance

A questo punto, vediamo come questa semplice differenza possa comportare un impatto sulle performance di un’applicazione.

Consideriamo nuovamente le due classi FirstClass e SecondClass precedenti, dotate entrambe di un campo statico intero “x” ma la prima, ovviamente compilata con l’attributobeforefieldinit. Utilizziamole nel seguente applicativo di esempio :

static void PerformanceTest1()
{
    Stopwatch sw = Stopwatch.StartNew();
    for (int i = 0; i < 1000; i++)
    {
        FirstClass.x = 1;
    }
    Console.WriteLine("Performance access ... {0} : FirstClass", sw.Elapsed);

    sw = Stopwatch.StartNew();
    for (int i = 0; i < 1000; i++)
    {
        SecondClass.x = 1;
    }
    Console.WriteLine("Performance access ... {0} : SecondClass", sw.Elapsed);
}

static void Main(string[] args)
{
    PerformanceTest1();
    Console.ReadLine();
}

Vogliamo valutare il tempo necessario per eseguire 100 assegnazioni consecutive sul campo statico “x” di ciascuna classe. Il risultato è il seguente (i tempi possono ovviamente variare da computer a computer e tra un avvio e l’altro, ma il rapporto rimarrà comunque lo stesso) :

1033.beforefieldinit_3_41763919

Utilizzando la classe compilata con l’attributo beforefieldinit, abbiamo un miglioramento di performance dell’80% circa (5 volte più veloce) !!

A cosa è dovuta questa notevole differenza ?

Quando il JIT compiler compila il metodo PerformanceTest1() produce un codice macchina leggermente diverso :

  • nel caso di utilizzo della FirstClass, il Type Initializer viene invocato prima dell’ingresso del ciclo for;
  • nel caso di utilizzo della SecondClass, la chiamata al Type Constructor viene inserita all’interno del ciclo for ma ovviamente il costruttore statico dovrà essere invocato una sola volta (alla prima iterazione). Per questo motivo, il JIT compiler produce un codice che esegue un check per verificare se tale costruttore sia stato o meno invocato; ciò determina un peggioramento delle performance !

Per rendercene conto, riporto di seguito il codice macchina (mixato con le istruzioni C#) prodotto dal JIT compiler :

7563.beforefieldinit_5_7DDE87F6

Nel caso della FirstClass, constatiamo la presenza della sola istruzione che assegna il valore 1 al campo statico “x” della classe stessa.

2313.beforefieldinit_6_423E7936

Nel caso della SecondClass, prima dell’istruzione di assegnazione del valore 1 al campo statico “x”, notiamo la presenza di una call. Con questa call, viene invocato il costruttore statico e viene eseguito il check che verifica, quindi ad ogni iterazione, se sulla classeSecondClass il costruttore stesso sia stato già invocato in precedenza.

Aggiungiamo un’altra funzione al nostro esempio che verrà invocata nel main :

static void PerformanceTest2()
{
    Stopwatch sw = Stopwatch.StartNew();
    for (int i = 0; i < 1000; i++)
    {
        FirstClass.x = 1;
    }
    Console.WriteLine("Performance access ... {0} : FirstClass", sw.Elapsed);

    sw = Stopwatch.StartNew();
    for (int i = 0; i < 1000; i++)
    {
        SecondClass.x = 1;
    }
    Console.WriteLine("Performance access ... {0} : SecondClass", sw.Elapsed);
}

static void Main(string[] args)
{
    PerformanceTest1();
    PerformanceTest2();
    Console.ReadLine();
}

Osserviamo che la funzione PerformanceTest2() è volutamente uguale allaPerformanceTest1() e viene invocata subito dopo di essa nel main. Non ho utilizzato la stessa funzione chiamandola due volte, perché voglio forzare il JIT compiler ad eseguire una nuova compilazione (nel caso della doppia chiamata alla PerformanceTest1() ciò non accadrebbe). L’output sarà il seguente :

0830.beforefieldinit_7_5125A850

Eseguendo la funzione PerformanceTest2(), nonostante sia comunque diversa dallaPerformanceTest1() (anche se il codice è lo stesso), si osserva che le performance sono praticamente le stesse. Cosa è successo ? Semplicemente questo…

Quando il JIT compiler esegue la compilazione della funzione PerformanceTest2(), sa già che il costruttore statico della SecondClass è stato invocato (nella funzione PerformanceTest1()) e non genera il codice macchina che invoca il costruttore ed il check di verifica ad ogni iterazione.

5826.beforefieldinit_8_55BBB30A

Per quanto riguarda la classe FirstClass non è cambiato ovviamente nulla.

2046.beforefieldinit_9_3E40FED9

Viceversa, per la SecondClass non esiste più la call al costruttore statico della classe.