Astuces DotNet (Sébastien Courtois)

10/05/2010

[Nouveautés C# .NET 4] Code Contracts : Mettre ses contrats sur des interfaces

Filed under: .NET, C# 4, DevLabs, Hors Catégorie, Intermediaire — Étiquettes : , , , , , — sebastiencourtois @ 15:30

Ce post fait suite à une introduction sur les Code Contracts publié précedement.

Les codes contracts permettent de spécifier et contrôler des règles au sein de votre code. Dans l’optique de la création d’API, il est interessant d’utiliser les Code Contract au sein de classe mais il est encore plus judicieux de les utiliser sur des interfaces (notamment pour les tests unitaires / IoC …). Nous allons donc voir comment réaliser des “code contracts” sur des interfaces.

  • Pour cela mettons nous en situation …

public interfaceIPerson
{
    void SetAge(int age);
}

public interfaceICustomer : IPerson
{
    void SetId(int Id);
}

public classPerson: IPerson
{
    protected int Age { get; set; }

    public void SetAge(int age)
    {
        this.Age = age;
    }
}

public classCustomer: Person,ICustomer
{
    protected int Id { get; set; }
    public void SetId(int id)
    {
        this.Id = id;
    }
}

Nous créons deux interfaces IPerson pouvant modifier l’age d’une personne et ICustomer dérivant de IPerson (donc on pourra modifier son age aussi) où l’on pourra modifier son Identifiant client. Ces deux interfaces ont été implémenté dans deux classes Person et Customer. Si on prend le scénario de la création d’une API, le créateur de l’API aura défini et manipulera les interfaces et le développeur utilisant l’API, aura implémenté les deux classes.

  • Comment le créateur de l’API peut définir des spécifications sur les paramètres de ses interfaces ?

Le but pour le créateur de l’API est de fournir un modèle de développement. Il peut ainsi indiquer les méthodes et les types à utiliser mais ne peut pas avoir plus de précision quand au contenu des paramètres. On a vu, qu’avec Code contract, il peut définir ces informations au sein des méthodes. Cela reviendrait à créer une classe abstrait (pas toujours pertinent surtout dans un langage où il n’y a pas d’héritage multiple comme le C#).

Pour définir un code contract sur une interface, quatre étapes sont nécessaires :

  1. Créer une classe contrat héritant el ‘interface sur laquelle on souhaite faire le contrat.
  2. Implémenter les méthodes de l’interface dans la classe de contrat en fournissant les code contracts.
  3. Décorer l’interface avec l’attribut ContractClass en fournissant le type de la classe de contract
  4. Décorer la classe avec l’attribut ContractClassFor en fournissant le nom des interface qui sont régit par le contrat
[ContractClass(typeof(PersonContracts))]
public interface IPerson
{
    void SetAge(int age);
}

[ContractClassFor(typeof(IPerson))]
public class PersonContracts : IPerson
{
    public void SetAge(int age)
    {
        Contract.Requires(age > 0 && age < 120, "L'age doit être compris entre 1 et 120 ans.");
    }
}

Le tour est joué. Ainsi pour le code exemple devient :

[ContractClass(typeof(PersonContracts))]
public interface IPerson
{
    void SetAge(int age);
}

[ContractClassFor(typeof(IPerson))]
public class PersonContracts : IPerson
{
    public void SetAge(int age)
    {
        Contract.Requires(age > 0 && age < 120, "L'age doit être compris entre 1 et 120 ans.");
    }
}

[ContractClass(typeof(CustomerContracts))]
public interface ICustomer : IPerson
{
    void SetId(int Id);
}

[ContractClassFor(typeof(ICustomer))]
public class CustomerContracts : PersonContracts,ICustomer
{
    public void SetId(int Id)
    {
        Contract.Requires(Id > 0, "L'identifiant doit être supérieur à 0.");
    }
}

Le code ci dessus est fait par le créateur de l’API.

public class Person : IPerson
   {
       protected int Age { get; set; }

       public void SetAge(int age)
       {
           this.Age = age;
       }
   }

   public class Customer : Person,ICustomer
   {
       protected int Id { get; set; }
       public void SetId(int id)
       {
           this.Id = id;
       }
   }

Le code ci dessus est fait par l’utilisateur de l’API. Il n’a pas à se préoccuper des contrats définis dans l’API et peut définir ses propres contracts.

Publicités

09/05/2010

[Nouveautés C# .NET 4] Code Contracts

Filed under: .NET, C# 4, Débutant, DevLabs — Étiquettes : , , , , , , , — sebastiencourtois @ 16:12

Code Contracts est une fonctionnalité ajouté à .NET 4 cette année mais qui a, pendant des années, été un projet DevLabs sous le nom de Spec# ou Code Contracts. L’idée est de permettre au développeur de fournir des informations sur son code au travers du code lui même.

  • Un exemple concret vaut mieux que de long discours

Prenons un exemple d’une classe utilitaire.

public class Division
{
    public Division(decimal _num,decimal _denom)
    {
        this.Denominator = _denom;
        this.Numerator = _num;
    }

    public decimal Numerator { get; set; }
    public decimal Denominator { get; set; }

    public void Invert()
    {
        decimal tmpNum = this.Numerator;
        this.Numerator = this.Denominator;
        this.Denominator = tmpNum;
    }

    public decimal Compute()
    {
        return this.Numerator / this.Denominator;
    }

    public static decimal StaticCompute(decimal numerator, decimal denominator)
    {
        return numerator / denominator;
    }
}

public class Absolu
{
    public static decimal StaticCompute(decimal value)
    {
        return Math.Abs(value);
    }
}

La première classe de division permet de créer des fractions et d’éxécuter le résultat. Un méthode statique permet de faire la même opération sans créer une instance de la classe. Une deuxième classe qui permet, au travers d’une méthode statique, de récupérer la valeur absolu d’un nombre.

Si vous vous rappelez vos cours de mathématiques, vous savez surement qu’une valeur interdite dans le calcul d’une division est la valeur 0 pour le dénominateur. Si on regarde la méthode division, on voit que la valeur Dénominator peut être changé par le constructeur, par la propriété elle même (public get;set;), par la méthode Invert (si le numérateur = 0, alors l’invert donne un dénominateur = 0. Une solution avec .NET 3.5 serait de mettre un condition dans le set du numérateur afin de lancer une exception si une valeur = 0 tente d’être assigné. Toutefois cette façon de faire peut entrainer des codes dans les set assez long car il peut y avoir d’autres processus dans ce set ( NotifyPropertyChanged en WPF par exemple).

  • Contract Invariant / PréConditions (Requires)

Afin de sortir le code de validation des propriétés, Code Contract propose de créer une méthode dite “Invariant” où l’on mettra toute les conditions à vérifier lorsqu’une méthode ou un constructeur est exécuté. Pour la classe Divsion, on pourrait créer la méthode “Invariant” suivante :

[ContractInvariantMethod()]
protected void DivisionInvariant()
{
   Contract.Invariant(this.Denominator != 0, "Le dénominateur doit toujours être différent de 0.");
}

Ainsi si on tente de créer une instance Division d = new Division(2,0), on obtient l’exception suivantes

cc1jpg

Il est possible de faire cela sur des méthodes pour vérifier les paramètres par exemple.

public static decimal StaticCompute(decimal numerator, decimal denominator)
{
    Contract.Requires(denominator != 0, "Le dénominator doit être différent de 0.");
    return numerator / denominator;
}

Remarque : Le Contract.Requires fonctionne aussi pour les méthodes non statique.

  • PostConditions (Ensures)

Les méthodes ci-dessus permettent de vérifier les entrées. Il serait aussi intéressant de définir les sorties. Toujours grâce à vos rappels de mathématiques, vous savez que la méthode absolu renverra toujours une valeur supérieure ou égale à 0. Cela est une spécification d’une sortie de méthode. Ainsi la classe absolu ci-dessus, devient :

public class Absolu
{
    public static decimal StaticCompute(decimal value)
    {
        Contract.Ensures(Contract.Result<decimal>() >= 0);
        return Math.Abs(value);
    }
}

La méthode Ensures() indique que la condition qui suit est satisfait lors de la sortie de la méthode. La méthode Result() récupère le résultat afin de l’analyser. Il est aussi possible de vérifier l’état d’un paramètre au début de la méthode gràce à la méthode Contract.OldValue().

Si on remplace return Math.Abs(value) par return –1;, on obtient l’exception suivante :

cc2

  • Installation et Configuration

Afin d’utiliser les code contracts, il est nécessaire d’installer les outils Code Contracts : http://msdn.microsoft.com/fr-fr/devlabs/dd491992(en-us).aspx

Une fois installé, il est nécessaire d’aller dans les propriétés du projet pour activer Code Contracts :

cc3

L’écran ci-dessus dépend de votre version de Visual Studio.

    • VS Express 2010 : Aucune possibilité de d’activer Code Contracts
    • VS Pro 2010 : Runtime Checking  (Standard Edition)
    • VS Team System 2008 ou VS Premium / Ultimate 2010 : Runtime + Static Checkin.  (Premium Edition)

Juusqu’à maintenant, nous avons vu le Runtime Checking. Les vérifications se font lors de l’éxécution et génère des exceptions. Le Static Checking permet de voir ces problèmes dès la compilation lors d’une analyse statique de code.

  • Fonctionnement de Code Contracts

Les codes de vérifications Code Contracts ne sont pas utilisé uniquement à l’exécution mais aussi à la compilation. Lors de la compilation ,le compilateur analyse les codes et génère du IL pour l’insérer aux endroits nécessaire. Ainsi la méthode Compute de Division est représenté comme suit :

cc4 En Haut : Code généré sans code contract / En Bas : Code généré  avec Code Contracts

Il est donc possible de mettre ses codes Contracts.Requires/Contracts.Ensures dans n’importe quel ordre dans les méthodes car tout est reclassé lors de la compilation.

08/05/2010

[MS Research / DevLabs] Accelerator v2 : Exploiter vos GPU facilement en .NET

Filed under: .NET, DevLabs, Hors Catégorie — Étiquettes : , , , , , , , , — sebastiencourtois @ 17:47

Suite à ma visite sur les sites Internet de Microsoft Research et DevLabs, j’ai décidé de vous parler de ces projets de Microsoft qui seront dans les prochaines versions de .NET (ou pas). Dans les anciens project MS Research que l’on retrouve aujourd’hui au grand public, on pourrait cité WPF/Silverlight,Surface,PFX…

De ce post, nous allons parler d’une technologie dont le nom de code est “Accelerator”.

  • Présentation du contexte

De nos jours, les fabricants d’ordinateur nous fournisse des processeurs toujours plus puissants que ce soit en fréquence d’horloge qu’en nombre de cœurs. Ces nouvelles possibilités commencent à être exploiter correctement (notamment grâce à PFX de .NET 4).

Mais ce qui est moins exploiter est la puissance de calcul des cartes graphiques (GPU). Il faut savoir qu’une carte graphique peut avoir plusieurs processeurs dont chacun peut avoir de nombreux coeurs (jusqu’à 500 pour les plus récentes). Tout ce potentiel n’est utilisé que pour les jeux afin de faire des rendus et des calculs mathématiques afin de rendre des scènes 3D.

Le gros avantages des GPU est sa rapidité de calcul et sa gestion optimisé du parallélisme.

En revanche, les défauts des GPU par rapport aux CPU :

  • Les instructions sont limités aux opérations mathématiques 3D alors que le CPU peut avoir des opérations plus génériques
  • Les instructions dépendent beaucoup du constructeurs de la carte graphique (beaucoup plus que pour un CPU)
  • Il y a très peu d’API permettant d’accéder aux GPU directement (sans passer des API 3D).
  • Qu’est ce qu’Accelerator ?

L’idée du projet Accelerator est de fournir une API (Managé et C++) permettant d’envoyer du code sur ces GPU qu’il l’exécute.

D’autres ont déjà eu l’idée avant Microsoft :

  • Nvidia avec CUDA
  • ATI avec ATI Stream
  • Appel avec OpenCL
  • “Pixel Shaders”

Bien que ces API existent (encore au stade BETA pour la plupart), Microsoft a décidé de créé le projet Accelerator afin de fournir une API de développement pour les GPU indépendant de la plateforme et extensible.

  • Comment cela fonctionne ?

Accelerator se compose de deux gros blogs : Accelerator Library et Target Runtime.

image Structure général d’une application Accelerator

L’”Accelerator Library” est une API de développement pour préparer les données afin d’être exécutés sur le GPU. Cela se base sur un système abstrait à base d’arbre d’expression (nous le verrons plus loin). Cette API est composé d’une partie Native (C++) et d’une partie Managé (wrapper léger sur l’API native).

Le “Target Runtime” est une sorte de driver permettant de faire communiquer l’API avec les couches matériels (GPU … mais aussi CPU Multicore). Ce driver lit les arbres d’expressions d’”Accelarator Library” et envoie les instructions correspondantes aux GPU. Le driver est aussi en charge de la gestion de la charge sur les processeurs/cœurs. Microsoft fournit deux target runtime par défaut : Un DirectX9 (permettant d’accéder indépendamment à l’ensemble des carte graphiques du marché) et un pour les multi-coeurs CPU 64bits. Si vous souhaitez utilisé Accelerator sur d’autres types de processeurs, il suffit de développer un target runtime pour l’architecture cible.

Afin de comprendre le fonctionnement de Accelerator Library, prenons un exemple. Prenons deux tableaux contenant des valeurs à virgules flottantes (float). On souhaite additionner les cases une à une dans un troisième tableaux. Sur un CPU, on aurait une boucle for qui ferait les opération dans l’ordre des cellules et une cellule à la fois.

acceleratorv2 Exécution d’une addition sur un tableau (Sequential Program = CPU / Data Parallel Program = GPU)

Sur le GPU, le tableau est divisé et chaque cœurs à son propre lot de données à exécutés. Après exécution, le tableau est réassemblé à partir des résultats des tableaux de chacun des cœurs.

Afin que les opérations soient abstraites par rapport aux matériels sous jacent, Accelerator fournit un système d’arbre permettant de faciliter la création des instructions GPU. Par exemple, le graph suivant représente l’addition case par case de deux tableau puis la division de chacun des cases résultat par 2.

image Arbre d’expression Accelerator

  • Comment installer Accelerator ?

Accelerator v2 est actuellement disponible sur https://connect.microsoft.com/acceleratorv2.

Une fois installé, vous avez accès à  un document d’introduction et un document contenant l’ensemble des classes du projet. Je vous conseille le document d’introduction qui est très bien fait (j’en reprend un partie dans ce post).

Afin de développer, vous devez ajouter en référence l’assembly Microsoft.Accelerator.dll (contenu dans le répertoire C:\Program Files\Microsoft\Accelerator v2\bin\Managed).

Attention : Pour l’exécuter, il est nécessaire de placer la dll Accelerator.dll (se trouvant dans le répertoire C:\Program Files\Microsoft\Accelerator v2\bin\x86 ou C:\Program Files\Microsoft\Accelerator v2\bin\x64 selon votre architecture ) dans le répertoire où s’exécute l’application sous peine d’avoir l’exception : “DllNotFoundException : Unable to load DLL ‘Accelerator.dll’: Le module spécifié est introuvable. (Exception from HRESULT: 0x8007007E)”

  • Un Hello world “Accelrator”

En reprenant l’exemple d’addition case à case puis d’une division par 2, on obtient un code C# suivant (le reste du code a été enlevé afin de pas alourdir le code).

On commence par simplifier le code en utilisant des using.

using FPA = Microsoft.ParallelArrays.FloatParallelArray;
using PA = Microsoft.ParallelArrays.ParallelArrays;

FloatParallelArray représente un tableau de float sur la cible (carte graphique dans notre cas).. ParallelArrays regroupe les instruction disponible pour la cible.

DX9Target evalTarget = new DX9Target();

On crée le driver qui sera utilisé pour exécuter les opérations. Afin de faire simple, nous utiliserons le driver DirectX9 afin d’accéder aux GPU.

float[] inputArray1 = new float[arrayLength];
float[] inputArray2 = new float[arrayLength];
float[] stackedArray = new float[arrayLength];
/* Ajout de données dans les tableaux  inputArray1/inputArray2 */

On crée trois tableaux. Deux tableaux contenant les données d’entrée (inputArray1,2) que l’on remplit de valeurs diverses et un tableau pour les résultats.

FPA fpInput1 = new FPA(inputArray1);
FPA fpInput2 = new FPA(inputArray2);

On “transforme” les tableaux afin qu’il soit utilisables pour la cible.

FPA fpStacked = PA.Add(fpInput1, fpInput2);
FPA fpOutput = PA.Divide(fpStacked, 2);

On crée les opérations entre les tableaux. On utilise le résultat de l’addition pour la division. Cela génère l’arbre d’expression en copie d’écran plus haut.

evalTarget.ToArray(fpOutput, out stackedArray);

C’est la partie la plus importante du programme. En effet, jusqu’à maintenant nous avons préparé l’arbre d’expression et les données et c’est sur cette méthode, que l’arbre est évalué et que la cible (GPU dans notre cas) réalise les opérations. Le résultat est ensuite convertir et stocké dans un tableau de float .NET.

  • Et les performances alors ?

Il  est vrai que tout ça est un peu contraignant alors on est en droit de se demander si les performances sont aux rendez vous. Sur l’exemple ci dessus par exemple sur ma machine, les performances étaient meilleures en .NET qu’en GPU. La raison est, selon moi, que le temps de copie et d’éxécution est long pour le peu d’opérations exécutées.

Pour des données plus claires sur le domaine, je vous renvoie sur le PDF de MS Research parlant des performances : Document PDF . On peut voir que les performance peuvent être jusqu’à 70x plus rapide sur certains code par rapport au C++ sur CPU.

Le kit de développement contient aussi des samples (dont un jeu de la vie) qui permettent un peu de se rendre compte de ces performances.

Il est nécessaire de rappeler que ce projet est en alpha et donc loin d’être optimisé.

  • Conclusion

Ce projet est plein de promesses même s’il reste en dessous de technologies comme CUDA qui se rapproche du langage C alors que Accelerator est basé sur des arbres d’expressions / opérations mathématiques de bases. De plus Accelerator étant basé sur le parallélisme, il est nécessaire de rappeler que, comme PFX, tous les algorithmes ne sont pas faits pour ces technologies.

  • Quelques liens utiles :

Site du projet sur Microsoft Research (Attention c’est la v1 sur ce site)

Téléchargement de la v2

Article d’un français sur le sujet : Accelerator & F# / Accelerator : F# vs C#

Propulsé par WordPress.com.

%d blogueurs aiment cette page :