[Visual C# 2010][ TPL] Code Séquentiel versus Code Parallèle

Vous avez peut être déjà pratiqué l'offre parallèle .NET à travers les différentes CTP de la librairie Parallel Extensions for .NET 3.5. Depuis l'annonce de la disponibilité de la première CTP de Visual Studio 2010 pendant la PDC08, Microsoft a annoncé qu'elle ne proposerait plus de librairie compatible avec Visual Studio 2008. Les raisons qui ont motivé ce choix sont relatives aux modifications apportées à la CLR 4.0 afin de rendre la gestion du pool thread compatible avec les exigences de l'équipe Parallel Extensions for .NET. L'objectif ici n'est pas d'illustrer toute l'offre, mais d'adapter un petit code séquentiel en mode parallèle: l’exemple naïf d’une multiplication de matrices, si facile à comprendre :-)

void MatrixMult(int size, double[][] m1, double[][] m2, double[][] result)

{

for (int i = 0; i < size; i++)

{

for (int j = 0; j < size; j++)

{

double t = 0;

for (int k = 0; k < size; k++)

{

t += m1[i][k] * m2[k][j];

}

result[i][j] = t;

}

}

}

Figure 1: code séquentiel à adapter

Pour évaluer les performances de cette première version, nous allons solliciter la méthode MatrixMult, avec une valeur de size de 1000 sur machine contenant un processeur quadri cœurs.

Console.WriteLine("Starting execution in serial mode...");

Stopwatch sw = new Stopwatch();

sw.Start();

MatrixMult(SIZE, _m1, _m2, _result);

sw.Stop();

Console.WriteLine("Elapsed time: {0} ms\n", sw.ElapsedMilliseconds);

Figure 2: code pour mesurer les traitements

On lance le programme en mode Release tout en observant le Task Manager pour suivre l'occupation des cœurs.

image001 

Figure 3: exécution en mode séquentiel

Nous constatons que le traitement occupe peu les cœurs disponibles avec un temps d'exécution de 14 secondes.

Pour profiter de tous les cœurs disponibles, modifions le code et ajoutons l'espace de nom pour éviter de frapper de longues lignes de codes :

using System.Threading;

Puis nous altérons légèrement le code de la méthode MatrixMult afin de paralléliser le code. Nous remplaçons l'instruction for par la méthode d'extension Parallel.For dont le premier argument est l'indice de départ, le second est la taille à itérer et le dernier une lambda expression contenant le corps du traitement repris du code séquentiel.

void MatrixMultParallel(int size, double[][] m1, double[][] m2, double[][] result)

{

Parallel.For(0, size, (i) =>

{

for (int j = 0; j < size; j++)

{

double t = 0;

for (int k = 0; k < size; k++)

{

t += m1[i][k] * m2[k][j];

}

result[i][j] = t;

}

});

}


Figure 4: Code adapté au mode parallèle

On remarque que le code est lisible, sémantiquement compréhensible et finalement très similaire à la version originale (figure 1) J

On relance le programme et on observe une nouvelle fois le Task Manager.

 image002

Figure 5: exécution en mode parallèle

Cette fois les cœurs sont pleinement exploités avec un temps d’exécution de 4 secondes.

Finalement avec très peu d'efforts nous avons divisé par 3.5 le temps d’exécution de notre petit programme. C’est naturellement un exemple simple, mais je trouve qu’il démontre parfaitement l’intention de l’offre parallèle de Visual Studio 2010.

Le prochaine fois c'est en C++0x que nous feront le test.

A bientôt,

Bruno

Bruno Boucard (boucard.bruno@free.fr)