Parallel Programming – PLINQ #4

In questo post continueremo il “viaggio” nel meraviglioso mondo di PLINQ esplorandone altre caratteristiche.

Ammettiamo di avere tra i requisiti utente di un nostro ipotetico software da sviluppare, il dover presentare dei dati preservandone l’ordinamento. Il nostro software dovrà presentare a video 10.000 numeri interi garantendo la sequenzialità degli stessi; utilizzando LINQ in versione sequenziale possiamo operare con il seguente segmento di codice :

int[] data = new int[10000];
for (int i = 0; i < data.Length; i++)
{
      data = i;
}

IEnumerable<int> res = from x in data                                
                                   select x;

l’operazione viene eseguita sulla mia macchina dual core in più di cinque secondi mostrando a video i 10.000 numeri interi perfettamente ordinati.

Ai requisiti utente precedenti se ne aggiunge un altro, per la serie “sempre più difficile!” Smile : il compito deve essere svolto in meno di cinque secondi. Per conseguire l’obbiettivo richiesto dal cliente, utilizziamo una query PLINQ in modo da ottimizzare le performance sfruttando al meglio le risorse di processore. La query precedente verrà leggermente modificata come nel seguente segmento di codice :

int[] data = new int[10000];
for (int i = 0; i < data.Length; i++)

{
   data = i;
}

IEnumerable<int> res = from x in data.AsParallel()                               
                                   select x;

l’operazione viene eseguita sulla mia macchina dual core in quattro secondi  ma osservando i risultati a video notiamo che questi non sono ordinati; abbiamo rispettato il secondo requisito utente ma non il primoSad. Anche in questo caso “l’imputato” principale causa di questo comportamento inaspettato è il partizionamento dei dati da parte di PLINQ, che frammenta la fonte dati, nel mio caso specifico in due partizioni, “rompendo” così l’ordine naturale degli elementi in questione e ricomponendo i risultati ad elaborazione finita.

Come possiamo porre rimedio a questo problema? Possiamo utilizzare l’extension method AsOrdered(), che consente di eseguire query PLINQ parallelizzate garantendo però l’ordinamento dei risultati. Proviamo a scrivere il seguente frammento di codice:

int[] data = new int[10000];
for (int i = 0; i < data.Length; i++)

{
   data = i;
}

IEnumerable<int> res = from x in data.AsParallel().AsOrdered()                              
                                   select x;

eseguendo l’esempio in questione, i 10.000 numeri interi risulteranno completamente allineati e l’operazione sulla mia macchina dual core verrà eseguita in poco più di quattro secondi, ma se misuriamo con precisione i millisecondi intercorsi per il completamento dell’operazione, ci accorgiamo che la query PLINQ ordinata è più costosa rispetto alla versione parallela non ordinata: questo accade in quanto PLINQ, dopo aver partizionato i dati e terminato l’elaborazione, deve ripristinare l’ordine degli elementi. Pertanto è opportuno utilizzare AsOrdered() se l’ordine degli elementi è richiesto, come nel nostro caso, dai requisiti utente, o se è l’operazione che stiamo eseguendo ad aver bisogno di dati ordinati in un certo modo, altrimenti, per ottimizzare le performance, è opportuno eseguire query PLINQ non ordinate.

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

Published Tuesday, July 20, 2010 12:32 AM by leo.alario
Powered by Community Server (Commercial Edition), by Telligent Systems