Parallel Programming - PLINQ #1

Grazie al numero sempre maggiore di CPU disponibili su personal computer e workstation, che consentono l’esecuzione simultanea di più thread, è possibile parallelizzare il codice per distribuire il lavoro su più processori rendendo scalabili a seconda dell’ hardware disponibile le nostre applicazioni.

Con l’avvento del .NET Framework 4.0 è stato migliorato il supporto alla programmazione parallela tramite l’introduzione di un nuovo runtime e di nuovi tipi di librerie di classi. Tali funzionalità semplificano lo sviluppo parallelo consentendo di scrivere codice efficiente,senza dover utilizzare soluzioni personalizzate per gestire le problematiche comuni della programmazione parallela e utilizzare codice a basso livello per gestire i thread o il pool di thread.

In questa serie di post cercheremo di affrontare e approfondire tutti gli aspetti inerenti la programmazione parallela partendo da PLINQ.

PLINQ sta per Parallel LINQ ed è l’implementazione in parallelo di LINQ to Objects che il framework 4.0 mette a disposizione. PLINQ. oltre ad implementare il set standard di operatori di query, mette a disposizione operatori aggiuntivi per le operazioni in parallelo.

L’entry point per PLINQ è l’extension method AsParallel() richiamabile come metodo di instanza di ogni oggetto di tipo IEnumerable<TSource>, che consente di eseguire query LINQ to Objects spalmando, quando possibile,  il carico computazionale tra il vari core presenti nel sistema.

Ipotizziamo di voler implementare un software che effettua il ping di 10 indirizzi IP differenti.

Utilizzando LINQ to objects in modo sequenziale avremo  il seguente frammento di codice :

var addrs = new[] { "10.1.0.1", "10.1.0.2", "10.1.0.3", "10.1.0.4",
                             "10.1.0.5", "10.1.0.6", "10.1.0.7", "10.1.0.8",
                             "10.1.0.9", "10.1.0.10" };
// Versione sequenziale
var pings = from ip in addrs
                  select new Ping().Send(ip);
foreach (var ping in pings)
{
     Console.WriteLine("{0}: {1}", ping.Status, ping.Address);
}
 

la versione sequenziale effettua i 10 ping sulla mia macchina dual core in circa 25 secondi ed utilizza un solo thread per effettuare l’operazione.

Vediamo ora la rispettiva versione parallela :

var addrs = new[] { "10.1.0.1", "10.1.0.2", "10.1.0.3", "10.1.0.4",
                             "10.1.0.5", "10.1.0.6", "10.1.0.7", "10.1.0.8",
                             "10.1.0.9", "10.1.0.10" };

// Versione parallela standard

var pings = from ip in addrs.AsParallel()
                 select new Ping().Send(ip);

foreach (var ping in pings)
{
    Console.WriteLine("{0}: {1}", ping.Status, ping.Address);
}

la versione parallela effettua i 10 ping sulla mia macchina dual core in circa 15 secondi con un incremento delle prestazioni del 40 % Big Smile. La versione parallela utilizza di default un numero di thread uguale al numero di processori logici presenti sulla macchina. E’ possibile comunque forzare l’uso di più thread o, nel caso si disponga di più core, ridurre il numero di thread impiegati utilizzando l’extension method WithDegreeOfParallelism(). Questo extension method accetta in input un argomento dove è possibile indicare il numero di thread  da impiegare, valore compreso tra 1 e 63.

Per verificare quanto detto in precedenza è sufficiente rimpiazzare il frammento di codice precedente con questo :

var pings = from ip in addrs.AsParallel().WithDegreeOfParallelism            (System.Environment.ProcessorCount)
                 select new Ping().Send(ip);

Otterremo le stesse prestazioni che avremmo ottenuto chiamando solo l’extension method AsParallel().

Vediamo nel frammento di codice qui di seguito la nuova versione dell’esempio dove stiamo dicendo al framework di utilizzare 8 thread  per effettuare l’operazione :

var pings = from ip in addrs.AsParallel().WithDegreeOfParallelism(8)
                 select new Ping().Send(ip);

foreach (var ping in pings)

{
     Console.WriteLine("{0}: {1}", ping.Status, ping.Address);

}

questa nuova versione effettua i 10 ping sulla mia macchina dual core in circa 7 secondi Big Smile con un incremento delle prestazione del 72 %! anche se, va detto, è opportuno utilizzare questo tipo di approccio solo ed esclusivamente nelle applicazioni client che girano su desktop in quanto, utilizzandolo in un’applicazione web ASP.NET in grado di gestire diverse richieste contemporaneamente, si “ucciderebbe” letteralmente la macchina! (vedremo come gestire questa problematica nei prossimi post)

Per chi volesse cimentarsi con gli esempi utilizzati in questo post e magari verificare le relative tempistiche può trovarli qui.

Published Sunday, June 6, 2010 8:55 PM by leo.alario

Comments

# Parallel Programming – PLINQ #2

Sunday, June 13, 2010 11:49 AM by .Net Café

Aggiungiamo un altro tassello all’immenso mosaico della parallel programming con .NET 4.0: è possibile

# Parallel Programming – PLINQ #3

Sunday, June 20, 2010 11:40 AM by .Net Café

Nelle precedenti puntate avevamo visto come elaborare in parallelo query PLINQ incrementando le prestazioni

Powered by Community Server (Commercial Edition), by Telligent Systems