IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C# Discussion :

Programmation GPU et C# sans CUDA


Sujet :

C#

  1. #1
    Membre à l'essai
    Profil pro
    Étudiant
    Inscrit en
    Août 2012
    Messages
    25
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Août 2012
    Messages : 25
    Points : 11
    Points
    11
    Par défaut Programmation GPU et C# sans CUDA
    Bonjour à tous,

    Dans le but d'optimiser mon logiciel codé en C# je souhaitais distribuer des calculs répétitifs au GPU plutôt que de les exécuter successivement avec le CPU. J'ai pu voir dans la littérature que CUDA était assez répandu, avec diverses bibliothèques open-source qui permettent de faire le lien C++/C#. Cependant, une de mes cartes graphiques est nVidia mais d'après leur site non CUDA donc je pense que c'est mort.

    J'ai pu trouver sur le site de Microsoft deux pistes intéressantes mais qui s'avèrent être aussi en C++. D'une part une bibliothèque de Microsoft Research, Accelerator V2, et d'autre part AMP C++.

    Cependant, la fonction que je souhaite activer étant solicitée assez souvent (pour une interface graphique en plus) il faut que ce soit réactif et je ne sais pas si recourir à ces systèmes nuirait à cela.

    Par ailleurs je ne sais pas non plus comment je dois m'y prendre pour inclure et utiliser une bibliothèque C++ dans mon projet C#.

    Quelqu'un connaîtrait-il une bibliothèque ou une façon de faire qui permettrait de distribuer les calculs aux GPU tout en restant en C#, de façon gratuite ou si ca n'existe pas quelque chose de fiable, ou pourrait m'expliquer comment m'y prendre pour inclure le C++?

    La plupart des articles que j'ai pu consulter ont plus d'un an, c'est pourquoi je ne parviens pas à savoir s'il y a eu du nouveau dans le domaine.

    Merci par avance,

    Mikaël

  2. #2
    Inactif  

    Homme Profil pro
    Ingénieur test de performance
    Inscrit en
    Décembre 2003
    Messages
    1 986
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur test de performance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2003
    Messages : 1 986
    Points : 2 605
    Points
    2 605
    Par défaut
    Bonjour.

    Sur Google, en tapant "C# Cuda", il y a deux liens intéressants :

    http://www.c-sharpcorner.com/uploadf...-with-C-Sharp/

    http://managedcuda.codeplex.com/

  3. #3
    Membre confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2011
    Messages
    269
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2011
    Messages : 269
    Points : 460
    Points
    460
    Par défaut
    Bonjour,

    Il faut savoir deux ou trois choses avec les GPU :
    - Le temps de transfert CPU <--> GPU est pénalisant.
    - La puissance de calcul d'un GPU vient des possibilités de parallelisation.

    Donc tu parles de calculs répétitif, mais sont-ils parallélisable?
    Si ce n'est pas le cas, utiliser un GPU ne sert a rien.

  4. #4
    Membre à l'essai
    Profil pro
    Étudiant
    Inscrit en
    Août 2012
    Messages
    25
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Août 2012
    Messages : 25
    Points : 11
    Points
    11
    Par défaut
    Bonjour,

    effectivement il faut que je rende mes calculs parallélisables avant de commencer à programmer le GPU, c'est pourquoi je commence par essayer de rendre ces calculs parallélisables sur CPU dans un premier temps, à l'aide de la méthode System.Threading.Tasks.Parallel.For(...).

    Ma fonction consiste à tracer le contour d'objets 3D, donc sur un plan 2D :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
     
    public static Geometry Create2DGeometryFromModel3D(Model3D model3D, Matrix3D transform2D)
    {
                    var gm = (GeometryModel3D)model3D;
                    var mesh = gm.Geometry as MeshGeometry3D;
     
                    var points = new Point3D[mesh.Positions.Count];
                    mesh.Positions.CopyTo(points, 0);
                    Matrix3D screenTransform = multiplyMatrix3D(gm.Transform, transform2D);
                    screenTransform.Transform(points);
                    Int32Collection indicies = mesh.TriangleIndices;
                            // Combined all triangle into one geometry (union)
                            Geometry g1 = new StreamGeometry();
     
                            for (int i = 0; i < indicies.Count; i += 3)
                            {
                                var g2 = new StreamGeometry();
                                using (StreamGeometryContext ctx = g2.Open())
                                {
                                    ctx.BeginFigure(new Point(points[indicies[i]].X, points[indicies[i]].Y), true, true);
                                    ctx.LineTo(new Point(points[indicies[i + 1]].X, points[indicies[i + 1]].Y), true, false);
                                    ctx.LineTo(new Point(points[indicies[i + 2]].X, points[indicies[i + 2]].Y), true, false);
                                }
                                g2.Freeze();
     
                                g1 = Geometry.Combine(g1, g2, GeometryCombineMode.Union, null);
                            }
                            g1.Freeze();
                            return g1;
    }
    La partie la plus couteuse en temps de calcul est "geometry.combine". Il est bien précisé sur msdn que cette fonction ne doit pas être utilisée pour une union... Cependant je n'ai pas réussi à utiliser des GeometryGroup comme ils le suggèrent...

    Le principal problème pour la parallélisation est que tous mes threads ont besoin d'un accès à "points" et à "g1". J'ai donc utilisé l'instruction lock, mais je ne pense pas que ce soit le plus optimal, car c'est la fonction Combine qui est la plus couteuse en temps et je la fait de façon non parallèle...
    Voilà mon code avec les lock, qui pose encore des problèmes pour l'instant de "Thread appelant ne peut pas accéder à l'objet car il n'en est pas propriétaire" à l'intérieur même du lock.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
     
    public static Geometry Create2DGeometryFromModel3D(Model3D model3D, Matrix3D transform2D)
            {
                        var points = new Point3D[mesh.Positions.Count];
                        mesh.Positions.CopyTo(points, 0);
     
                        Matrix3D screenTransform = multiplyMatrix3D(gm.Transform, transform2D);
                        screenTransform.Transform(points);
     
                         Int32Collection indicies = mesh.TriangleIndices;
                         // Combined all triangle into one geometry (union)
                         Geometry g1 = new StreamGeometry();
                         Parallel.For (0, (int)indicies.Count/3, i => {
     
                                var g2 = new StreamGeometry();
     
                                using (StreamGeometryContext ctx = g2.Open())
                                {
                                    lock (points)
                                    {
                                        ctx.BeginFigure(new Point(points[indicies[i * 3]].X, points[indicies[i * 3]].Y), true, true);
                                        ctx.LineTo(new Point(points[indicies[i * 3 + 1]].X, points[indicies[i * 3 + 1]].Y), true, false);
                                        ctx.LineTo(new Point(points[indicies[i * 3 + 2]].X, points[indicies[i * 3 + 2]].Y), true, false);
                                    }
                                }
                                g2.Freeze();
     
                                lock (g1)
                                {
                                    g1 = Geometry.Combine(g1, g2, GeometryCombineMode.Union, null);
                                }
     
                         });
                         g1.Freeze();
                         return g1;
    }
    Je ne sais pas trop manipuler la classe Geometry, avec les GeometryGroup ou PathGeometry donc si quelqu'un savait manipuler cela et savait rendre mon code parallèle (en changeant le Combine) ce serait vraiment génial !

    Merci d'avance !

  5. #5
    Membre à l'essai
    Profil pro
    Étudiant
    Inscrit en
    Août 2012
    Messages
    25
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Août 2012
    Messages : 25
    Points : 11
    Points
    11
    Par défaut
    J'ai essayé d'écrire un code parallèle un peu meilleur en utilisant la surcharge de Parallel.For avec variable locale. J'ai créé une classe pour cette variable locale qui possède en attributs tout ce dont j'ai besoin dans une boucle. Je me suis inspiré de l'exemple de msdn.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
     
    public class GeometryGroupAndPoint3D
            {
                public GeometryGroup geometryGroup { get; set; }
                public Point3D[] point3D { get; set; }
                public Int32Collection indicies { get; set; }
     
                public GeometryGroupAndPoint3D(GeometryGroup geometryGroup, Point3D[] point3D, Int32Collection indicies)
                {
                    this.geometryGroup = geometryGroup;
                    this.point3D = point3D;
                    this.indicies = indicies;
                }
            }
     
    public static Geometry Create2DGeometryFromModel3D(Model3D model3D, Matrix3D transform2D)
            {
                            Int32Collection indicies = mesh.TriangleIndices;
                            // Combined all triangle into one geometry (union)
                            GeometryGroup g1 = new GeometryGroup();
     
    //Lancement du calcul parallèle
     //i : paramètre qui s'incrémente, loop: paramètre de boucle, subtotal : variable locale du type GeometryGroupAndPoint3D
                            Parallel.For<GeometryGroupAndPoint3D>(0, (int)indicies.Count / 3, 
    () => new GeometryGroupAndPoint3D(g1, points, indicies)//Initialisation de la variable locale, effectuée au tout début
    , (i, loop, subtotal) =>
                            {
                                var g2 = new StreamGeometry();
     
                               //Tracé des points
                                using (StreamGeometryContext ctx = g2.Open())
                                {
                                    ctx.BeginFigure(new Point(subtotal.point3D[subtotal.indicies[i * 3]].X, subtotal.point3D[subtotal.indicies[i * 3]].Y), true, true);
                                    ctx.LineTo(new Point(subtotal.point3D[subtotal.indicies[i * 3 + 1]].X, subtotal.point3D[subtotal.indicies[i * 3 + 1]].Y), true, false);
                                    ctx.LineTo(new Point(subtotal.point3D[subtotal.indicies[i * 3 + 2]].X, subtotal.point3D[subtotal.indicies[i * 3 + 2]].Y), true, false);
                                }
                                g2.Freeze();
     
                            //Ajout du tracé à la collection
                                subtotal.geometryGroup.Children.Add((Geometry)g2);
                                return subtotal;
                            },
                            (x) => g1.Children.Add((Geometry) x.geometryGroup) //Action effectuée au sortir de la boucle
                            );
     
                            g1.Freeze();
                            return (Geometry)g1;
    }
    En utilisant une variable locale je pensait qu'il n'y aurait pas de problème d'accès aux ressources entre les threads. La partie de ma variable locale qui s'actualise est subtotal.GeometryGroup alors que les autres restent les mêmes dans toutes les boucles.

    J'ai choisi de faire cela, qui complique un peu, car le mot clé "lock" ne suffisait pas, j'avais quand même l'erreur "Le thread appelant ne peut accéder à l'objet car un autre thread en est propriétaire" à la ligne "ctx.BeginFigure".

    Cependant j'ai toujours la même erreur et je ne comprends vraiment pas comment c'est possible alors que mes ressources sont spécialement locales, la fonction doit être faite pour ca... Je ne comprends donc pas l'erreur de propriétaire...

  6. #6
    Membre à l'essai
    Profil pro
    Étudiant
    Inscrit en
    Août 2012
    Messages
    25
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Août 2012
    Messages : 25
    Points : 11
    Points
    11
    Par défaut
    Je pense que le problème se trouve dans le constructeur de ma variable temporaire GeometryGroupAndPoint3D. En effet, lorsque je fais le même algorithme avec des types simples (List<int>) je n'ai pas ce souci.

    J'ai comme l'impression que lorsque je crée ma variable locale avec (g1, points, indicies) (qui sont toutes les trois des variables "globales"), alors quand je souhaite y accéder avec subtotal.points, ce n'est qu'une référence, et que donc quand chaque thread essaie d'y accéder en même temps il y a conflit.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Parallel.For<GeometryGroupAndPoint3D>(0, (int)((indicies.Count) / 3), () => new GeometryGroupAndPoint3D(g1, points, indicies), (i, loop, subtotal) =>
                            {
                                var g2 = new StreamGeometry();
                                
                                using (StreamGeometryContext ctx = g2.Open())
                                {
                                    ctx.BeginFigure(new Point(subtotal.point3D[subtotal.indicies[i * 3]].X, subtotal.point3D[subtotal.indicies[i * 3]].Y), true, true); // erreur : "Le thread appelant n'est pas propriétaire"
    
                                }
                                subtotal.geometryGroup.Children.Add(g2); 
                                return subtotal;
    Comment spécifier dans le constructeur que les propriétés de l'objet doivent être dupliqués et pas juste être des références? Comment forcer cette opération?

    Merci d'avance, j'espère que le coeur du problème est là !

  7. #7
    Membre à l'essai
    Profil pro
    Étudiant
    Inscrit en
    Août 2012
    Messages
    25
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Août 2012
    Messages : 25
    Points : 11
    Points
    11
    Par défaut
    Bonsoir,

    au final je suis arrivé à avoir le fin mot de l'histoire... Après m'être convaincu qu'en C# les objets passés aux fonctions sont par défauts de type valeur (et non de type référence à moins de spécifier "ref"), j'en suis arrivé à la conclusion que la variable locale n'avait bien rien à voir avec mes variables globales, et que donc il n'était pas normal d'avoir des problèmes de possession de thread...

    Il semblerait que la fonction Parallel.For ait donc des problèmes avec les classes, étant donné que j'ai testé et tout fonctionne bien avec un algorithme semblable dont on passe un type simple (un tableau) en paramètre (variable locale) !

    J'ai donc créé un tableau avant la boucle Parallel.For, dont je remplis les cases à chaque itération, de manière parallèle !

    Je n'ai donc au final pas programmé sur GPU mais j'ai fini par réussir à parallèliser mon code...

    J'espère que ce topic pourra être utile à d'autres quand même...

    Bonne soirée !

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Programme qui s'arrête sans se terminer
    Par tnarol dans le forum C++
    Réponses: 0
    Dernier message: 30/12/2008, 17h16
  2. Cours de Programmation GPU pour les nuls
    Par MohEllayali dans le forum Développement 2D, 3D et Jeux
    Réponses: 4
    Dernier message: 24/08/2008, 14h29
  3. Réponses: 2
    Dernier message: 11/12/2007, 08h38
  4. Réponses: 4
    Dernier message: 15/06/2007, 19h59
  5. Programme vb6 copier liste sans répéter les noms
    Par nuage33 dans le forum VB 6 et antérieur
    Réponses: 14
    Dernier message: 26/10/2006, 10h06

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo