Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are C++ enums slower to use than integers?

It's really a simple problem :

I'm programming a Go program. Should I represent the board with a QVector<int> or a QVector<Player> where

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

I guess that of course, using Player instead of integers will be slower. But I wonder how much more, because I believe that using enum is better coding.

I've done a few tests regarding assigning and comparing Players (as opposed to 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);
}

Basically, it's only 10% slower. Is there anything else I should know before continuing?

Thanks!

Edit : The 10% difference is not a figment of my imagination, it seems to be specific to Qt and QVector. When I use std::vector, the speed is the same

like image 835
B. Decoster Avatar asked Jan 31 '11 14:01

B. Decoster


People also ask

Why we need enums advantages over macros?

In terms of readability, enumerations make better constants than macros, because related values are grouped together. In addition, enum defines a new type, so the readers of your program would have easier time figuring out what can be passed to the corresponding parameter.

Is enum just an int?

Enumerations are integers, except when they're not - Embedded.com. Home. Blog. Enumerations are integers, except when they're not.

Are C enums ints?

enums are not always ints in C. However, it is reasonable to assume they are built on integral types.


7 Answers

Enums are completely resolved at compile time (enum constants as integer literals, enum variables as integer variables), there's no speed penalty in using them.

In general the average enumeration won't have an underlying type bigger than int (unless you put in it very big constants); in facts, at §7.2 ¶ 5 it's explicitly said:

The underlying type of an enumeration is an integral type that can represent all the enumerator values defined in the enumeration. It is implementation-defined which integral type is used as the underlying type for an enumeration except that the underlying type shall not be larger than int unless the value of an enumerator cannot fit in an int or unsigned int.

You should use enumerations when it's appropriate because they usually make the code easier to read and to maintain (have you ever tried to debug a program full of "magic numbers"? :S).

As for your results: probably your test methodology doesn't take into account the normal speed fluctuations you get when you run code on "normal" machines1; have you tried running the test many (100+) times and calculating mean and standard deviation of your times? The results should be compatible: the difference between the means shouldn't be bigger than 1 or 2 times the RSS2 of the two standard deviations (assuming, as usual, a Gaussian distribution for the fluctuations).

Another check you could do is to compare the generated assembly code (with g++ you can get it with the -S switch).


  1. On "normal" PCs you have some indeterministic fluctuations because of other tasks running, cache/RAM/VM state, ...
  2. Root Sum Squared, the square root of the sum of the squared standard deviations.
like image 148
Matteo Italia Avatar answered Oct 02 '22 20:10

Matteo Italia


In general, using an enum should make absolutely no difference to performance. How did you test this?

I just ran tests myself. The differences are pure noise.

Just now, I compiled both versions to assembler. Here's the main function from each:

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

Player

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

It's hazardous to base any statement regarding performance on micro-benchmarks. There are too many extraneous factors skewing the data.

like image 33
Marcelo Cantos Avatar answered Oct 02 '22 20:10

Marcelo Cantos


Enums should be no slower. They're implemented as integers.

like image 39
Oliver Charlesworth Avatar answered Oct 02 '22 22:10

Oliver Charlesworth


if you use Visual Studio for example you can create a simple project where you have

     a=Player::EMPTY;

and if you right click "go to disassembly" the code will be

mov         dword ptr [a],0

So the compiler replace the value of the enum, and normally it will not generate any overhead.

like image 37
DesignFirst Avatar answered Oct 02 '22 22:10

DesignFirst


Well, I did a few tests and there wasn't much difference between the integer and enum forms. I also added a char form which was consistently about 6% quicker (which isn't surprising as it is using less memory). Then I just used a char array rather than a vector and that was 300% faster! Since we've not been given what QVector is, it could be a wrapper for an array rather than the std::vector I've used.

Here's the code I used, compiled using standard release options in Dev Studio 2005. Note that I've changed the timed loop a small amount as the code in the question could be optimised to nothing (you'd have to check the assembly code).

#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;
}
like image 34
Skizz Avatar answered Oct 02 '22 21:10

Skizz


The compiler should convert enum into integers. They get inlined at compile time, so once your program is compiled, it's supposed to be exactly the same as if you used the integers themselves.

If your testing produces different results, there could be something going on with the test itself. Either that, or your compiler is behaving oddly.

like image 22
Justin Morgan Avatar answered Oct 02 '22 21:10

Justin Morgan


This is implementation dependent, and it is quite possible for enums and ints to have different performance and either the same or different assembly code, although it is probably a sign of a suboptimal compiler. some ways to get differences are:

  • QVector may be specialized on your enum type to do something surprising.
  • enum doesn't get compiled to int but to "some integral type no larger than int". QVector of int may be specialized differently from QVector of some_integral_type.
  • even if QVector isn't specialized, the compiler may do a better job of aligning ints in memory than of aligning some_integral_type, leading to a greater cache miss rate when you loop over the vector of enums or of some_integral_type.
like image 20
sailordave Avatar answered Oct 02 '22 20:10

sailordave