Developpez.com - Rubrique 2D-3D-Jeux

Le Club des Développeurs et IT Pro

CppCon 2014 - La programmation multicœur dans les jeux C++

Une vidéo de Jeff Preshing

Le 2014-12-22 19:44:33, par LittleWhite, Responsable 2D/3D/Jeux
Jeff Preshing, architecte logiciel à Ubisoft Montréal nous montre les contraintes liés à la programmation multicoeurs dans les jeux vidéos AAA.
Dans cette vidéo, vous comprendrez mieux comment découper la boucle principale du jeu afin de paralléliser les calculs et comment utiliser les nouveautés du C++11 pour les opérations atomiques.

Bonne vidéo.
  Discussion forum
9 commentaires
  • "CPU manages its cores at very low level" = programmation multicoeurs = mulitthreading

    https://software.intel.com/en-us/com...1#comment-form

    Il y a une différence entre thread et coeurs du CPU:


    * thread = file d'attente d'instruction asm de tous les programmes tournant sur l'OS, organisation ? pos = instruction du programme#1 , pos-1 = instruction du programme#2.
    * coeurs = vitesse d'éxécution d'instruction * nbr_coeurs
  • Astraya
    Membre chevronné
    "CPU manages its cores at very low level" = programmation multicoeurs = mulitthreading
    https://software.intel.com/en-us/com...1#comment-form
    Il y a une différence entre thread et coeurs du CPU:
    Dans le cas du task scheduler c'est idem, tu creer autant de thread que de core, et certaines architecture (Playstation par exemple) permette de lancer ton thread sur un SPU unique.

    Uh, cela me semble être une erreur. volatile ne fait qu'interdire au compilateur de faire le mariole avec les lecture/ecriture optimisées en registre, aucun mécanisme d'atomicité n'entre en jeu.
    Et côté pédagogique, il faut par ailleurs justement marteler que les volatile n'ont rien à voir avec l'atomicité car c'est une croyance ancrée par VC++ qui n'était ainsi pas conforme au standard.
    Pour le compilateur de Visual Studio (depuis 2003), le mot clé volatile empêche de re-ordering de la variable ==>http://msdn.microsoft.com/en-us/libr...=vs.85%29.aspx
    Je pense qu'il ne parle vraiment que du compilateur Visual Studio ici, les autres comme GCC je ne sais pas trop, il doit exister des options de compilations pour faire de même...

    On peut voir à partir de la 24min de la vidéo que le compilateur PowerPC fait un peu n'importe quoi, pourquoi changer l'ordre des instructions de manière aussi brutale ? On dirait plus un bug que de l'optimisation.
    Non c'est tout a fait normal, il existe 2 type de re-ordering, celle faite par le compilateur et celle faite par le processeur. Seulement 1% des programmeurs s'en souci car le re-ordering devient dérangeant lors d'une application mutlithread, et le plus souvent, l'utilisation de semarphore et mutex cache le problème au yeux du programmeur, ce qui n'est pas du tout le cas en lock free

    Pourquoi ne pas simplement utiliser un lock ?
    Le lock comme le semaphore apporte de la contention et des appels au système qui coute énormément de temps, tu peux avoir un gain de 10% sur un système comme le task scheduler. Il existe une autre petite méthode mais à utiliser avec précaution, c'est le spinlock ( utilisation des atomics oblige )

    pas optimal le code de l'exemple (try_pop/try_push): une semantics acquire/release va être suffisant et plus performant. là on a une semantics memory_order_seq_cst c'est tout à fait superflue
    C'est du psedo code, le but ici est de faire comprendre le raisonnement lié au lockfree queue.

    Il exite un très bon articile d'un autre lead de Ubisoft Montreal dans le livre Game Programming Gems 8 de Jean-François Dubé qui parle également du meme systeme. " Efficient and Scalable Mutli-Core Programming"
  • MoDDiB
    Membre expert
    *Erreur 404*

    Je répondais à ce post en fait : http://www.developpez.net/forums/d14...p-jeux-triple/
    ...
  • germinolegrand
    Membre expert
    les opérations atomiques bas niveau (similaires aux variables volatiles du C/C++).
    Uh, cela me semble être une erreur. volatile ne fait qu'interdire au compilateur de faire le mariole avec les lecture/ecriture optimisées en registre, aucun mécanisme d'atomicité n'entre en jeu.

    Et côté pédagogique, il faut par ailleurs justement marteler que les volatile n'ont rien à voir avec l'atomicité car c'est une croyance ancrée par VC++ qui n'était ainsi pas conforme au standard.
  • guillaume07
    Débutant
    pas optimal le code de l'exemple (try_pop/try_push): une semantics acquire/release va être suffisant et plus performant. là on a une semantics memory_order_seq_cst c'est tout à fait superflue

    Pourquoi ne pas simplement utiliser un lock ?
    car ça apporte de la contention
  • germinolegrand
    Membre expert
    Envoyé par guillaume07
    pas optimal le code de l'exemple (try_pop/try_push): une semantics acquire/release va être suffisant et plus performant. là on a une semantics memory_order_seq_cst c'est tout à fait superflue
    En effet. Mais c'est parce qu'il me semble qu'il précise que dans le vrai code c'est acquire/release (ce que je fais dans mon code aussi) et qu'il n'a pas souhaité compliquer l'exemple puisque le code est fonctionnel (l'optimisation se fait facilement lorsqu'on a étudié les barrières mémoire), ce qui est toujours le cas en C++11 lorsqu'on utilise les atomic.

    @Gugelhupf: Un seul thread est autorisé à appeler tryPush, et un seul thread est autorisé à appeler tryPop.
  • Bousk
    Rédacteur/Modérateur
    Je n'ai pas encore vu la vidéo, mais ce tryPush est une liste "thread-safe".
    Son utilisation est justement pas pour 2 threads fassent un tryPush mais pour un unique thread en écriture, et un unique en lecture.
  • Astraya
    Membre chevronné
    Intel a également fait de très bonne vidéo sur la création de task dans un game engine.

    https://software.intel.com/en-us/vid...threads-part-1
    https://software.intel.com/en-us/vid...threads-part-2
    https://software.intel.com/en-us/vid...threads-part-3

    Bon la troisième vidéo est plus branché sur leur techno TBB. Mais bon, c'est toujours bon a connaitre pour ceux que ça intéresse.
  • Gugelhupf
    Modérateur
    On peut voir à partir de la 24min de la vidéo que le compilateur PowerPC fait un peu n'importe quoi, pourquoi changer l'ordre des instructions de manière aussi brutale ? On dirait plus un bug que de l'optimisation.

    Aussi il y a un truc que je ne comprends pas :
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    T m_items[size]; 
    atomic<int> m_writePos; 
    // ... 
     
    bool tryPush(const T& item) 
     { 
        int w = m_writePos 
        if (w >= size) 
            return false; 
        m_items[w] = item; 
        m_writePos = w + 1; 
        return true; 
    }
    Pourquoi ne pas simplement utiliser un lock ? Prenons l'exemple où 2 threads exécutent en même temps la méthode tryPush :
    PS: On va commencer avec m_writePos = 0.

    Ligne du temps :
    1. Thread 1 : int w = m_writePos; -> m_writePos vaut 0, donc w vaut 0
    2. Thread 2 : int w = m_writePos; -> m_writePos vaut 0, donc w vaut 0
    3. Thread 2 : if (w >= size) return false; -> On considère qu'on ne dépasse pas size, donc on ne retourne pas false
    4. Thread 2 : m_items[w] = item; -> m_items[0] vaut itemX
    5. Thread 1 : if (w >= size) return false; -> On considère qu'on ne dépasse pas size, donc on ne retourne pas false
    6. Thread 1 : m_items[w] = item; -> m_items[0] vaut itemY -> On a écrasé la valeur itemX à la position m_items[0].


    C'est moi ou bien y a un problème ?

    Envoyé par germinolegrand 
    Et côté pédagogique, il faut par ailleurs justement marteler que les volatile n'ont rien à voir avec l'atomicité car c'est une croyance ancrée par VC++ qui n'était ainsi pas conforme au standard.

    Je ne comprenais pas non plus pourquoi j'avais cette croyance entre volatile et atomicité, ça devait venir de là