Ottimizzazioni del compilatore e multithreading: loop invariant

by Marco 18. novembre 2009 10.58

Codice(C#,VS2008,.NET 3.5):

public class LoopInvariant
   {
       int count = 0;
       public void Cicla()
       {
           while (this.count == 0) {}
       }
       public void Cambia()
       {
           this.count = 1;
       }
   }

fino a qua, nulla di particolare, questa classe non fa un bel niente, semplicemente abbiamo 2 metodi, un metodo Cicla() che esegue una while verificando che count sia uguale a 0, poi abbiamo Cambia() un metodo che cambia il contatore e lo porta a 1.

Ora usiamo questa classe(console application):

static void Main(string[] args)
       {
           LoopInvariant var = new LoopInvariant();
           ThreadPool.QueueUserWorkItem(o =>
           {
               System.Threading.Thread.Sleep(5000);
               ((LoopInvariant)o).Cambia();
               Console.WriteLine("Valore modificato");
           }, var);
           var.Cicla();
           Console.WriteLine("Fine test");
           Console.ReadKey();
       }

qua cosa succede; niente di particolare: istanzio la classe LoopInvariant, faccio partire in un thread separato(attraverso il threadpool di .NET) un pezzo di codice che si blocca per 5 secondi(Sleep(5000)) dopo di che chiama il metodo Cambia() sulla mia istanza e stampa che lo ha fatto.

Bene, compiliamo(in release). Lanciamo(CTRL+F5)…passano i 5 secondi…6…7….8….9….?!?!?!?!?

LoopInvariant

cavolo succede?perchè non stampa “Fine test”?ho cambiato il valore di count…dovrebbe stampare “Fine test” (visto che la condizione count == 0 è falsa) e fermarsi li ad aspettare la pressione di un tasto…

Il problema è che il Jit compiler analizzando il metodo Cicla() applica una ottimizzazione chiamata Loop-invariant code motion, ovvero dal fatto che la variabile count non viene “toccata” all’interno del corpo del while, il compilatore può concludere che count è “loop invariant” e così metterlo in una variabile temporanea prima di iniziare il loop(in un registro del processore magari, così evitiamo di dover risolvere l’indirizzo in ram della variabile ad ogni ciclo migliorando le prestazioni) , questo comporta che quanto l’altro thread chiama Cambia() sulla stessa istanza la modifica non viene “vista” dal nostro metodo Cicla() portanto ad un loop infinito. Cosa che genera ancora più confusione è che se facciamo il debug(F5) di questo codice tutto funziona correttamente in quanto le ottimizzazioni sono disabilitate in configurazione DEBUG.

Per disabilitare questa ottimizzazione occorre dichiarare la nostra variabile count come volatile, questo rende il nostro count esplicitamente non “loop invariant” , come Joe Duffy spiega nel suo libro:

“Load and stores of volatile variables can never be introduced or removed, both in .NET and VC++, because they are assumed to be constantly changing. As such, they aren’t eligible for being considered loop invariant and hoisted outside of loops…”

quindi basta cambiare il nostro codice in questo modo:

public class LoopInvariant
   {
       volatile int count = 0;
       public void Cicla()
       {
           while (this.count == 0) {}
       }
       public void Cambia()
       {
           this.count = 1;
       }
   }

a questo punto se compiliamo(sempre in release altrimenti le ottimizzazioni vengono soppresse per supporto al debug) e lanciamo(CTRL+F5)…1…2….3..4..5…e

LoopInvariant2

adesso il programma funziona come ci aspettavamo…

Attenzione questo non è il solo effetto della keyword volatile, ricordo che leggere da un campo volatile(o usare Thread.VolatileRead) equivale logicamente ad una acquire fence, mentre scrivere un campo volatile(o usare Thread.VolatileWrite) equivale logicamente ad una release fence.

La classe LoopInvariant chiaramente non serve ad un piffero ed è li solo per didattica, ma questo ci fa capire che la programmazione multithread “aggiunge” una dimensione che oggi non è gestita in modo automatico dai compilatori, nel caso della loop invariant fare quella ottimizzazione non cambiava la semantica del programma, ma il danno lo abbiamo visto tutti.

Occhio…

Fonti: Concurrent Programming on Windows

Correntemente valutato 5.0 da 1 utenti

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , ,

Concurrent Programming | Multithreading | Parallel Programming

Web Service?

by Marco 12. novembre 2009 19.26

“…deve usare il nostro web service che come da standard si autodocumenta attraverso il wsdl e le permette di interagire con i nostri sistemi…”

io penso…”…fico, sarà uno scherzo, grazie soap…”

download wsdl…..diamo un’occhiata…eeeeeeeeeee!?!??!!?!?

public string FintaOperazioneAltrimentiNonLavoroPiù(string auth,string parametriRicerca);

…e tonnellate di carta a spiegare come deve essere formattato l’xml dei parametri di ingresso e dei parametri di ritorno…allora ripenso ai capisaldi di SOAP…

Contracts should be designed to be as explicit as possible to minimize misinterpretation. Additionally, contracts should be designed to accommodate future versioning of the service via the extensibility of both the XML syntax and the SOAP processing model.

…cavolo almeno quell’auth…mettetelo in un custom soap header se non vi va bene niente di WS-Security…vabbè proviamo a chiamare e vediamo che succede…

eccezione dal servizio, cosa dice il soap fault?…Object not set to an instance….eeeeeeeee!?!?!?!?e ripenso ai capisaldi di SOAP

Internal (private) implementation details should not be leaked outside of a service boundary

…vabbè almeno usano .net…la classe XXX ha sollevato un'eccezione…spetta che mando la riga dell’errore allo sviluppatore, è nell’errore….

…ho capito l’errore…ho sbagliato il case di un elemento del parametro string da passare come xml…se fosse stato un contratto fatto con criterio questo errore me lo evitavo…

morale: sono stufo di “String XML HTTP POST Services”(POX services) spacciati per "Web Services SOA", per quello c'è REST ;-)

Correntemente valutato 5.0 da 1 utenti

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: ,

Wcf

Disclaimer
Le opinioni espresse in questo blog sono mie opinioni personali.

© Copyright 2012 Knowledge.CreateAsync()