C ++枚举的使用是否比整数慢?


67

这确实是一个简单的问题:

我正在编写Go程序。我应该用aQVector<int>或a代表董事会QVector<Player>

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

我想当然会发现,使用Player而不是整数会更慢。但是我想知道还有多少,因为我相信使用enum是更好的编码。

我已经完成了一些有关分配和比较Player的测试(相对于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);
}

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

谢谢!

编辑:10%的差异不是我的想象力,它似乎特定于Qt和QVector。当我使用std :: vector时,速度是相同的


47
+1进行性能测试,然后再询问。真难得!
比约恩博动

您是否仍在使用调试信息运行这些性能测试?结果应该没有表现出任何性能差异,因此这似乎是缺少编译器优化的问题。或者可能是您用来测试性能的方法有问题。
汤米·安德森

“当然,我认为使用Player而不是整数会更慢。”,是什么让您这么认为呢?您是否在某处阅读或听说过它?
Matthieu M.

好吧,每个测试已经进行了大约10次。整数在399到421毫秒之间。枚举在429到438毫秒之间。这种差异的原因引起了我的兴趣。至于“我想当然,使用Player而不是整数会更慢。”,我可以反过来说:没有办法会比整数快(枚举不是布尔值),但这是一个纯粹的想法。猜测。
B. Decoster

1
真的很奇怪 我已经在打开和关闭优化的情况下对其进行了测试,并且两个测试都显示出明显的性能差异。我已经尝试检查程序集的输出,但是我唯一能弄清楚的是代码完全不同。也许它必须与QVector的技巧有关,以找出如何处理特定类型。这与编译器如何处理枚举和整数无关,这纯粹是QVector的错。并不是说它对现实生活有任何影响。
Sergei Tachenov 2011年

Answers:


82

枚举在编译时已完全解析(枚举常量作为整数文字,枚举变量作为整数变量),使用它们不会影响速度。

通常,平均枚举的基本类型不会大于int(除非您输入非常大的常量);实际上,在§7.2¶5中明确指出:

枚举的基础类型是整数类型,可以表示枚举中定义的所有枚举器值。由实现定义,哪种整数类型用作枚举的基础类型的基础类型,int 除非intunsigned int基础类型不得大于,除非枚举器的值不能适合or

您应该在适当的时候使用枚举,因为它们通常会使代码更易于阅读和维护(您是否曾经尝试调试充满“幻数”的程序?:S)。

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

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


  1. 在“普通” PC上,由于其他任务正在运行,缓存/ RAM / VM状态,...等,您可能会不确定地波动。
  2. 平方根的平方根,是标准差平方和的平方根。

17
有人告诉我,魔术数字是拼写“工作安全”(等级3)的主要组成部分之一。
corsiKa 2011年

2
我目前正在一个充满魔术字符串的系统上工作。'if(code.equals(“ A”)&& status.equals(“ T”)',依此类推。随着我们逐步弄清枚举,我们正在缓慢地建立枚举集。– 2011
杰伊

2
@Jay:那就是您通常在Daily WTF上看到的那种东西……可悲的是这种东西确实存在。:(
Matteo Italia

@ yes123:是的,我是:)呃...你是谁?
Matteo Italia

@ yes123:是',ora mi hai fatto incuriosireire ... che nick usi dilà?
Matteo Italia

45

通常,使用枚举对性能绝对没有影响。您是如何测试的?

我只是对自己进行了测试。区别在于纯噪声。

刚才,我将两个版本都编译为汇编器。这是每个的主要功能:

整型

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

任何有关性能的陈述都基于微基准测试是很危险的。有太多无关紧要的因素使数据倾斜。


好吧,每个测试已经进行了大约10次。整数在399到421毫秒之间。枚举在429到438毫秒之间。这种差异的原因引起了我的兴趣。
B. Decoster

25
+1用于比较装配体本身。证明两个程序具有相同的程序集将取代对基准的需求。
布莱恩

有时我想写一篇文章:Do Not Write Microbenchmarks Because You Can't:)
bestsss 2011年

1
您在此处使用了优化的编译器,该编译器已删除了所有代码。使用问题中发布的代码,但使用std :: vector,发布版本消除了第二个for循环。用替换第二个for循环的内容将if (vec[i] == 1) break;阻止优化程序删除该循环。
Skizz

@Skizz:同意,但是那不会产生完全相同的影响,现在吗?;-)
Marcelo Cantos


3

例如,如果您使用Visual Studio,则可以在其中创建一个简单项目

     a=Player::EMPTY;

如果右键单击“开始反汇编”,代码将为

mov         dword ptr [a],0

因此,编译器将替换枚举的值,并且通常不会产生任何开销。


3

好吧,我做了一些测试,并且整数和枚举形式之间没有太大区别。我还添加了一个char形式,该形式始终快大约6%(这并不奇怪,因为它使用的内存更少)。然后我只使用了char数组而不是向量,速度快了300%!由于尚未提供QVector,因此它可能是数组的包装器,而不是我使用的std :: vector。

这是我使用的代码,是使用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;
}

2

编译器应将其转换enum为整数。它们在编译时被内联,因此一旦您的程序被编译,就应该与您自己使用整数完全一样。

如果您的测试产生不同的结果,则测试本身可能有问题。要么是,要么您的编译器的行为异常。


2

这取决于实现,并且枚举和整数有可能具有不同的性能以及相同或不同的汇编代码,尽管这可能是次优编译器的标志。获得差异的一些方法是:

  • QVector可能专门针对您的枚举类型做一些令人惊讶的事情。
  • 枚举不会被编译为int,而是会编译为“不大于int的某些整数类型”。int的QVector可能不同于some_integral_type的QVector专门化。
  • 即使QVector不是专门的,编译器在对齐内存中的int方面比对齐some_integral_type也可能做得更好,导致当您遍历枚举或some_integral_type的矢量时,缓存的未命中率更高。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.