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

Langage C++ Discussion :

Overhead de mémoire lié à l'héritage


Sujet :

Langage C++

  1. #1
    Membre habitué
    Homme Profil pro
    Doctorant en Astrophysique
    Inscrit en
    Mars 2009
    Messages
    312
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Astrophysique
    Secteur : Enseignement

    Informations forums :
    Inscription : Mars 2009
    Messages : 312
    Points : 176
    Points
    176
    Par défaut Overhead de mémoire lié à l'héritage
    Bonjour.

    Je bosse actuellement sur code C++ où j'essaye d'optimiser au mieux la consommation mémoire (c'est pour faire de l'astrophysique sur supercalculateurs). Donc comme souvent j'essaye de trouver le bon équilibre entre faire un code "lisible" pas trop horrible à utiliser, et optimiser partout où je peux (c'est tellement critique qu'il m'arrive de compacter plusieurs données qui tiennent sur quelques bits dans des "unsigned char"). Donc pour ne pas faire n'importe quoi, je voudrais être sûr de bien comprendre l'overhead de mémoire liée à l'héritage et aux membres virtuels.

    Je précise que ce qui m'intéresse c'est l'overhead par objet, l'overhead "static" par classe n'étant pas un problème pour moi.

    1) Est-ce que MaClasseB : public MaClasseA va ajouter un overhead mémoire si MaClasseA et MaClasseB ne comportent aucune méthode virtuelle ? (par exemple si MaClasseB se contente d'ajouter quelques fonctions à MaClasseA)

    2) Maintenant supposons que MaClasseA comporte n méthodes virtuelles : cela change-t-il quelque chose qu'elles soient pures ou non du point de vue de l'overhead mémoire (je ne pense que non, mais j'aimerai en être sûr) ?

    3) Toujours dans le cas où MaClasseA comporte n méthodes virtuelles : quel va être l'overhead pour chaque objet de type MaClasseB ? Un seul pointeur pour chaque objet indépendamment de n ? (+ un overhead static proportionnel à n dont je me fous un peu).


    4) Maintenant plus sioux, MaClasseC : public MaClasseA, public MaClasseB avec MaClasseA comportant nA méthodes virtuelles et MaClasseB comportant nB méthodes virtuelles. Quel est l'overhead pour chaque objet de type MaClasseC ?

    5) Et une dernière : MaClasseB : public MaClasseA et MaClasseC : public MaClasseB avec nA méthodes virtuelles dans A qui sont implémentées dans B ET dans C (donc celles qui sont appelées pour C "écrasent" celles de B) + nB méthodes virtuelles supplémentaires déclarées dans B qui sont implémentées dans C. Quel overhead pour chaque objet de type MaClasseC ?

    Merci infiniment à ceux qui m'aideront à y voir plus clair

  2. #2
    Rédacteur/Modérateur


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

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 128
    Points : 33 053
    Points
    33 053
    Billets dans le blog
    4
    Par défaut
    Bonjour,

    loin d'en connaître long sur le sujet, il me semble que
    1- si aucune méthode n'est virtual, il n'y aura aucun overhead. Ca revient à avoir une seule classe qui implémente tout.
    2- à partir du moment où il y a des méthodes virtual, il ya création d'une vtable qui contient le pointeur vers la méthode réelle. Si les méthodes sont virtual pure ça n'y changera rien, ça bloquera uniquement la compilation si elle manque.


    J'ai trouvé ceci à ce sujet
    http://en.wikipedia.org/wiki/Virtual_method_table
    http://www.learncpp.com/cpp-tutorial...virtual-table/

  3. #3
    Membre éclairé
    Avatar de Ekleog
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2012
    Messages
    448
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2012
    Messages : 448
    Points : 879
    Points
    879
    Par défaut
    1- Si la sous-classante ajoute des membres, il faudra essayer de compiler avec -Wpadding -Wpadded ou équivalent pour vérifier que l'alignement colle bien, et sinon, essayer de trouver un moyen pour organiser les membres dans le bon ordre.

    2- Au niveau de l'overhead pour quelle variable ? (virtuelle pure => non instanciable)

    3,4,5- Un pointeur sur fonction par fonction virtuelle par objet, je pense. Ou bien, peut-être, selon le compilo (peu probable), un int32 par objet, et après un lookup dans une table globale. (peu probable que le compilo suppose qu'il y aura moins de 65535 descendants) Il faudra essayer pour savoir, selon ton compilo. Au besoin, il est toujours possible d'émuler une vtable globale, mais ça risque de devenir lourd à gérer. Enfin, tu peux aller voir pour les projets C qui font de l'"héritage" avec des "fonctions virtuelles", pour savoir comment faire. (Il me semble que GCC fait ça, quelque part.) Mais je crois qu'ils font la technique du 1 pointeur / fonction / objet.

  4. #4
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Salut,
    En général les implémentations utilisent un vpointeur par 'chemin' d'héritage, i.e, ce n'est pas la profondeur de l'héritage qui compte mais sa largeur.
    Avec l'héritage virtuel, on a souvent en plus un pointeur en plus pour gérer les réajustements du pointeur this.
    En l'absence de fonctions virtuelles, il n'y a généralement pas d'impact (pas de vtable, pas de vpointeur).
    Le nombre de fonctions virtuelles n'a pas d'impact sur une instance. Comme tu le présents, ça n'impacte que la taille de la vtable qui est partagée par toutes les instances.
    virtuelle ou virtuelle pure n'impacte pas la taille de l'objet (ni la taille de la vtable d'ailleurs).

    Bien sûr tout cela peut varier d'un compilateur à l'autre et même selon les options de compilation.

    cf Implémentation des fonctions virtuelles. De mémoire, certains détails sont peut être approximatifs mais dans l'ensemble cela donne une idée de comment ça marche.

    Si cela s'y prête, tu peux faire du 'polymorphisme d'inclusion statique' avec le CRTP.

  5. #5
    Membre habitué
    Homme Profil pro
    Doctorant en Astrophysique
    Inscrit en
    Mars 2009
    Messages
    312
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Astrophysique
    Secteur : Enseignement

    Informations forums :
    Inscription : Mars 2009
    Messages : 312
    Points : 176
    Points
    176
    Par défaut
    Merci pour vos réponses.
    J'ai aussi eu l'occasion de regarder dans le bouquin "More Effective C++" ou tout cela est assez bien décrit.

    3D archi, merci pour le lien vers la FAQ sur le CRTP, je ne connaissais pas du tout et ça m'a l'air assez intéressant. De toutes façons, comme il y a une grosse partie de développement, pour l'instant je vais faire de l'héritage normal en gardant en tête les éventuelles optimisations futures qui seront nécessaires, et puis après je ferai peut être une version "low memory" si cela s'impose.

    J'ai fait quelques mesures sur des exemples simples et le vptr pèse 8 octets par objet sur les machines que j'utilise. C'est loin d'être négigeable !

    Par rapport à cette remarque :
    Ou bien, peut-être, selon le compilo (peu probable), un int32 par objet, et après un lookup dans une table globale. (peu probable que le compilo suppose qu'il y aura moins de 65535 descendants)
    , ce serait tellement bien qu'il y ait des options de compilations qui permettent de faire ce genre de chose. Passer de 8 octets à 2 (unsigned short int), serait quand même très appréciable (et ça m'éviterai d'avoir à faire une autre version "low memory"). Quelqu'un saurait-il si quelque chose de ce genre existe (je compile soit avec g++, soit avec intel icpc) ?

    Merci beaucoup.

  6. #6
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par Kaluza Voir le message
    J'ai fait quelques mesures sur des exemples simples et le vptr pèse 8 octets par objet sur les machines que j'utilise. C'est loin d'être négigeable !
    64 bits ?
    Si la variation de comportement est déterminée à la compilation, au delà de la pertinence de l'utilisation de l'héritage ou non tu auras quelque part une indirection et donc un pointeur. Tu ne fera donc pas mieux qu'un vpointeur et une vtable en terme de taille d'objet.
    Si la variation de comportement est totalement déterminée à la compilation, alors CRTP et autre approche générique pour garder un code OCP et permettre au codeur et au compilateur d'être plus efficace.


    Citation Envoyé par Kaluza Voir le message
    Par rapport à cette remarque :
    c'est juste une c..rie. Un vpointeur sera toujours plus efficace que n index sur une table globale.

  7. #7
    Membre habitué
    Homme Profil pro
    Doctorant en Astrophysique
    Inscrit en
    Mars 2009
    Messages
    312
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Astrophysique
    Secteur : Enseignement

    Informations forums :
    Inscription : Mars 2009
    Messages : 312
    Points : 176
    Points
    176
    Par défaut
    64 bits ?
    Vu qu'en général ce sont des machines avec au moins 10Tb de mémoire vive, c'est du 64 bits of course.

    c'est juste une c..rie. Un vpointeur sera toujours plus efficace que n index sur une table globale.
    J'utilise déjà la virtualité au minimum pour avoir le bon équilibre entre un code performant, lisible, maintenable et facilement extensible. Pour certaines applications c'est plutôt les perfs à l'exécution qui vont être recherchées, pour d'autre c'est plutôt l'occupation mémoire. Donc si il le faut, je ferais 2 versions : une "normale" et une "low memory", mais si une option de compilation me permettait de gagner 1TB facilement au détriment des perfs d'exécution je ne serais pas contre...

  8. #8
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Selon le contexte, certains compilateurs savent résoudre les appels des fonctions virtuelles et les transformer en appel statique (lorsqu'il n'existe par expl qu'une seule spécialisation de la fonction ou que seul des objets d'un type dérivé sont créés, etc.). Je ne sais pas jusqu'où peuvent aller ces optimisations pour réduire la taille des objets. La première chose est évidemment de travailler sur tes algos et tes types de données. Ensuite, ben, étudie ton compilateur. Lis sa doc et épluche toutes les options.

  9. #9
    screetch
    Invité(e)
    Par défaut
    Lorsque tu en es a calculer les octets de la table virtuelle, j'éspère qu'avant ca tu as bien optimisé tes algos et que tu as bien groupé tes objets en cache pour un accès mémoire efficace et que tu n'appèles pas des méthodes virtuelles a l'interieur d'une boucle.

    Si tu n'as que 2 ou 3 niveaux d'heritage, bien plus efficace qu'une méthode vituelle: faire 3 tableaux distincts d'objets qui sont tous de type A ou B ou C, et appliquer l'opération traiterA sur le premier tableau, traiterB sur le deuxième et traiterC sur le troisième.
    et faire ca de manière linéaire et coller tes A, B et C contigus en mémoire au lieu de faire new.

    si c'est vraiment la place mémoire qui risque de manquer, le plus gros problème que je vois c'est si tu alloues tes objets un par un ils vont tous avoir 16 octets d'overhead, a cause de l'overhead de l'allocateur mémoire. Alors ta table virtuelle, es tu sur que c'est bien le problème?

  10. #10
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 629
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut
    Salut,

    8 bytes, sur une machine 64 bits, cela correspond au type ptr_t, ce qui est normal, étant donné que c'est le type qui est justement sensé représenter un pointeur, et donc pouvoir représenter l'ensemble des adresses mémoire potentiellement accessibles.

    Cela correspond, finalement à un unsigned long long

    Mais, toute proportion gardées, 8 bytes, c'est à peine suffisant pour écrire la chaine "coucou" (en considérant son \0 terminal )

    Sur un système 64 bits, il faudrait vraiment que tu en arrives soit à n'avoir que des ressources mémoire extrêmement limitée (je parle de descendre bien en dessous de ce que la plupart des systèmes, meme embarqués, actuels propose), soit que tu aies des quantités phénoménale de données à manipuler pour que la présence d'un pointeur dans tes classes viennent à mettre le système à genoux

    Dis toi bien, meme en C, le pointeur est pour ainsi dire ton unité de base, et que si dois décider de te passer des pointeurs, tu dis littéralement adieux à la possibilité de gérer des données un tout petit peu complexes (adieux listes chainées et autre trucs du style )

    Je peux comprendre le principe qui essaye d'avoir une empreinte mémoire la plus faible possible, mais il ne faut quand meme pas exagérer

    En outre, bien avant de vouloir grapiller des miettes sur la taille de tes données, il me semble bien plus intéressant de gagner quelques gateaux entier en évitant, entre autres, les copies inutiles

    Ceci dit, si, vraiment, tu veux limiter l'empreinte mémoire, commence par appliquer stricto sensu le principe de substitution de Liskov, et par préférer le CRTP partout où l'héritage n'aurait pour seul objectif que de te permettre d'avoir des types présentant une interface commune (mais sans rapport les uns avec les autres)

  11. #11
    Membre éclairé
    Avatar de Ekleog
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2012
    Messages
    448
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2012
    Messages : 448
    Points : 879
    Points
    879
    Par défaut
    Citation Envoyé par Kaluza Voir le message
    [...]

    J'ai fait quelques mesures sur des exemples simples et le vptr pèse 8 octets par objet sur les machines que j'utilise. C'est loin d'être négigeable !

    Par rapport à cette remarque :
    , ce serait tellement bien qu'il y ait des options de compilations qui permettent de faire ce genre de chose. Passer de 8 octets à 2 (unsigned short int), serait quand même très appréciable (et ça m'éviterai d'avoir à faire une autre version "low memory"). Quelqu'un saurait-il si quelque chose de ce genre existe (je compile soit avec g++, soit avec intel icpc) ?

    [...]
    Si tu as moins de 256 classes qui héritent, tu peux même réduire à 1 octet.
    Si le compilateur ne propose pas d'option spécifique, alors tu peux faire quelque chose comme ceci, pour émuler la virtualité :
    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
    enum { F_FOO, N_FCTS_VIRT }; // [...]
    enum { T_BASE, T_DERIVED, N_HERITANTS }; // [...]
    class Base {
    protected:
       unsigned char inheritant_id; // Ou unsigned short, selon les besoins
    public:
       Base() : inheritant_id(T_BASE) {}
       // Pour chaque fonction "virtuelle", quelque chose comme :
       int foo(long bar);
       int foo_impl(long bar) {
          return static_cast<int>(bar);
       }
    };
    class Derived : public Base {
       int val;
    public:
       Derived() : inheritant_id(T_DERIVED), val(42) {}
       int foo_impl(long) {
          return val;
       }
    };
     
    void (*Base_vtable[N_HERITANTS][N_FCTS_VIRT])() = { // (Attention, je ne suis plus sûr de l'endroit où mettre les crochets)
       {&Base::foo_impl},
       {&Derived::foo_impl},
       // [...]
    };
     
    int Base::foo(long bar) {
       return this->*reinterpret_cast<int (Base::*) (long)>(vtable[inheritant_id][F_FOO])(bar); // Syntaxe à vérifier
    }
    N'ayant pas de compilateur sous la main pour tester, j'espère ne pas m'être trompé !

    Citation Envoyé par 3DArchi Voir le message
    [...]
    c'est juste une c..rie. Un vpointeur sera toujours plus efficace que n index sur une table globale.
    En effet, et j'en suis désolé.
    Est-ce à cause du downvote que je ne peux pas éditer mon message, pour l'indiquer en en-tête et éviter que quelqu'un d'autre ne fasse la même erreur que moi ?

    Citation Envoyé par screetch Voir le message
    Lorsque tu en es a calculer les octets de la table virtuelle, j'éspère qu'avant ca tu as bien optimisé tes algos et que tu as bien groupé tes objets en cache pour un accès mémoire efficace et que tu n'appèles pas des méthodes virtuelles a l'interieur d'une boucle.
    [...]
    Le cache évitera les soucis d'appel de méthode virtuelle dans une boucle. Non ?

    Citation Envoyé par koala01 Voir le message
    [...]
    Sur un système 64 bits, il faudrait vraiment que tu en arrives soit à n'avoir que des ressources mémoire extrêmement limitée (je parle de descendre bien en dessous de ce que la plupart des systèmes, meme embarqués, actuels propose), soit que tu aies des quantités phénoménale de données à manipuler pour que la présence d'un pointeur dans tes classes viennent à mettre le système à genoux
    1TB consommé avec 8 octets par objet, ça fait un certain nombre d'objets, non ?
    Si on prend 1TiB, pour simplifier les calculs, ça fait précisément 137438953472 objets. Donc, on peut considérer qu'il y a bien une quantité phénoménale de données à manipuler, je crois.

    Par contre, (question à l'OP) est-ce que ces 137438953472 objets sont bien tous distincts ? Parce que, si non, le volume de données pourrait probablement être bien diminué en utilisant quelque chose comme le DP flyweight, je crois.

  12. #12
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 629
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut
    Citation Envoyé par Ekleog Voir le message
    1TB consommé avec 8 octets par objet, ça fait un certain nombre d'objets, non ?
    Si on prend 1TiB, pour simplifier les calculs, ça fait précisément 137438953472 objets. Donc, on peut considérer qu'il y a bien une quantité phénoménale de données à manipuler, je crois.
    A vrai dire, je n'ai encore jamais vu un seul système (en ordinateur unique) atteignant le terra de mémoire (car on parle bien de la mémoire, là, non des données sauvegardées ), et il faut quand meme aussi compter sur les données à manipuler


    Mais tu as magnifiquement abondé en mon sens : avant d'en arriver à se poser des questions sur l'impact qu'un pointeur dans une structure peut avoir sur la mémoire, ce qui fait vraiment partie des micros optimisations, il y a, très certainement, d'autres choses à controler et à vérifier, non
    Par contre, (question à l'OP) est-ce que ces 137438953472 objets sont bien tous distincts ? Parce que, si non, le volume de données pourrait probablement être bien diminué en utilisant quelque chose comme le DP flyweight, je crois.
    Et, surtout, il faut éviter les copies inutiles, et, dans un sens, ca tombe bien car une classe ayant vocation à être dérivée (ou étant dérivée d'une autre) est forcément une classe ayant sémantique d'entité et, par conséquent, non copiable et non assignable

  13. #13
    Membre éclairé
    Avatar de Ekleog
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2012
    Messages
    448
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2012
    Messages : 448
    Points : 879
    Points
    879
    Par défaut
    Citation Envoyé par koala01 Voir le message
    A vrai dire, je n'ai encore jamais vu un seul système (en ordinateur unique) atteignant le terra de mémoire (car on parle bien de la mémoire, là, non des données sauvegardées ), et il faut quand meme aussi compter sur les données à manipuler
    Citation Envoyé par Kaluza
    (c'est pour faire de l'astrophysique sur supercalculateurs)
    [...]
    Vu qu'en général ce sont des machines avec au moins 10Tb de mémoire vive, c'est du 64 bits of course.
    Donc si ?

    Mais tu as magnifiquement abondé en mon sens : avant d'en arriver à se poser des questions sur l'impact qu'un pointeur dans une structure peut avoir sur la mémoire, ce qui fait vraiment partie des micros optimisations, il y a, très certainement, d'autres choses à controler et à vérifier, non
    Et, surtout, il faut éviter les copies inutiles, et, dans un sens, ca tombe bien car une classe ayant vocation à être dérivée (ou étant dérivée d'une autre) est forcément une classe ayant sémantique d'entité et, par conséquent, non copiable et non assignable
    Totalement d'accord. Mais l'impact de la virtualité peut quand même être assez important, pour des objets ayant peu de données membres mais ayant des fonctions virtuelles. Cet impact peut même dépasser la taille de l'objet lui-même, ou presque, je pense. Et 8 octets, quand on "compacte plusieurs données qui tiennent sur quelques bits dans des unsigned char", ça peut être très beaucoup.
    D'où mon idée de flyweight, si c'est possible.

  14. #14
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 629
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut
    Citation Envoyé par Ekleog Voir le message
    Donc si ?
    Ouppsss
    Totalement d'accord. Mais l'impact de la virtualité peut quand même être assez important, pour des objets ayant peu de données membres mais ayant des fonctions virtuelles. Cet impact peut même dépasser la taille de l'objet lui-même, ou presque, je pense. Et 8 octets, quand on "compacte plusieurs données qui tiennent sur quelques bits dans des unsigned char", ça peut être très beaucoup.
    Mais quand meme...
    Pour en arriver à doubler la taille des données en mémoire, il faudrait qu'aucune structure ne soit composée (au choix)
    1. de plus d'1 long long
    2. de plus de 2 long (ou deux int, car ils ont généralement la meme taille)
    3. de plus de 4 short
    4. de plus de 8 char
    et qu'aucune structure ne contienne aucun type de collection quel qu'il soit (ni tableau C style, ni chaine de caractère, ni liste, ni quoi que ce soit)

    A la limite, le simple fait de vouloir gérer des coordonnées en trois dimensions (ou en coordonnées spatiales) et des couleurs vraies fera que nous sommes déjà dans l'incapacité d'atteindre l'objectif qui serait de diviser l'utilisation mémoire par deux

    Tu me dira que, sur 10Tb de mémoire, gagner 10% est déjà pas si mal, mais il est beaucoup plus facile de les gagner en évitant de doubler la consommation suite à une copie inutile qu'en essayant de grapiller l'utilisation d'un pointeur, non
    D'où mon idée de flyweight, si c'est possible.
    Elle n'est pas forcément mauvaise ceci dit

  15. #15
    screetch
    Invité(e)
    Par défaut
    Citation Envoyé par Ekleog Voir le message
    Le cache évitera les soucis d'appel de méthode virtuelle dans une boucle. Non ?
    Ben si c'est toujours la même méthode virtuelle appelée, oui peut être (mais ou est l'intérêt de la méthode virtuelle dans ce cas), même pas sur; si c'est des méthodes différentes, non, chaque appel de méthode risque d'invalider le cache

  16. #16
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 629
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut
    En outre, il faut bien se rendre compte que la virtualité (qu'elle soit implicite comme en C# ou en java ou explicite comme en C++) des fonction est la base technique sur laquelle reposent tous les fondements du paradigme orienté objet!!!

    Je m'explique :

    La virtualité permet d'adapter le comportement d'un objet à son type réel (dynamique) même s'il passe pour être "du type de sa classe parent".

    Supprimer ou éviter d'avoir recours à la virtualité rend la substituabilité caduque (vu qu'il n'est plus possible d'adapter le comportement d'un objet à son type réel) et, de ce fait, c'est tout le chateau de carte dressé autour du principe de substitution de Liskov qui s'effondre.

    On se retrouve alors avec un langage structuré fortement typé comme il en existe tant d'autres

    Alors, bien sur, il est aussi possible de se passer, dans une certaine mesure du moins, de la virtualité en adoptant le paradigme générique et les template (vu que l'on parle de C++ )

    Mais le paradigme générique, tel que conçus par C++ ne permet l'adaptation des comportements qu'en phase de compilation et l'on perd donc malgré tout tout l'intérêt de l'évaluation du type réel à l'exécution.

    Bien sur, l'énorme avantage de C++ est qu'il n'oblige personne à recourir aux fonctions virtuelles, et je ne veux absolument pas dire ici qu'il faut impérativement avoir recours aux fonctions virtuelles

    Ce sur quoi je veux attirer l'attention, c'est sur le fait que les fonctions virtuelles doivent être utilisées si elles se justifient et qu'elles sont tout à fait justifiées dans le cadre d'une conception orientée objet.

    Si donc on en arrive à être "dérangé", d'une manière ou d'une autre et pour une raison ou une autre, par l'overhead qu'impliquent les fonctions virtuelles, c'est peut etre le signe qu'il est temps de revoir les besoins fondamentaux que doit couvrir le langage et qu'il est peut etre utile de remettre le choix de C++ en cause.

    Je ne sais pas si cela existe, mais, peut etre, un langage structuré non OO, fortement typé, proposant éventuellement la généricité pourrait il être considéré comme le langage le plus adapté aux besoins de ce projet particulier

  17. #17
    screetch
    Invité(e)
    Par défaut
    hmmm attention ici il y a le même "extrémisme" que ceux qui parlent dans le jeu vidéo des termes a la mode: "data oriented programming"
    le "data oriented programming" semble pour certains être un nouveau concept de programmation qui va a l'encontre de la programmation orientée objet; en effet, "fini l'encapsulation"; on fait comme j'ai indiqué plus haut: on a des listes d'objets qui ne sont que des données du même type et on applique la même opération dessus.

    Ca ne contredit pas la programmation objet; ca la déplace.
    Ca revient a dire que ce n'est pas l'objet lui-même qui se modifie, c'est un autre objet qui le modifie. On passe d'un paradigme:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    for object in liste:
      object->virtualOperation();
    a:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    for objectA in listeA:
      objectA->operationA();
    for objectB in listeB:
      objectB->operationB();
    qui est une étape intermédiaire (et celle ou les développeurs feignants s'arrêtent)
    a la version objet/data:

    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
    class Operation
    {
      virtual doOperation() = 0;
    }
     
    class OperationA
    {
      vector<A> listeA;
      virtual doOperation()
      { for a in listeA: a->operationA(); }
    }
     
    class OperationB
    {
      vector<B> listeB;
      virtual doOperation()
      { for b in listeB: a->operationB(); }
    }
     
    class Operations
    {
      for op in operations: op->doOperation();
    }
    on passe d'un code à N méthodes virtuelles (N le nombre d'objets), a un code a M méthode virtuelles (M le nombre de types). On a donc deux classes qui forment un tandem mais les A ne connaissent pas les B, et la classe principale ne connait ni les A ni les B mais seulement une interface abstraite.

    Tout est encapsulé, principe de liskov est respecté, mais on a optimisé le code en deplacant la boucle for, rien de plus!

  18. #18
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 629
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut
    J'avoue que je n'avais pas vu les choses sous cet angle

    C'est bien la preuve que l'on peut réfléchir toute une journée pour en arriver à écrire une demi bêtise malgré tout

    C'est d'autant plus grave que je suis le premier à prôner la délégation des tâches (re )

    J'émettrais cependant un bémol, sans tenter d'enlever quoi que ce soit à la qualité du principe:

    Le nombre d'opérations que l'on peut effectuer sur un objet est susceptible d'exploser très rapidement, et, surtout, d'avoir des portées distinctes.

    Ne risque-t-on pas (et je pose réellement la question ) d'assister à une explosion de l'interface de la classe opération ou, pire encore, à une explosion du nombre de classes d'opérations

    Car, si l'on se retrouve avec un hiérarchie composée de (Nombre de types * Nombre d'opérations possibles) d'opération (ou peut s'en faut), cela peut rapidement devenir ingérable

    De plus, si chaque type d'opération doit maintenir sa propre liste (de pointeurs ? ) d'objets (mais je présume que tu as simplement oublié le static dans ton exemple ), ce que l'on gagne (en terme d'utilisation de mémoire) en évitant la virtualité est perdu plusieurs fois rien que par l'existence de plusieurs instances d'opérations

    Comprend moi bien, je n'écarte pas l'idée du principe que tu présentes, très loin de là, je me contente ici de réfléchir à ses implications et aux écueils auxquels il faut être attentif afin d'éviter de faire pire que mieux

  19. #19
    screetch
    Invité(e)
    Par défaut
    en fait c'est clairement plus compliqué que ce que j'ai mis, ce n'est pas a généraliser a tous les concepts. Ca marche pour peu:
    - qu'il n'y ai pas trop de types différents: sinon on a un overhead 'ecriture non negligeable
    - qu'il y ait beaucoup d'instances, sinon c'est pas pratique

    j'utilise ce pattern précis dans mon moteur de jeu et je sais qu'il marche jusqu'a une certaine limite, a savoir: une boucle qui permet de faire une opération unique sur un objet dont il existe des centaines/milliers d'instances


    ce que tu as indiqué est très vrai: il y a de grosses limitations. Ce pattern n'est interessant que pour résoudre ce genre exact de problème, pas pour faire une bibliothèque d'interface utilisateur par exemple.

  20. #20
    Membre expert
    Avatar de Klaim
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Août 2004
    Messages
    1 717
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 717
    Points : 3 344
    Points
    3 344
    Par défaut
    Notez que dans le jeu video, une maniere simple d'optimiser des boucles d'update d'objets avec une fonction d'update virtuelle est de trier la liste des objets par adresse de la fonction d'update. De cette maniere, on les groupe par definition de fonction ce qui les groupes par types d'objet implicitement a updater en groupe.

    Par contre, si l'ordre d'update depends d'autres paramettres, cela deviens impossible a mettre en place.

Discussions similaires

  1. Réponses: 22
    Dernier message: 31/07/2015, 10h28
  2. Gestion mémoire, héritage et cast
    Par Nemix dans le forum Langage
    Réponses: 9
    Dernier message: 24/01/2013, 11h50
  3. fichier mappé en mémoire
    Par WinBernardo dans le forum Delphi
    Réponses: 7
    Dernier message: 01/12/2006, 09h38
  4. Vitesse de la mémoire vidéo
    Par Anonymous dans le forum x86 16-bits
    Réponses: 3
    Dernier message: 06/06/2002, 20h20
  5. Problème avec la mémoire virtuelle
    Par Anonymous dans le forum CORBA
    Réponses: 13
    Dernier message: 16/04/2002, 16h10

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