web-dev-qa-db-ja.com

C ++列挙型は整数よりも使用が遅いですか?

それは本当に単純な問題です:

Goプログラムをプログラミングしています。ボードをQVector<int>またはQVector<Player>で表す必要があります。

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

もちろん、整数の代わりにPlayerを使用すると遅くなると思います。しかし、enumを使用する方がコーディングが優れていると思うので、どれだけ多くなるのだろうか。

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を使用すると、速度は同じになります

66
Fezvez

列挙型はコンパイル時に完全に解決され(整数リテラルとしての列挙型定数、整数変数としての列挙型変数)、それらを使用する際の速度の低下はありません。

一般に、平均列挙には、intよりも大きい基になる型はありません(非常に大きな定数を入力しない限り)。実際、§7.2¶5では次のように明示されています。

列挙型の基になる型は、列挙型で定義されたすべての列挙型の値を表すことができる整数型です。列挙型の基になる型として使用される整数型は実装によって定義されますただし、基になる型はint列挙型の値がintまたはunsigned intに収まらない場合を除きます。

列挙型は通常、コードの読み取りと保守を容易にするため、適切な場合は列挙型を使用する必要があります(「マジックナンバー」でいっぱいのプログラムをデバッグしようとしたことがありますか?:S)。

結果について:おそらく、テスト方法では、「通常の」マシンでコードを実行したときに得られる通常の速度変動が考慮されていません。1;テストを何度も(100回以上)実行し、時間の平均と標準偏差を計算してみましたか?結果には互換性があるはずです。平均間の差はRSSの1倍または2倍を超えてはなりません。2 2つの標準偏差の(通常どおり、変動のガウス分布を想定)。

実行できるもう1つのチェックは、生成されたアセンブリコードを比較することです(g ++では、-Sスイッチで取得できます)。


  1. 「通常の」PCでは、他のタスクの実行、キャッシュ/ RAM/VMの状態などにより、不確定な変動が発生します。
  2. Root Sum Squared、標準偏差の2乗の合計の平方根。
80
Matteo Italia

一般に、列挙型を使用しても、パフォーマンスにまったく違いはありません。これをどのようにテストしましたか?

自分でテストを実行しました。違いは純粋なノイズです。

ちょうど今、私は両方のバージョンをアセンブラーにコンパイルしました。それぞれの主な機能は次のとおりです。

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

プレーヤー

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

マイクロベンチマークに基づいてパフォーマンスに関する記述を行うことは危険です。データを歪める外部要因が多すぎます。

44
Marcelo Cantos

列挙型は遅くならないはずです。それらは整数として実装されます。

17

たとえば、Visual Studioを使用している場合は、次のような簡単なプロジェクトを作成できます。

     a=Player::EMPTY;

「逆アセンブリに移動」を右クリックすると、コードは次のようになります。

mov         dword ptr [a],0

したがって、コンパイラは列挙型の値を置き換え、通常はオーバーヘッドを生成しません。

3
DesignFirst

さて、私はいくつかのテストを行いましたが、整数型と列挙型の間に大きな違いはありませんでした。また、一貫して約6%高速なcharフォームを追加しました(メモリの使用量が少ないので驚くことではありません)。次に、ベクトルではなく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;
}
3
Skizz

コンパイラはenumを整数に変換する必要があります。これらはコンパイル時にインライン化されるため、プログラムがコンパイルされると、整数自体を使用した場合とまったく同じになるはずです。

テストで異なる結果が得られる場合は、テスト自体で何かが起こっている可能性があります。それか、コンパイラがおかしな動作をしています。

2
Justin Morgan

これは実装に依存し、列挙型と整数型のパフォーマンスが異なり、アセンブリコードが同じまたは異なる可能性がありますが、コンパイラが最適ではない可能性があります。違いを得るいくつかの方法は次のとおりです。

  • QVectorは、驚くべきことをするために列挙型に特化している場合があります。
  • enumはintにコンパイルされませんが、「int以下の整数型」にコンパイルされます。 intのQVectorは、some_integral_typeのQVectorとは異なる方法で特殊化される場合があります。
  • qVectorが特殊化されていない場合でも、コンパイラはsome_integral_typeを整列するよりも、メモリ内のintを整列する方が優れている可能性があり、列挙型またはsome_integral_typeのベクトルをループするときにキャッシュミス率が高くなります。
2
sailordave