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 :

Events asynchrones en C++


Sujet :

C++

  1. #1
    Membre actif Avatar de Rewpparo
    Homme Profil pro
    Amateur
    Inscrit en
    Décembre 2005
    Messages
    170
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Amateur

    Informations forums :
    Inscription : Décembre 2005
    Messages : 170
    Points : 281
    Points
    281
    Par défaut Events asynchrones en C++
    Bonjour,
    Je fais un jeu complexe et multithreadé. J'ai décidé de faire communiquer les différents threads de manière asynchrone et générique par un système d'events. Mais j'ai l'impression d'avoir construit une usine a gaz, et j'ai peur que la performance finale ne soit très affectée par ce système.

    Les particularités de mes besoins sont qu'un même event doit pouvoir être multicasté sur plusieurs threads différents, et doit être effacé proprement quand tout le monde l'a traité.

    Je suis donc parti sur une classe Event qui ressemble a cela :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Event
    {
    private:
       unsigned m_refCount;
    protected:
       Event();
       virtual ~Event();
    public:
       void grab();
       void release();
       unsigned getCount();
    };
    je dérive ensuite event autant que nécessaire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    class InputEvent : public Event;
    class KeyboardEvent : public InputEvent;
    class KeyboardKeyEvent : public KeyboardEvent;
    class GameEvent : public Event;
    ...
    Je créé ensuite des classes EventReceiver et EventSender afin de gérer le routage et le stockage des events, ainsi que le comptage des référence. Elles seront templatées afin que la file puisse ne traiter qu'une catégorie d'events plus ou moins précise.

    Quand je recois un event, j'utilise typeid et dynamic_cast pour récupérer les informations.


    Ce design est idéal en terme de fonctionnalités, je sais que ce système d'events s'adaptera à tout ce que je vais lui balancer. par contre, entre le RTTI (dynamic_cast, destructeur virtuel) et le comptage de références, j'ai bien peur que ca ne soit beaucoup trop intensif. Il y a aussi le problème de la fragmentation mémoire, car j'aurais beaucoup d'events créés et détruits. Je pensais utiliser un allocateur pour limiter les dégâts.

    D'une manière générale, mon approche est elle réaliste ? Existe t il des optimisations afin d’alléger la charge ? Qu'utilisez vous pour transmettre des informations entre threads dans des programmes complexes ?

  2. #2
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 012
    Points : 23 145
    Points
    23 145
    Par défaut
    Bonjour,

    Pour les événements j'ai l'habitude de voir dans les bibliothèques un union avec un discriminant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    struct
    {
          enum TypeEvent{CLAVIER, SOURIS};
          union
          {
                    struct Clavier{}clavier;
                    struct Souris{}souris;
           };
           TypeEvent typeEvent;
    };
    Pourquoi chaque évent doit-il être traité par chaque thread? N'est-ce pas plutôt un problème de conception?
    Si tes threads partages trop de ressources, ils vont se bloquer les uns les autres constamment et ils ne seront au final presque aussi performant qu'un seul thread.

  3. #3
    Membre actif Avatar de Rewpparo
    Homme Profil pro
    Amateur
    Inscrit en
    Décembre 2005
    Messages
    170
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Amateur

    Informations forums :
    Inscription : Décembre 2005
    Messages : 170
    Points : 281
    Points
    281
    Par défaut
    Citation Envoyé par Neckara Voir le message
    Pour les événements j'ai l'habitude de voir dans les bibliothèques un union avec un discriminant :
    C'est le système que j'avais mis en place au début. Le problème de ce système, c'est que pour ajouter un nouveau type d'event, il faut modifier une classe centrale, et tout recompiler. J'aimerais que des systèmes puissent créer de nouveaux types d'events selon leurs besoins sans avoir besoin de changer le code du coeur du moteur, mais en utilisant des "canaux" génériques.
    L'approche avec les structs est d'une manière générale très 'C' et j'essaie justement de trouver mieux. C'est un bon fallback si ce que j'aimerais ne marche pas.

    Citation Envoyé par Neckara Voir le message
    Pourquoi chaque évent doit-il être traité par chaque thread? N'est-ce pas plutôt un problème de conception?
    Si tes threads partages trop de ressources, ils vont se bloquer les uns les autres constamment et ils ne seront au final presque aussi performant qu'un seul thread.
    Tous les events ne sont pas traités par chaque thread, mais peut être traité dans plusieurs, juste ceux qui en ont besoin.
    Dans un jeu vidéo, il y a beaucoup de ressources partagées. Faire toutes les communications sous forme d'events permet justement d'éviter qu'ils ne partagent un état. L'idée est que chacun garde les infos dont il a besoin sous la forme la plus pratique pour sa tache, et ils se tiennent au courant des changements. Et ce de manière asynchrone, justement pour ne pas bloquer.

  4. #4
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 012
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 012
    Points : 23 145
    Points
    23 145
    Par défaut
    Serait-il possible d'avoir un diagramme ou une présentation expliquant le rôle de chaque thread, ainsi qu'un exemple de fonctionnalité entraînant des échanges entre thread (exemple : gestion des collisions?) afin de mieux comprendre ce que tu fais ?

  5. #5
    Membre actif Avatar de Rewpparo
    Homme Profil pro
    Amateur
    Inscrit en
    Décembre 2005
    Messages
    170
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Amateur

    Informations forums :
    Inscription : Décembre 2005
    Messages : 170
    Points : 281
    Points
    281
    Par défaut
    Heu j'ai ca sur un paquet de papier, mais ça reste partiel. Il y a des systèmes où j'ai une vague idée, mais sans avoir mis ça sur papier car je ne les développerais pas avant un bail.
    Coté modèle, le concept général est d'avoir chaque unité du jeu potentiellement dans un thread différent. En fait j'utilise un scheduler simple, on lui donne un nombre de threads, on lui balance les unités et il les répartit la charge entre les threads disponibles.
    Coté vue, Il y aura un thread graphique, chaque unité le tient au courant de ses changements par l'envoi d'un event contenant la position, les infos d'apparence, etc...
    Coté contrôleur, le clavier/souris seront dans le thread graphique, le joystick et autres inputs louches dans leur thread a eux. Ils envoient des events (appui de touche, etc..) a un contrôleur qui transformera ça en commande (avancer, tourner a droite, tirer...) qui sera envoyée à l'unité contrôlée.

    C'est simplifié, mais c'est l'esprit.

  6. #6
    screetch
    Invité(e)
    Par défaut
    il y a quand meme pas mal d'optimisations possibles, ca depend de tes contraintes

    deja, si tu elimines le dynamic_cast et que tu appelles seulement une methode virtuelle, ca sera ca de gagne (les dynamic_cast sont tres tres chers, surtout ceux qui echouent) (enfin ils l'etaient en 2007 quand j'ai ete confronte au probleme)

    pourquoi un dynamic_cast? pourquoi pas un

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class IEvent
    {
      virtual void execute() = 0;
    };
     
    class MouseEvent : public IEvent
    {
      virtual void execute() /*override*/;
    };
    sinon, est-ce que le premier evenement traite est toujours le premier envoye (une FIFO) ou bien est-ce que les evenements peuvent etre traites dans un ordre aleatoire?

    j'ai deja realise un scheduler, qui demarre N threads (N le nombre de processeurs). Les "evenements" dont tu parles sont chez moi des taches, chaque tache a une methode virtuelle. Le resultat n'est pas horrible dans la mesure ou chaque tache a un peu de boulot a faire, cela compense l'appel de methode virtuelle.

    selon tes contraintes (ordre aleatoire, ou bien certains evenements sont assignes a certains threads particuliers) il y a des optimisations possibles, mais commencer avec une methode virtuelle n'est pas mauvais.

  7. #7
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 115
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 967
    Points
    32 967
    Billets dans le blog
    4
    Par défaut
    Citation Envoyé par Rewpparo Voir le message
    Coté modèle, le concept général est d'avoir chaque unité du jeu potentiellement dans un thread différent. En fait j'utilise un scheduler simple, on lui donne un nombre de threads, on lui balance les unités et il les répartit la charge entre les threads disponibles.
    Coté vue, Il y aura un thread graphique, chaque unité le tient au courant de ses changements par l'envoi d'un event contenant la position, les infos d'apparence, etc...
    Coté contrôleur, le clavier/souris seront dans le thread graphique, le joystick et autres inputs louches dans leur thread a eux. Ils envoient des events (appui de touche, etc..) a un contrôleur qui transformera ça en commande (avancer, tourner a droite, tirer...) qui sera envoyée à l'unité contrôlée.
    Je trouve que ça fait beaucoup de thread, et surtout beaucoup trop.

    Lors de mon passage dans le jeu-vidéo, on utilisait au total 4 threads il me semble
    - le thread principal
    - le thread de rendu
    - un thread pour le chargement et streaming des ressources
    - un thread audio

    Multiplier les thread ne fera qu'alourdir la logistique du logiciel, et surtout : quel intérêt ?
    Que chaque unité ait son propre thread ça semble sexy, mais va synchroniser le tout, c'est la galère.
    Alors que faire un update du dt sur chaque unité à chaque fois que nécessaire est enfantin. Et toutes tes unités évoluent du même dt.
    Idem pour les entrées clavier/joystick etc... quel intérêt réel ?! Le mieux étant de traiter la pile d'évènements à chaque évolution du dt.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  8. #8
    screetch
    Invité(e)
    Par défaut
    pourquoi vos threads sont ils si specialises? cela genere plutot pas mal de synchronisation non?

  9. #9
    Membre actif Avatar de Rewpparo
    Homme Profil pro
    Amateur
    Inscrit en
    Décembre 2005
    Messages
    170
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Amateur

    Informations forums :
    Inscription : Décembre 2005
    Messages : 170
    Points : 281
    Points
    281
    Par défaut
    Cette approche, un thread par système, a un inconvénient majeur : elle ne s'adapte pas au nombre de threads matériels (Article, valve)
    Les quad core ont déja 8 threads matériels, et on n'a pas 8 systèmes dans un jeu vidéo typique. Et la mode coté processeurs est à la multiplication des coeurs. Je veux un moteur qui puisse tirer parti des oridinateurs du futur. Entre ne pas utiliser 32 coeurs sur 48 ou perdre un peu de temps en synchro, le choix est vite fait. L'industrie du jeu vidéo s'accorde sur le fait qu'un jeu atomique sous forme de "jobs" est la meilleure façon de faire pour les nouveaux projets, et ceux qui ont un moteur avec un thread par système portent vers un système de job quand c'est réaliste.

    Dans tous les cas, le sujet qui m'intéresse n'est pas tant le modèle de threads que la recherche d'un moyen extensible et pratique pour les faire communiquer, sans trop sacrifier la performance.

  10. #10
    screetch
    Invité(e)
    Par défaut
    je disais le contraire, que des threads moins specialises comme tu le proposes necessitent moins de synchronisation

    pour on cas, je ferais avec une liste simplement chainee (une pile en fait) car ces piles peuvent etre implementees avec des operations interlock donc sans primitive de synchronisation (j'utilise simplement un semaphore pour signaler qu'un objet est en attente et pour reveiller un thread, mais pas de mutex)
    chaque thread retire la tete de la liste, qui est un objet avec une methode virtuelle Execute en gros (run, chez moi) et donc fait tourner le code. En gros.

    Ca veut dire que tous les threads peuvent executer toutes les taches. C'est facilement extensible (le scheduler est a la base du moteur, pas au sommet).

  11. #11
    Membre actif Avatar de Rewpparo
    Homme Profil pro
    Amateur
    Inscrit en
    Décembre 2005
    Messages
    170
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Amateur

    Informations forums :
    Inscription : Décembre 2005
    Messages : 170
    Points : 281
    Points
    281
    Par défaut
    Le scheduler est déjà fait, le système est suffisamment générique pour gérer n'importe quel job. Mais j'ai essayé regrouper tout ce qui concerne une unité dans un même job pour réduire la latence dans le traitement d'une unité, donc ça revient a un job par unité.
    Par contre je ne connais pas les opérations interlock, ça me semble intéressant. As tu un article sur le sujet ?

    Mais il y a des actions qui doivent être threadées en dur, je pense notamment aux graphismes, qui doivent, notamment sur mac, être exécutés dans le thread principal. D’où un système hybride, en l’occurrence inspiré du système utilisé par source de valve.

    Le fonctionnement du scheduler est une chose, mais il y a également des opérations transversales, par exemple une unité qui doit envoyer ses changements de position et d'apparence au moteur graphique qui est dans son thread a lui. Ou l'envoi par le joueur à l'unité de consignes (avancer, tirer) dérivées d'instructions clavier. C'est le genre de choses que je compte faire par event, et c'est surtout la dessus que je voulais du retour.

  12. #12
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 115
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 967
    Points
    32 967
    Billets dans le blog
    4
    Par défaut
    Citation Envoyé par screetch Voir le message
    pourquoi vos threads sont ils si specialises? cela genere plutot pas mal de synchronisation non?
    Alors je dois t'avouer que je ne sais pas. Mais j'ai finalement adopté ce fonctionnement qui me parait depuis tout à fait correct et logique.
    Mais il s'agissait d'un moteur commun destiné aux plateformes PC mais aussi PS3 et X360, si cela joue.
    De mon côté je m'occupais "que" des threads principaux, de loading et audio. Et la synchronisation n'était pas compliqué : je balançais mon job, et je demandais à chaque update s'il était prêt.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  13. #13
    Membre actif Avatar de Rewpparo
    Homme Profil pro
    Amateur
    Inscrit en
    Décembre 2005
    Messages
    170
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Amateur

    Informations forums :
    Inscription : Décembre 2005
    Messages : 170
    Points : 281
    Points
    281
    Par défaut
    Et comment tu communiquais par exemple entre le thread principal et le thread de rendu ? Des structures de données partagées ? Comment étaient elles sécurisées ?

  14. #14
    Membre actif Avatar de Rewpparo
    Homme Profil pro
    Amateur
    Inscrit en
    Décembre 2005
    Messages
    170
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Amateur

    Informations forums :
    Inscription : Décembre 2005
    Messages : 170
    Points : 281
    Points
    281
    Par défaut
    Houlla j'avais raté un post !

    Citation Envoyé par screetch Voir le message
    il y a quand meme pas mal d'optimisations possibles, ca depend de tes contraintes

    deja, si tu elimines le dynamic_cast et que tu appelles seulement une methode virtuelle, ca sera ca de gagne (les dynamic_cast sont tres tres chers, surtout ceux qui echouent) (enfin ils l'etaient en 2007 quand j'ai ete confronte au probleme)

    pourquoi un dynamic_cast? pourquoi pas un

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class IEvent
    {
      virtual void execute() = 0;
    };
     
    class MouseEvent : public IEvent
    {
      virtual void execute() /*override*/;
    };
    sinon, est-ce que le premier evenement traite est toujours le premier envoye (une FIFO) ou bien est-ce que les evenements peuvent etre traites dans un ordre aleatoire?

    j'ai deja realise un scheduler, qui demarre N threads (N le nombre de processeurs). Les "evenements" dont tu parles sont chez moi des taches, chaque tache a une methode virtuelle. Le resultat n'est pas horrible dans la mesure ou chaque tache a un peu de boulot a faire, cela compense l'appel de methode virtuelle.

    selon tes contraintes (ordre aleatoire, ou bien certains evenements sont assignes a certains threads particuliers) il y a des optimisations possibles, mais commencer avec une methode virtuelle n'est pas mauvais.
    Déja, un event est pour moi juste une séries de données que je veux transmettre d'un système a un autre. L'event ne sait ni comment ses données ont été générées, ni comment elles seront utilisées. C'est le récepteur de l'event qui est chargé d'agir en fonction des données de l'event. Je parle ici de communication entre les taches, pas des taches elles même.

    Pour l'input, j'avais pensé a une classe générique du style :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    class InputEvent : public Event
    {
    public:
       unsigned/Enum getDeviceType();
       unsigned getDeviceId();
       Vector3f getAxis(unsigned id=0);
       bool getButton(unsigned id);
    };
    Ca évite les static_cast, et la file d'event transmet directement des InputEvent, donc pas besoin de downcast du tout.

    Le problème c'est par exemple pour les commandes, qui sont des ordres envoyés par le contrôleur à l'unité. Il en existe de beaucoup de types. Je fais un jeu de vaisseaux spatiaux, nous aurons donc au début les commandes de pilotage (avancer, reculer, tourner..), mais nous rajouteront ensuite le combat, avec la commande de tir (en incluant l'orientation du canon au moment du tir), mais également les commandes pour gérer les systèmes du vaisseau (énergie, portes des hangars....) et sûrement beaucoup d'autres commandes au fur et a mesure de nos idées. Toutes ces commandes doivent passer par une abstraction commune, afin de devoir rajouter juste du code dans le contrôleur (donner la possibilité au joueur de commander la fonctionnalité), et dans l'unité (pour qu'elle agisse sur cette commande), sans avoir besoin de toucher quoi que ce soit entre les deux. Vu que le "tuyau" est générique, la spécificité du type est perdue, la seule solution est un downcast.
    En utilisant typeid au lieu de dynamic_cast, je peux déjà retrouver le type rapidement, et avoir un seul dynamic_cast a faire.
    Je peux également encoder un discriminant afin de retrouver le type plus vite, mais ça demande une convention pour les discriminants, et c'est plus difficile a maintenir que typeid.

Discussions similaires

  1. WebMethod avec un event (retour asynchrone)
    Par LaurentC33 dans le forum Services Web
    Réponses: 0
    Dernier message: 18/02/2015, 16h51
  2. [CF 2.0][Windows Mobile 6.1] Event asynchrone
    Par thibaud dans le forum Windows Mobile
    Réponses: 0
    Dernier message: 14/05/2010, 18h58
  3. DLL TCP Asynchrone et delegate/event
    Par marso dans le forum C#
    Réponses: 1
    Dernier message: 03/09/2008, 19h05
  4. architecture d'un programme client/serveur asynchrone (win)
    Par Heimdall dans le forum Développement
    Réponses: 2
    Dernier message: 05/09/2003, 23h59
  5. Réponses: 6
    Dernier message: 25/03/2002, 21h11

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