Marco Russo

.NET, Business Intelligence e dintorni

Corsi

Miei blog in inglese

Programmazione multi-thread su processori a 64 bit

Uno dice: abbiamo .NET, non dobbiamo più preoccuparci delle differenze architetturali dell'hardware, nemmeno di quelle tra 32 e 64 bit...

La realtà è un po' diversa, ma a volte per motivi che è difficile immaginare. I processori come Itanium hanno delle caratteristiche che inducono i compilatori (nonché l'unità di esecuzione della CPU) a "riordinare" il codice se, dal punto di vista del thread in esecuzione, non esistono interdipendenze tra le istruzioni. Chiaramente questa cosa, in un contesto multi-thread, può portare ad avere dei problemi.

Non vorrei stare a tradurre tutto il discorso, questo vecchio messaggio di Vance Morrison spiega abbastanza chiaramente quale è il problema. Lo spunto per andare a rileggere questo messaggio me l'ha dato uno dei commenti a un (non più tanto) recente post di Brad Abrams, che affronta le differenze tra volatile e System.Threading.Thread.MemoryBarrier(). Devo confessare che l'argomento merita di essere approfondito perché mantengo qualche piccolo dubbio su quale sia l'utilizzo corretto di volatile, MemoryBarrier e WriteMemoryBarrier (quest'ultima citata nel post di Vance Morrison ma non documentata e non presente in .NET 1.1). Mi devo procurare una macchina a 64 bit multiprocessore, ma soprattutto un po' di tempo (risorsa molto più difficile da trovare).

La questione è molto meno accademica di quanto sembra: immaginate un programma (con operazioni asincrone di qualsiasi tipo) che funziona perfettamente oggi ma che domani, eseguito su una macchina con almeno due Itanium a bordo (non so ancora dire se AMD-64 presenta caratteristiche simili, ma è possibile), presentasse sporadici malfunzionamenti. E' esattamente il rischio che si corre se non si seguono le giuste precauzioni, anche se per fortuna il codice "a rischio" dovrebbe essere in punti limitati e la problematica diventa tanto più complessa quanto più vogliamo "spingere" la scalabilità del codice (che implica ridurre i lock al minimo indispensabile).

Già mi immagino la "pezza" che qualcuno metterà trovandosi di fronte a un problema più o meno sconosciuto: esecuzione di un processo su singolo processore e tanti saluti alla scalabilità. Aspettiamo ancora qualche anno e vediamo se questa previsione nefasta si avvererà.

UPDATE: leggendo meglio alcuni commenti al post di Brad Abrams ne ho visto uno di Vance Morrison che anticipa l'adozione di un "default" per .NET che consentirà di simulare, anche su processori con un "weak memory model", lo stesso modello di x86 che non è soggetto a problemi di memory-barrier (neanche con sistemi multi-processore). Ovviamente la cosa ha un costo molto significativo in termini di scalabilità, ma per lo meno non obbligherà chi non ne ha bisogno a rivedere tutta la gestione della concorrenza nel proprio codice.