Are C++ enums slower to use than integers?

C++EnumsProfilingPerformance

C++ Problem Overview


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

C++ Solutions


Solution 1 - C++

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.

Solution 2 - C++

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.

Solution 3 - C++

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

Solution 4 - C++

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.

Solution 5 - C++

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;
}

Solution 6 - C++

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.

Solution 7 - C++

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionB. DecosterView Question on Stackoverflow
Solution 1 - C++Matteo ItaliaView Answer on Stackoverflow
Solution 2 - C++Marcelo CantosView Answer on Stackoverflow
Solution 3 - C++Oliver CharlesworthView Answer on Stackoverflow
Solution 4 - C++DesignFirstView Answer on Stackoverflow
Solution 5 - C++SkizzView Answer on Stackoverflow
Solution 6 - C++Justin MorganView Answer on Stackoverflow
Solution 7 - C++sailordaveView Answer on Stackoverflow