Publicité
+ Répondre à la discussion
Affichage des résultats 1 à 7 sur 7
  1. #1
    Invité régulier
    Étudiant
    Inscrit en
    août 2012
    Messages
    25
    Détails du profil
    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : août 2012
    Messages : 25
    Points : 5
    Points
    5

    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
    Expert Confirmé

    Homme Profil pro david
    Responsable développement
    Inscrit en
    décembre 2003
    Messages
    1 652
    Détails du profil
    Informations personnelles :
    Nom : Homme david
    Âge : 40
    Localisation : France, Haute Garonne (Midi Pyrénées)

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

    Informations forums :
    Inscription : décembre 2003
    Messages : 1 652
    Points : 2 849
    Points
    2 849

    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/
    Media Foundation video decoder mpeg1/mpeg2, MediaSource Kinect
    http://sourceforge.net/projects/mfnode/

    http://jeux.developpez.com/faq/directx/?page=dshow

  3. #3
    Membre éprouvé
    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 : 459
    Points
    459

    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
    Invité régulier
    Étudiant
    Inscrit en
    août 2012
    Messages
    25
    Détails du profil
    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : août 2012
    Messages : 25
    Points : 5
    Points
    5

    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 :
    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 :
    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
    Invité régulier
    Étudiant
    Inscrit en
    août 2012
    Messages
    25
    Détails du profil
    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : août 2012
    Messages : 25
    Points : 5
    Points
    5

    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 :
    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
    Invité régulier
    Étudiant
    Inscrit en
    août 2012
    Messages
    25
    Détails du profil
    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : août 2012
    Messages : 25
    Points : 5
    Points
    5

    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 :
    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
    Invité régulier
    Étudiant
    Inscrit en
    août 2012
    Messages
    25
    Détails du profil
    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : août 2012
    Messages : 25
    Points : 5
    Points
    5

    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.

Liens sociaux

Règles de messages

  • Vous ne pouvez pas créer de nouvelles discussions
  • Vous ne pouvez pas envoyer des réponses
  • Vous ne pouvez pas envoyer des pièces jointes
  • Vous ne pouvez pas modifier vos messages
  •