Accueil > .Net, C# > [C#4] Parallélisme

[C#4] Parallélisme

Un peu de programmation parallèle aujourd’hui.

Voici la situation :
Je récupère en base de données 1530 lignes.
Sur chaque ligne, j’ai un BLOB contenant un fichier (doc, pdf, jpg…).
Si je récupère le BLOB en même temps que mes lignes, j’explose ma RAM (en tout, ça fait près de 1Go).
Donc, je fais une petite boucle de ce type :

// Récupération des données, puis on boucle dessus :
foreach (DataRow row in dt.Rows)
{
    object fileContent = dal.ExecuteScalar(String.Format(Queries.FICHIER_PJs_BLOB, row["IDPJ"]));
    CreateFile(row["TargetPath"].ToString(), (byte[])fileContent);
}

Sur mes 1530 lignes, je boucle en 737 secondes (soit un peu moins de 13 minutes…).
On va dire qu’il y a quand même mieux.
Surtout quand la deuxième vague de pièces jointes comprend 9600 lignes…

Alors, que faire ?
En C# 4.0, on peut utiliser le parallélisme.

Je créé donc un classe pour modéliser ma pièce jointe :

public class FileToSave
{
    public String Id { get; set; }
    public String CurrentPath { get; set; }
    public String TargetPath { get; set; }
    public String TargetName { get; set; }
    public String TargetFullPath
    {
       get { return Path.Combine(TargetPath, TargetName); }
    }
    public void SaveFile(GenericDal dal)
    {
    // Fix le fait d'avoir plusieurs accès concurrentiels à la Dal (cf. plus loin dans le billet)
        GenericDal tempDal = new GenericDal(dal.ConnectionString);
        Byte[] content = (Byte[])dal.ExecuteScalar(String.Format(Queries.FICHIER_PJs_BLOB, Id));
        if (content != null)
        {
            if (!Directory.Exists(TargetPath))
                Directory.CreateDirectory(TargetPath);

            using (FileStream fs = System.IO.File.Create(TargetFullPath))
            {
                fs.Write(content, 0, content.Length);
            }
        }
    }
    public Boolean Equals(FileToSave obj)
    {
        return TargetFullPath.Equals(obj.TargetFullPath, StringComparison.InvariantCultureIgnoreCase);
    }
}

Dans SaveFile, plusieurs choses importantes.
Je créé un objet de DAL spécifique à chaque boucle pour éviter quelques petits problèmes du fait qu’elle n’est pas conçue (pour l’instant…) pour avoir un nombre aussi massif d’appels en même temps.

Après, que faire ?
J’utilise deux namespace particuliers :

Dans une première boucle foreach, sur ma DataTable (qui n’est thread-safe qu’en lecture), j’alimente mon objet (sauf la propriété Content, donc), en testant pour voir si j’ai pas des doublons (autant éviter de traiter un fichier qui va être écrasé par un autre, un peu plus loin…c’est le but de la méthode Equals).

Et après :

BlockingCollection<FileToSave> files = new BlockingCollection<FileToSave>();
foreach (DataRow row in dt.Rows)
{
// On aliment la collection files
}
Parallel.ForEach(files, item => item.SaveFile(dal));

Y a plus dur, quand même…
Au final, je génère mes 1518 fichiers (j’ai 12 doublons) en 113 secondes  (~2minutes).
Autrement dit, je créé plus de 13 fichiers par secondes !
Qui dit mieux ???

Le seul problème réside donc dans la première boucle, sur la DataTable pour alimenter ma collection. J’ai un autre jeu de 94k pièces jointes et ça pêche un brin :s

Pour plus d’infos sur le parallélisme et surtout les guidelines, je conseille ce PDF.

Pour info, j’ai dis que la DataTable n’était thread-safe qu’en lecture, mais on peut quand même utiliser le parallélisme :

Parallel.ForEach(dt.AsEnumerable(), row =>
{
// le code ici
});
Catégories :.Net, C#
  1. Aucun commentaire pour l’instant.
  1. 27/05/2011 à 20:04

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s

%d blogueurs aiment cette page :