使用c++枚举比使用整数慢吗?

时间:2022-09-01 11:56:14

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

我正在编写围棋程序。我应该用QVector 还是QVector 在哪里

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.

我想当然,用Player代替integer会比较慢。但我不知道还有多少,因为我相信使用enum是更好的编码方式。

I've done a few tests regarding assigning and comparing Players (as opposed to int)

我做了一些关于分配和比较玩家的测试(与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?

基本上,它只慢了10%。在继续之前,我还有什么要知道的吗?

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

编辑:10%的差异并不是我想象出来的,它似乎是针对Qt和QVector的。当我使用std::vector时,速度是一样的

7 个解决方案

#1


79  

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.

枚举在编译时被完全解析(枚举常量作为整数常量,enum变量作为整数变量),在使用它们时没有速度损失。

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:

一般来说,一般的枚举不会有比int大的底层类型(除非您在其中放入非常大的常量);在事实,§7.2¶5这是显式地说:

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.

枚举的底层类型是一个完整类型,它可以表示枚举中定义的所有枚举数值。它是实现定义的用于枚举的整数类型作为基础类型,但是除非枚举数的值不能适用于int或unsigned int,否则基础类型不应该大于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).

至于结果:您的测试方法可能没有考虑到在“正常”机器上运行代码时的正常速度波动;您是否尝试过多次(100次以上)运行测试并计算时间的平均值和标准差?结果应该是一致的:均值之间的差异不应该大于两个标准差之间的RSS2(假设像往常一样,波动是高斯分布)。

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

您可以做的另一个检查是比较生成的汇编代码(使用g+您可以使用-S开关获得它)。


  1. On "normal" PCs you have some indeterministic fluctuations because of other tasks running, cache/RAM/VM state, ...
  2. 在“正常”pc上,由于运行其他任务,缓存/RAM/VM状态,您会有一些不确定的波动……
  3. Root Sum Squared, the square root of the sum of the squared standard deviations.
  4. 根和的平方,根号下标准差的平方。

#2


43  

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

一般来说,使用enum应该对性能没有任何影响。你是怎么测试的?

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.

在微基准测试中建立任何关于性能的声明都是很危险的。有太多的外来因素扭曲了数据。

#3


17  

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

嗯,不应该慢。它们实现为整数。

#4


3  

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

例如,如果你使用Visual Studio,你可以在你拥有的地方创建一个简单的项目

     a=Player::EMPTY;

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

如果你右键单击“go to disassembly”,代码将会是

mov         dword ptr [a],0

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

所以编译器会替换enum的值,通常它不会产生任何开销。

#5


3  

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.

我做了一些测试,整数形式和enum形式并没有太大的区别。我还添加了一个持续约6%的char表单(这并不奇怪,因为它使用的内存更少)。然后我使用了一个char数组而不是一个向量,这比之前快了300% !因为我们还没有得到QVector是什么,所以它可能是一个数组的包装器,而不是我使用过的std::vector。

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).

下面是我使用的代码,使用Dev Studio 2005中的标准发布选项进行编译。注意,我已经对定时循环做了少量修改,因为问题中的代码可以被优化为空(您必须检查汇编代码)。

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

#6


2  

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.

如果您的测试产生不同的结果,那么测试本身可能会发生一些事情。要么是这样,要么是编译器的行为异常。

#7


2  

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:

这是依赖于实现的,而且enums和int都有可能具有不同的性能以及相同或不同的汇编代码,尽管这可能是次优编译器的标志。有些方法可以得到不同的结果:

  • QVector may be specialized on your enum type to do something surprising.
  • QVector可以专门针对enum类型做一些令人惊讶的事情。
  • 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.
  • 枚举不会被编译成int,而是“一些整数类型,不大于int”。int的QVector可能与some_integral_type的QVector不同。
  • 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.
  • 即使QVector不是专门化的,编译器也可以更好地在内存中对ints进行对齐,而不是对some_integral_type进行对齐,这导致在循环遍历enums或some_integral_type的向量时,缓存缺失率更高。

#1


79  

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.

枚举在编译时被完全解析(枚举常量作为整数常量,enum变量作为整数变量),在使用它们时没有速度损失。

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:

一般来说,一般的枚举不会有比int大的底层类型(除非您在其中放入非常大的常量);在事实,§7.2¶5这是显式地说:

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.

枚举的底层类型是一个完整类型,它可以表示枚举中定义的所有枚举数值。它是实现定义的用于枚举的整数类型作为基础类型,但是除非枚举数的值不能适用于int或unsigned int,否则基础类型不应该大于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).

至于结果:您的测试方法可能没有考虑到在“正常”机器上运行代码时的正常速度波动;您是否尝试过多次(100次以上)运行测试并计算时间的平均值和标准差?结果应该是一致的:均值之间的差异不应该大于两个标准差之间的RSS2(假设像往常一样,波动是高斯分布)。

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

您可以做的另一个检查是比较生成的汇编代码(使用g+您可以使用-S开关获得它)。


  1. On "normal" PCs you have some indeterministic fluctuations because of other tasks running, cache/RAM/VM state, ...
  2. 在“正常”pc上,由于运行其他任务,缓存/RAM/VM状态,您会有一些不确定的波动……
  3. Root Sum Squared, the square root of the sum of the squared standard deviations.
  4. 根和的平方,根号下标准差的平方。

#2


43  

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

一般来说,使用enum应该对性能没有任何影响。你是怎么测试的?

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.

在微基准测试中建立任何关于性能的声明都是很危险的。有太多的外来因素扭曲了数据。

#3


17  

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

嗯,不应该慢。它们实现为整数。

#4


3  

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

例如,如果你使用Visual Studio,你可以在你拥有的地方创建一个简单的项目

     a=Player::EMPTY;

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

如果你右键单击“go to disassembly”,代码将会是

mov         dword ptr [a],0

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

所以编译器会替换enum的值,通常它不会产生任何开销。

#5


3  

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.

我做了一些测试,整数形式和enum形式并没有太大的区别。我还添加了一个持续约6%的char表单(这并不奇怪,因为它使用的内存更少)。然后我使用了一个char数组而不是一个向量,这比之前快了300% !因为我们还没有得到QVector是什么,所以它可能是一个数组的包装器,而不是我使用过的std::vector。

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).

下面是我使用的代码,使用Dev Studio 2005中的标准发布选项进行编译。注意,我已经对定时循环做了少量修改,因为问题中的代码可以被优化为空(您必须检查汇编代码)。

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

#6


2  

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.

如果您的测试产生不同的结果,那么测试本身可能会发生一些事情。要么是这样,要么是编译器的行为异常。

#7


2  

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:

这是依赖于实现的,而且enums和int都有可能具有不同的性能以及相同或不同的汇编代码,尽管这可能是次优编译器的标志。有些方法可以得到不同的结果:

  • QVector may be specialized on your enum type to do something surprising.
  • QVector可以专门针对enum类型做一些令人惊讶的事情。
  • 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.
  • 枚举不会被编译成int,而是“一些整数类型,不大于int”。int的QVector可能与some_integral_type的QVector不同。
  • 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.
  • 即使QVector不是专门化的,编译器也可以更好地在内存中对ints进行对齐,而不是对some_integral_type进行对齐,这导致在循环遍历enums或some_integral_type的向量时,缓存缺失率更高。