2012-12-18 2 views
2

Consultez le code suivant:Frais généraux de construction de classe de base vides?

#include <iostream> 
#include <type_traits> 

class Base 
{ 
    public: static int f() {return 42;} 
}; 

class Derived : public Base 
{ 
    protected: int x; 
}; 

class NotDerived 
{ 
    protected: int x; 
}; 

int main() 
{ 
    std::cout<<sizeof(Base)<<std::endl; 
    std::cout<<sizeof(Derived)<<std::endl; 
    std::cout<<sizeof(NotDerived)<<std::endl; 
    return 0; 
} 

Avec g ++ 4.7 -O3, il imprime:

1 
4 
4 

et si je comprends bien que, cela signifie que l'optimisation vide de classe de base est activée.

Mais ma question concerne les frais généraux d'exécution: la création est-il en tête (et destructing) un objet Derived par rapport à un objet NotDerived en raison du fait que Derived devrait construire/détruire l'objet Base correspondant?

+0

Je le pense, mais je ne pense pas que vous devriez vous inquiéter à ce sujet. Vous devriez simplement utiliser n'importe quel outil approprié pour votre travail. Traiter toute optimisation pendant la phase de profilage. – RonaldBarzell

+0

Ceci est un aspect critique pour mon code. Utiliser une classe de base vide m'aiderait beaucoup en termes de design, mais comme je travaille dans le High Performance Computing et que des milliards d'objets de ce genre seront créés pendant l'exécution, je devrais m'y intéresser ... – Vincent

+0

, mais la question pour moi est de savoir si vous devriez vous en préoccuper si tôt? Puisque vous avez les chiffres, avez-vous fait des projections pour voir où vous seriez? Il semble que si vous vous inquiétez au début, la meilleure chose à faire est de trouver des nombres durs pour les hits maximum acceptables, puis de les comparer avec votre compilateur, en gardant à l'esprit qu'un compilateur différent pourrait produire des résultats différents. .. – RonaldBarzell

Répondre

3

Alors que la norme ne fait aucune garantie à ce que je considérerais un compilateur qui a fait quelque chose de différent dans ces cas légèrement défectueux.

Il n'y a littéralement rien à faire pour initialiser la base: aucune mémoire ne doit être initialisée, aucun mécanisme d'appel virtuel ne doit être mis en place. Aucun code ne devrait être généré pour cela. Cependant, vous devriez toujours vérifier un assemblage dans un paramètre non trivial si cela est vraiment important pour vous.

2

La réponse à cette question dépendra de l'implémentation, puisque la norme ne spécifie que la sémantique.

Cependant, avec tout compilateur moderne et les optimisations activées, je m'attendrais à ne voir aucune différence.

Il n'y a pas de mémoire supplémentaire à allouer, il n'y a pas de code supplémentaire à exécuter, il n'y a pas de pointeurs vtable à modifier pendant la construction car la base supplémentaire n'est pas virtuelle. Vos constructeurs Derrived et NotDerrived peuvent très bien être identiques pour l'instruction.

Une fois l'optimisation désactivée, vous pouvez appeler une fonction Base::Base() vide sur certaines plates-formes, mais vous ne devriez pas vous soucier des performances des builds non optimisés.


J'ai mis en place une petite démo sur gcc.godbolt.org: http://tinyurl.com/cg8ogym

En bref

extern void marker______________________________________(); 
    // ... 
    marker______________________________________(); 
    new NotDerived; 
    marker______________________________________(); 
    new Derived; 
    marker______________________________________(); 

Compile à

call marker______________________________________()@PLT 
movl $4, %edi 
call operator new(unsigned long)@PLT 
call marker______________________________________()@PLT 
movl $4, %edi 
call operator new(unsigned long)@PLT 
call marker______________________________________()@PLT 

Si vous passez au-dessus clanger, il va même optimiser l'allocation de mémoire

0
#include <type_traits> 
#include <unistd.h> 

class SlowBase 
{ 
    SlowBase() { ::sleep(5); } 

    public: static int f() {return 42;} 
}; 
static_assert(std::is_empty<SlowBase>::value , "SlowBase is empty"); 

La classe Base prend cinq secondes pour construire !!

#include <type_traits> 

class FatBase 
{ 
    FatBase() = default; 
    int data[1024*1024];  
    public: static int f() {return 42;} 
}; 
static_assert(!std::is_empty<FatBase>::value , "FatBase is not empty"); 

Mais celui-ci ne prend pas de temps! Mon argument est que la surcharge de construction n'est pas liée à la taille d'une classe, elle est liée à ce que le constructeur fait.SlowBase est une classe vide mais est très lente à construire. FatBase est un mégaoctet de taille, mais ne met même pas à zéro les éléments du tableau, donc rien à faire. Par exemple, dans votre exemple Base, il y a un constructeur par défaut trivial implicitement déclaré, donc il n'a rien à faire.

Questions connexes