Archive

Archive for mai 2011

Feeds Of The Week #2

Deuxième édition des Feeds Of The Week 🙂

Je ne l’ai pas fais la semaine dernière par manque de temps et aussi et avant tout par flemme ^^

Donc, voici ce qui m’a semblé intéressant cette semaine (et un bout de la semaine dernière).

Généralités

Pourquoi la communauté Java doit arrêter de prendre de haut C# C’est pas moi qui le dit, c’est un développeur Java !!!

Ensuite, les données… Souvent un très gros problème quand on veut avoir un résultat réaliste. Quand on veut du contenu, on va voir le Lorem Ipsum. Très connu, il a été utile à de très nombreuses personnes. Mais quid des noms, prénoms et autres données du style ???  Data! I need more data! aborde le sujet.

Ensuite…les patterns. J’avoue que je n’en connais pas assez, alors une piqure de rappel est toujours utile.

Une connerie (encore) de l’UE, ou des gens qui parlent et font des lois sur des sujets qu’ils ne connaissent pas (et ne veulent visiblement pas connaître, ce qui est encore pire à mon sens…). Cookies Law: Ah the Irony!

Langage C#

Qui n’a jamais utilisé DateTime ? Personne, a priori. Comment les stocker, sous quelle forme ? Besoin que l’application soit internationale ou non ? C’est souvent des questions qui reviennent. Par contre, on se pose presque jamais la question des performances de DateTime. Et pourtant…voici deux billets qui en parlent : The Darkness Behind DateTime.Now et Is Twitter Good for Developers — and DateTime.Now (ce dernier pose aussi la question de Twitter pour les développeurs, c’est assez bien pensé, je vais peut-être m’y mettre !).
Bonus

Je suis tombé sur un autre billet (pas foutu de retrouver lequel, la honte…) qui pointait vers ce billet : Dégage, sale programmeur ! Publié fin 2008, mais toujours aussi vrai, hélas… Bon, je me console, c’est marqué « Consultant » sur mon contrat ^^

Et enfin, un peu d’actualité (de la semaine dernière) concernant Hadopi (cf. mon commentaire au sujet du billet sur les cookies, même combat…) : TMG / Hadopi : les questions auxquelles il faudra répondre. J’ai trouvé l’article bien construit, bien réalisé et qui pose un certain nombre de questions intéressantes. [Troll] Pour une fois qu’un journaliste à l’air de savoir de quoi il parle… ^^

Publicités
Catégories :.Net, Divers, Feeds

[C#4] Parallélisme – Episode 2

Le parallélisme, c’est bon, c’est bien, c’est magnifique (prononcer mag-ni-fique).

J’ai parlé hier de mes 94k lignes à remonter. C’est des pièces jointes et faut que je les enregistre (copie) autre part. Les chemins sont tous stockés en base.

Premier jet : je récupère mes lignes, je les stocke dans une liste d’objets FileToSave (cf. billet d’hier) et je travaille dessus, voir je fais les deux en une seule boucle.
Ca donne 140 secondes pour 350 fichiers, soit 2.5 fichiers / secondes.
En extrapolant, avec mes 94.333 fichiers, ça donne 37.733 secondes, soit ~629 minutes, soit ~10 heures de traitements… Pas cool, ça…

Alors, comment procéder ?

Ma première pensée a été de prendre des blocs plus petits.
En regardant les données, mes pièces jointes sont de deux types.
Le premier type comprend 3078 pièces jointes. Avec un bloc de cette taille, je reprends grosso modo la même méthode qu’hier.

Ce qui me laisse un bloc de 91.255 pièces jointes.
Celles-ci, je dois les copier dans des répertoires, par années. J’ai 13 années (1999 -> 2011).
En décomposant ma requête principale par année, ma plus grosse année comprend 13k pièces jointes.

Donc, je procède comment ?
Voici le descriptif :

  • récupération des années, en BDD
  • pour chaque année
  •           créer une entrée dans un dictionnaire (la clef est l’année), la valeur dans le dictionnaire est une liste
  •           récupérer les pièces jointes de l’année, en BDD
  •           pour chaque pièce jointe
  •                    ajouter la pièce jointe dans la liste de l’année

Avec le parallélisme, ça donne quoi ?

ConcurrentDictionary<string, BlockingCollection<FileToSave>> pjs = new ConcurrentDictionary<string, BlockingCollection<FileToSave>>();
// Récupération des différentes années
dt = dal.ExecuteCommand Queries.YEARS_FOR_PJs);
// Pour chaque année
Parallel.ForEach(dt.AsEnumerable(), row =>
{
    string year = row["FILEYEAR"].ToString();
    // Création de la liste sur l'année courante
    pjs.TryAdd(year, new BlockingCollection<FileToSave>());
    // Récupération des PJs sur l'année courante (pour la DAL, je l’ai pas encore refaite…)
    DataTable innerDt = new GenericDal(dal.ConnectionString).ExecuteCommand(
        String.Format(Queries.PJs_By_YEAR, year));
    // Pour chaque pièce jointe
    Parallel.ForEach(innerDt.AsEnumerable(), innerRow =>
    {
        // On ajoute la pièce jointe à la liste des PJs de l'année courante
        pjs[year].Add(
            new FileToSave()
            {
                Type = FileType.FileSystem,
                CurrentPath = innerRow["FILEPATH"].ToString(),
                TargetName = innerRow["FILENAME"].ToString(),
                TargetPath = Path.Combine(basePath, year)
            });
    });
    // Le traitement sur l'année est achevée, on affiche le nombre de PJs pour faciliter le contrôle
    Console.WriteLine("==> Année {0} achevée avec {1} fichiers.", year, pjs[year].Count);
});

Sur la console, j’obtiens ceci :

Parallelisme Affichage de la console

Donc, là, j’ai un dictionnaire parfaitement exploitable et 12 secondes, si c’est pas magique, ça !!!

Après, comment je le traite ?

Parallel.ForEach(pjs.Keys, key =>
{
    i = 0;
    Parallel.ForEach(pjs[key], item =>
    {
        if (i++ % 100 == 0)
        {
            Console.WriteLine("Année {0}, fichier #{1} / {2} : {3}", key, i, pjs[key].Count, timerPJs.ElapsedMilliseconds);
        }
        item.SaveFile(dal);
    });
});

Et là, la console me dit que, en 74 secondes, j’ai copié 1379 fichiers pour un total de 681Mo.
Autrement dit, 18 fichiers par secondes. En extrapolant, ça fait 85 minutes pour mes 91k fichiers.

Grâce au parallélisme, je suis donc passé d’un temps théorique de 10h à un temps théorique de 2h. Y a de quoi être content, non ? 🙂

Sinon, pourquoi deux boucles ?
Principalement parce que j’ai constaté que si je fais les 2 opérations en même temps, c’est bien plus long (de l’ordre de x2 sur mon poste). De plus, c’est deux opérations bien distinctes, pour moi : récupération des données, copie des fichiers, ce qui veut dire qu’elles peuvent ne pas être faites successivement (ou, pour les tests, j’ai parfois besoin QUE la récupération, histoire de vérifier que j’ai bien tous mes fichiers, par exemple).

Pour ma machine, c’est un Intel Core 2 CPU, 1.86GHz, 3.25Go de RAM, donc c’est pas non plus une bête de course !

Mais, pour le coup, lors des traitements, les 2 cœurs…sont assez paisibles.

Parallelisme Utilisation CPU

Parallelisme Utilisation CPU

La marque rouge indique le début du traitement, j’ai une pause entre la récupération des données et la copie des fichiers. Au total, 500 ont été copiés pour cet exemple (le dernier pic sur le bord droit est l’arrêt du test).

Catégories :.Net, C#

[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#

Feeds Of The Week #1

De façon régulière (tous les vendredis soirs, je pense), de faire un billet avec tous les liens qui m’ont été utiles dans la semaine ou que je trouve pertinent.
Donc, voici le « Feeds of the week #1 » !

Les liens que j’ai trouvé utiles :

Back to basics: Usage of static members

Optional argument corner cases : part one, part two.

Une réflexion intéressante sur les commentaires : Don’t comment your code! Pour nous, développeurs français, il y a aussi le choix de la langue…

Task Parallel Library (plusieurs parties sont référencées).

Et une petite présentation de Regex Hero, un outil en Silverlight.

Les liens qui m’ont aidés cette semaine :

Je devais faire un peu de réflexion pour charger une classe à la volée, à partir de son namespace. Et là, le Type.GetType(string) retourne toujours null… Et oui, il faut le nom COMPLET…
Type.GetType(string typeName) returns null !?
Using Type.GetType(typeName)

En travaillant sur du Web Service REST, je devais aussi envoyer des libellés. Et là, à plusieurs reprise, je suis tombé sur des caractères spéciaux.
En XML, il n’y en a que cinq, alors autant les connaître !  Different ways how to escape an XML string in C#

Eh oui, c’est bien de faire des objets implémentant IDisposable, mais encore faut-il BIEN les faire et les penser. Une réflexion utile : IDisposable, and resource leaks: It could happen.

Catégories :.Net, Divers, Feeds

[C#] WebService – REST

Dans le billet précédent, on avait vu comment faire du Web Service, notamment avec WCF.

Dans ce cas là, il était destiné à être utilisé en web service via SOAP (avec une enveloppe).
Cependant, il peut arriver que l’on ait besoin d’avoir un service qui renvoie uniquement un flux XML. Par exemple, un composant JQuery qui appelle un web service et le traire en JSON.

Dans ce cas, l’enveloppe est totalement superflue et même gênante.
Comment faire ?
Et bien, utiliser REST. La différence majoritaire entre SOAP et REST, c’est que ce dernier ne possède que le corps du message, directement au format XML et que l’on peut attaquer le web service directement via URL.

L’implémentation…
Comme pour un service WCF, on va commencer par créer le contrat.

[ServiceContract(Namespace = "http://www.kerrubin.com/Test/2011/05", Name="RestService")]
public interface IRestService
{
	[OperationContract]
    [WebGet(UriTemplate = "Amazing?Name={name}", ResponseFormat = WebMessageFormat.Xml, BodyStyle = WebMessageBodyStyle.Bare)]
	string DoSomthingAmazing(string name);
}

Il est volontairement très simpliste.
Voyons dans le détail…
Les attributs ServiceContract et OperationContract sont censés être connus.
Le nouvel attribut est System.ServiceModel.Web.WebGet.
L’UriTemplate est la manière d’attaquer le service via URL, ici, on utilisera donc l’URL : RestService.svc/Amazing?Name=Kerrubin
On peut bien sûr mettre plusieurs paramètres, en cas de besoin.

Pour le service en lui-même :

public class RestService : IRestService
{
    public string DoSomthingAmazing(string name)
    {
        return String.Format("{0} have done something amazing!", name);
    }
}

Pas très dur, non ?

Après, il reste la configuration :

  <system.serviceModel>
    <behaviors>
      <endpointBehaviors>
        <behavior name="webHttp"><!-- Pour REST -->
          <webHttp />
        </behavior>
      </endpointBehaviors>
      <serviceBehaviors>
        <behavior name="publishMetaData"><!-- Permet de publier les méta données (WSDL) -->
          <serviceMetadata httpGetEnabled="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  <services>
    <service behaviorConfiguration="publishMetaData" name="RestService">
      <endpoint address="" behaviorConfiguration="webHttp" binding="webHttpBinding"
          contract="IRestService" /><!-- Endpoint spécifique pour REST -->
      <endpoint address="soap" binding="basicHttpBinding" contract="IRestService" /><!-- Endpoint pour un client SOAP -->
    </service>
  </services>
 </system.serviceModel>

Donc, on voit bien, ici, que l’on peut faire cohabiter un service classique SOAP avec un service REST.

Cependant, une limitation très importante : avec un service WCF, on peut créer sérialiseur custom, Inspector…
Avec REST, il faut oublier, ce n’est juste pas possible, ce n’est juste pas (encore ?) prévu pour.

Voilà, cela termine la première approche avec les web services.

Une petite chose encore. C’est très con, mais j’ai testé pour le fun.
Il est tout à fait possible de faire cohabiter service asmx, WCF et REST sur la même classe. On aura donc un contrat avec les attributs WCF (ServiceContract, OperationContract et WebGet) et une classe d’implémentation avec les attributs de l’asmx (WebService & WebMethod). Quant aux objets, ils auront les attributs des deux (DataContract + DataMember et XmlElement).
Ca sert strictement à rien, mais c’est quand même bon à savoir ! ^^

Catégories :.Net, ASP.Net, C#, Internet, WCF

[C#] Les Web Services

Dans ce billet un peu long (c’est pour ça que je fais une intro), on va voir un peu ce qui se fait dans le domaine des Web Services.

Enfin, surtout un bref aperçu donnant les bases pour les créer et les consommer. Et ce, que ce soit du asmx ou du WCF.

Bonne lecture !

Lire la suite…

Catégories :.Net, ASP.Net, C#, Internet, WCF