C’est vraiment un problème simple:
Je programme un programme Go. Dois-je représenter le forum avec un QVector
ou un QVector
où
enum Player { EMPTY = 0, BLACK = 1, WHITE = 2 };
Je suppose que, bien sûr, l’utilisation de Player à la place d’entiers sera plus lente. Mais je me demande combien de temps, car je crois que l’utilisation d’ enum
est un meilleur codage.
J’ai fait quelques tests concernant l’atsortingbution et la comparaison des joueurs (par opposition à l’ int
)
QVector 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 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); }
Fondamentalement, ce n’est que 10% plus lent. Y a-t-il autre chose que je devrais savoir avant de continuer?
Merci!
Edit: La différence de 10% n’est pas le fruit de mon imagination, elle semble spécifique à Qt et QVector. Quand j’utilise std :: vector, la vitesse est la même
Les enums sont complètement résolus au moment de la compilation (constantes enum en tant que littéraux entiers, variables enum en tant que variables entières), il n’y a pas de pénalité de vitesse dans leur utilisation.
En général, l’énumération moyenne n’aura pas un type sous-jacent plus grand que l’ int
(sauf si vous y mettez de très grandes constantes); en fait, au § 7.2.2, il est explicitement dit:
Le type sous-jacent d’une énumération est un type intégral qui peut représenter toutes les valeurs d’énumérateur définies dans l’énumération. Le type intégral utilisé comme type sous-jacent pour une énumération est défini par l’implémentation, sauf que le type sous-jacent ne doit pas être plus grand que
int
moins que la valeur d’un énumérateur ne puisse tenir dansint
ouunsigned int
.
Vous devez utiliser des énumérations lorsque cela est approprié car elles facilitent généralement la lecture et la maintenance du code (avez-vous déjà essayé de déboguer un programme contenant des “nombres magiques”?: :S
).
En ce qui concerne vos résultats: votre méthodologie de test ne tient probablement pas compte des fluctuations de vitesse normales que vous obtenez lorsque vous exécutez du code sur des machines “normales” 1 ; Avez-vous essayé de faire le test plus de 100 fois et de calculer la moyenne et l’écart type de vos temps? Les résultats doivent être compatibles: la différence entre les moyennes ne doit pas être supérieure à 1 ou 2 fois le RSS 2 des deux écarts types (en supposant, comme d’habitude, une dissortingbution gaussienne pour les fluctuations).
Une autre vérification que vous pouvez faire est de comparer le code assembleur généré (avec g ++, vous pouvez l’obtenir avec le commutateur -S
).
En général, utiliser un enum ne devrait absolument pas faire de 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 bruit pur.
Tout à l’heure, j’ai compilé les deux versions à l’assembleur. Voici la fonction principale de chacun:
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
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 dangereux de baser toute déclaration concernant la performance sur des micro-benchmarks. Il y a trop de facteurs externes qui faussent les données.
Enums ne devrait pas être plus lent. Ils sont implémentés sous forme d’entiers.
si vous utilisez Visual Studio par exemple, vous pouvez créer un projet simple où vous avez
a=Player::EMPTY;
et si vous cliquez avec le bouton droit sur “aller au déassembly”, le code sera
mov dword ptr [a],0
Le compilateur remplace donc la valeur de l’énumération et ne génère normalement aucune surcharge.
Eh bien, j’ai fait quelques tests et il n’y avait pas beaucoup de différence entre les formes entières et entières. J’ai également ajouté une forme de caractère qui était toujours d’environ 6% plus rapide (ce qui n’est pas surprenant car elle utilise moins de mémoire). Ensuite, j’ai juste utilisé un tableau de caractères plutôt qu’un vecteur et c’était 300% plus rapide! Comme on ne nous a pas donné ce qu’est QVector, cela pourrait être un wrapper pour un tableau plutôt que le std :: vector que j’ai utilisé.
Voici le code que j’ai utilisé, compilé en utilisant les options de publication standard dans Dev Studio 2005. Notez que j’ai modifié la boucle temporisée avec une petite quantité car le code dans la question pourrait être optimisé à rien (vous devriez vérifier le code de l’assemblage) .
#include #include #include using namespace std; enum Player { EMPTY = 0, BLACK = 1, WHITE = 2 }; template LONGLONG TimeFunction () { vector vec; vec.resize (10000000); size_t size = vec.size (); for (size_t i = 0 ; i < size ; ++i) { vec [i] = static_cast (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 (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 () << endl; cout << "Integer form = " << TimeFunction () << endl; cout << " Player form = " << TimeFunction (10)> () << endl; cout << " Array form = " << TimeArrayFunction () << endl; }
Le compilateur doit convertir enum
en entiers. Ils sont intégrés à la compilation, donc une fois que votre programme est compilé, il est censé être exactement le même que si vous utilisiez les entiers eux-mêmes.
Si vos tests produisent des résultats différents, il se peut que quelque chose se passe avec le test lui-même. Soit ça, soit votre compilateur se comporte bizarrement.
Cela dépend de l’implémentation, et il est tout à fait possible que les énumérations et les ints aient des performances différentes et un code d’assemblage identique ou différent, bien qu’il s’agisse probablement d’un signe d’un compilateur sous-optimal. certains moyens d’obtenir des différences sont les suivants: