66 votes

Les enums C++ sont-ils plus lents à utiliser que les entiers ?

C'est un problème très simple :

Je suis en train de programmer un programme Go. Dois-je représenter le tableau avec un QVector<int> ou un QVector<Player>

enum Player
{
    EMPTY = 0,
    BLACK = 1,
    WHITE = 2
};

Je suppose que, bien sûr, l'utilisation de Player au lieu d'entiers sera plus lente. Mais je me demande dans quelle mesure, car je pense que l'utilisation de enum est un meilleur codage.

J'ai effectué quelques tests concernant l'affectation et la comparaison de Joueurs (par opposition aux int )

QVector<int> vec;
vec.resize(10000000);
int size = vec.size();

for(int i =0; i<size; ++i)
{
    vec[i] = 0;
}

for(int i =0; i<size; ++i)
{
    bool b = (vec[i] == 1);
}

QVector<Player> vec2;
vec2.resize(10000000);
int size = vec2.size();

for(int i =0; i<size; ++i)
{
    vec2[i] = EMPTY;
}

for(int i =0; i<size; ++i)
{
    bool b = (vec2[i] == BLACK);
}

En gros, c'est seulement 10% plus lent. Y a-t-il autre chose que je devrais savoir avant de continuer ?

Merci !

80voto

Matteo Italia Points 53117

Les enums sont complètement résolus au moment de la compilation (les constantes enum sont des littéraux entiers, les variables enum sont des variables entières), il n'y a pas de pénalité de vitesse à les utiliser.

En général, l'énumération moyenne n'aura pas un type sous-jacent plus grand que int (sauf si vous y mettez de très grandes constantes) ; en fait, au §7.2 ¶ 5, il est explicitement dit :

Le type sous-jacent d'une énumération est un type intégral qui peut représenter toutes les valeurs de l'énumérateur définies dans l'énumération. Le type intégral utilisé comme type sous-jacent d'une énumération est défini par l'implémentation. sauf que le type sous-jacent ne doit pas être supérieur à int sauf si la valeur d'un énumérateur ne peut pas tenir dans un int o unsigned int .

Vous devriez utiliser les énumérations lorsque c'est approprié, car elles rendent généralement le code plus facile à lire et à maintenir (avez-vous déjà essayé de déboguer un programme rempli de "chiffres magiques" ? :S ).

Quant à vos résultats, il est probable que votre méthodologie de test ne tienne pas compte des fluctuations normales de vitesse que vous obtenez lorsque vous exécutez du code sur des machines "normales". 1 ; avez-vous essayé d'exécuter le test plusieurs fois (100+) et de calculer la moyenne et l'écart-type de vos temps ? Les résultats devraient être compatibles : la différence entre les moyennes ne devrait pas être supérieure à 1 ou 2 fois l'écart type. 2 des deux écarts types (en supposant, comme d'habitude, une distribution gaussienne pour les fluctuations).

Une autre vérification que vous pouvez faire est de comparer le code d'assemblage généré (avec g++, vous pouvez l'obtenir avec la commande -S interrupteur).


  1. Sur les PC "normaux", il y a des fluctuations indéterminées dues à d'autres tâches en cours, à l'état de la mémoire cache, de la mémoire vive et du moteur, etc.
  2. Root Sum Squared, la racine carrée de la somme des carrés des écarts types.

44voto

Marcelo Cantos Points 91211

En général, l'utilisation d'un enum ne devrait faire absolument aucune différence en termes de performances. Comment avez-vous testé cela ?

Je viens de faire des tests moi-même. Les différences sont du pur bruit.

A l'instant, j'ai compilé les deux versions en assembleur. Voici la fonction principale de chacune :

int

LFB1778:
        pushl   %ebp
LCFI11:
        movl    %esp, %ebp
LCFI12:
        subl    $8, %esp
LCFI13:
        movl    $65535, %edx
        movl    $1, %eax
        call    __Z41__static_initialization_and_destruction_0ii
        leave
        ret

Joueur

LFB1774:
        pushl   %ebp
LCFI10:
        movl    %esp, %ebp
LCFI11:
        subl    $8, %esp
LCFI12:
        movl    $65535, %edx
        movl    $1, %eax
        call    __Z41__static_initialization_and_destruction_0ii
        leave
        ret

Il est hasardeux de fonder toute déclaration concernant les performances sur des micro-benchmarks. Il y a trop de facteurs étrangers qui faussent les données.

17voto

Oli Charlesworth Points 148744

Les Enums ne devraient pas être plus lents. Ils sont implémentés comme des entiers.

3voto

DesignFirst Points 141

Si vous utilisez Visual Studio par exemple, vous pouvez créer un projet simple où vous avez

     a=Player::EMPTY;

et si vous cliquez droit sur "aller au désassemblage", le code sera

mov         dword ptr [a],0

Le compilateur remplace donc la valeur de l'enum, et normalement il ne génère pas d'overhead.

3voto

Skizz Points 30682

J'ai fait quelques tests et il n'y avait pas beaucoup de différence entre les formes integer et enum. J'ai également ajouté une forme char qui était systématiquement plus rapide d'environ 6% (ce qui n'est pas surprenant puisqu'elle utilise moins de mémoire). Ensuite, j'ai simplement utilisé un tableau de caractères plutôt qu'un vecteur, ce qui était 300 % plus rapide ! Puisque nous n'avons pas été informés de ce qu'est QVector, il pourrait s'agir d'une enveloppe pour un tableau plutôt que le std::vector que j'ai utilisé.

Voici le code que j'ai utilisé, compilé à l'aide des options standard de Dev Studio 2005. Notez que j'ai légèrement modifié la boucle temporisée, car le code de la question pourrait être optimisé à l'infini (vous devriez vérifier le code d'assemblage).

#include <windows.h>
#include <vector>
#include <iostream>

using namespace std;

enum Player
{
    EMPTY = 0,
    BLACK = 1,
    WHITE = 2
};

template <class T, T search>
LONGLONG TimeFunction ()
{
  vector <T>
    vec;

  vec.resize (10000000);

  size_t
    size = vec.size ();

  for (size_t i = 0 ; i < size ; ++i)
  {
      vec [i] = static_cast <T> (rand () % 3);
  }

  LARGE_INTEGER
    start,
    end;

  QueryPerformanceCounter (&start);

  for (size_t i = 0 ; i < size ; ++i)
  {
    if (vec [i] == search)
    {
      break;
    }
  }

  QueryPerformanceCounter (&end);

  return end.QuadPart - start.QuadPart;
}

LONGLONG TimeArrayFunction ()
{
  size_t
    size = 10000000;

  char
    *vec = new char [size];

  for (size_t i = 0 ; i < size ; ++i)
  {
      vec [i] = static_cast <char> (rand () % 3);
  }

  LARGE_INTEGER
    start,
    end;

  QueryPerformanceCounter (&start);

  for (size_t i = 0 ; i < size ; ++i)
  {
    if (vec [i] == 10)
    {
      break;
    }
  }

  QueryPerformanceCounter (&end);

  delete [] vec;

  return end.QuadPart - start.QuadPart;
}

int main ()
{
  cout << "   Char form = " << TimeFunction <char, 10> () << endl;
  cout << "Integer form = " << TimeFunction <int, 10> () << endl;
  cout << " Player form = " << TimeFunction <Player, static_cast <Player> (10)> () << endl;
  cout << "  Array form = " << TimeArrayFunction () << endl;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X