Question

Je mis en place un (peut-être très non scientifique) petit test pour déterminer les frais généraux des fonctions virtuelles dans un seul niveau d'héritage et les résultats que j'ai obtenu étaient, eh bien, exactement la même chose lors de l'accès à la classe dérivée polymorphically ou lorsqu'on y accède directement. Ce qui était un peu surprenant est l'ordre de grandeur du temps de calcul qui est introduit lorsqu'une fonction est déclarée virtuelle (voir les résultats ci-dessous).

Y at-il tellement frais généraux lors de la déclaration des fonctions membres en tant que telle, et pourquoi est-il toujours présent même lorsque l'accès à la classe dérivée directement?

Le code est le suivant:

class base
{
public:
    virtual ~base() {}
    virtual uint func(uint i) = 0;
};

class derived : public base
{
public:
    ~derived() {}
    uint func(uint i) { return i * 2; }
};

uint j = 0;
ulong k = 0;
double l = 0;
ushort numIters = 10;
base* mybase = new derived;  // or derived* myderived = ...

for(ushort i = 0; i < numIters; i++)
{
  clock_t start2, finish2;
  start2 = clock();

  for (uint j = 0; j < 100000000; ++j)
        k += mybase->func(j);

  finish2 = clock();
  l += (double) (finish2 - start2);
  std::cout << "Total duration: " << (double) (finish2 - start2) << " ms." << std::endl;

}

std::cout << "Making sure the loop is not optimized to nothing: " << k << std::endl;
std::cout << "Average duration: " << l / numIters << " ms." << std::endl;

Résultats:

base* mybase = new derived; donne une moyenne de ~ 338 ms.

derived* myderived = new derived; donne une moyenne de ~ 338 ms.

L'élimination de l'héritage et la suppression des fonctions virtuelles donne une moyenne de ~ 38 ms.

C'est presque 10 fois moins! Donc, fondamentalement, si une fonction est déclarée virtuelle les frais généraux sera toujours identique présent, même si je ne l'utilise pas polymorphically?

Merci.

Était-ce utile?

La solution

y accéder « directement » est en train de faire le même travail que d'y accéder « indirectement ».

Lorsque vous appelez la fonction sur myderived, le pointeur stocké il pourrait pointer vers un objet d'une classe dérivée de derived. Le compilateur ne peut pas supposer qu'il est vraiment un objet derived, il est peut-être un objet d'une autre classe dérivée qui remplace la fonction virtuelle, donc il doit être l'envoi de fonction virtuelle comme dans le cas de mybase. Dans les deux cas, la fonction est recherché dans la table de fonction virtuelle avant qu'il ne soit appelé.

Pour appeler la fonction non-polymorphically, ne pas utiliser un pointeur:

derived myderived;
myderived.func(1); 

Lorsque vous supprimez les fonctions virtuelles, le compilateur peut inline l'appel de fonction afin que vous finissez essentiellement avec une simple boucle:

for (uint j = 0; j < 100000000; ++j)
    k += i * 2;

Ceci est beaucoup plus rapide puisque vous sauver la surcharge de 100.000.000 appels de fonction et le compilateur pourrait même être en mesure d'optimiser la boucle plus d'une manière, il ne serait pas s'il y avait un appel de fonction en elle.

Notez également que la différence entre la version inline et l'appel de fonction virtuelle serait beaucoup moins si la fonction a fait un travail réel. Dans cet exemple, le corps de la fonction prend presque pas de temps, de sorte que les coûts pour appeler la fonction l'emportent sur les coûts d'exécution du corps.

Autres conseils

Les fonctions virtuelles coûtent pratiquement rien. La plupart des problèmes réels de performance sont causés par des arbres d'appel inutilement touffue faire des choses que vous ne devinerez jamais un problème.

La façon dont je les trouve est en suspendant l'application plusieurs fois sous le débogueur, et examiner l'état, y compris la pile d'appels. Voici un exemple d'utiliser cette méthode pour obtenir un 43x speedup .

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top