Programing

C ++ 열거 형은 정수보다 사용 속도가 느립니까?

lottogame 2020. 11. 14. 09:45
반응형

C ++ 열거 형은 정수보다 사용 속도가 느립니까?


정말 간단한 문제입니다.

Go 프로그램을 프로그래밍하고 있습니다. 이사회를 a QVector<int>또는 QVector<Player>where로 표현해야합니까?

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를 사용하면 속도가 동일합니다.


열거 형은 컴파일 타임에 완전히 해결됩니다 (열거 형 상수는 정수 리터럴로, 열거 형 변수는 정수 변수로). 사용시 속도 저하가 없습니다.

일반적으로 평균 열거 형은 다음보다 큰 기본 유형을 갖지 않습니다 int(매우 큰 상수를 넣지 않는 한). 사실, §7.2 ¶ 5에서는 다음과 같이 명시 적으로 언급됩니다.

열거 형의 기본 형식은 열거 형에 정의 된 모든 열거 자 값을 나타낼 수있는 정수 형식입니다. 열거 자의 값이 또는에 맞을 수없는 경우를 제외하고는 기본 형식이 더 크지 않아야한다는 점을 제외하고int 열거 형의 기본 형식으로 사용되는 정수 형식은 구현에 따라 정의됩니다 .intunsigned int

열거 형은 일반적으로 코드를 더 쉽게 읽고 유지 관리 할 수 ​​있기 때문에 적절할 때 사용해야합니다 ( "매직 넘버"로 가득 찬 프로그램을 디버깅 해 본 적이 있습니까? :S).

당신의 결과에 관해서 : 아마도 당신의 테스트 방법은 당신이 "정상적인"기계에서 코드를 실행할 때 얻는 정상적인 속도 변동을 고려하지 않을 것입니다 1 ; 테스트를 여러 번 (100 회 이상) 실행하고 시간의 평균과 표준 편차를 계산해 보셨습니까? 결과는 양립 할 수 있어야합니다. 평균 간의 차이 는 두 표준 편차 의 RSS 2 보다 1 배 또는 2 배 더 크지 않아야합니다 (평상시처럼 변동에 대한 가우스 분포를 가정).

또 다른 검사는 생성 된 어셈블리 코드를 비교하는 것입니다 (g ++를 사용하면 -S스위치로 얻을 수 있음 ).


  1. "정상"PC에서는 실행중인 다른 작업, 캐시 / RAM / VM 상태 등으로 인해 불확실한 변동이 있습니다.
  2. 제곱근 합계 제곱, 제곱 표준 편차 합계의 제곱근.

일반적으로 열거 형을 사용하면 성능에 전혀 영향을주지 않습니다. 이것을 어떻게 테스트 했습니까?

방금 직접 테스트를 실행했습니다. 차이점은 순수한 소음입니다.

지금 막 두 버전을 어셈블러로 컴파일했습니다. 각각의 주요 기능은 다음과 같습니다.

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

마이크로 벤치 마크를 기준으로 성능에 관한 진술을하는 것은 위험합니다. 데이터를 왜곡하는 외부 요인이 너무 많습니다.


열거 형은 느려서는 안됩니다. 그것들은 정수로 구현됩니다.


예를 들어 Visual Studio를 사용하는 경우 간단한 프로젝트를 만들 수 있습니다.

     a=Player::EMPTY;

"분해로 이동"을 마우스 오른쪽 버튼으로 클릭하면 코드가

mov         dword ptr [a],0

따라서 컴파일러는 enum의 값을 대체하며 일반적으로 오버 헤드를 생성하지 않습니다.


글쎄요, 몇 가지 테스트를했는데 정수와 열거 형 사이에 큰 차이가 없었습니다. 또한 지속적으로 약 6 % 더 빠른 char 형식을 추가했습니다 (메모리를 적게 사용하므로 놀라운 일이 아닙니다). 그런 다음 벡터 대신 문자 배열을 사용했는데 300 % 더 빨랐습니다! QVector가 무엇인지 제공되지 않았기 때문에 내가 사용한 std :: vector 대신 배열에 대한 래퍼가 될 수 있습니다.

다음은 Dev Studio 2005의 표준 릴리스 옵션을 사용하여 컴파일 된 제가 사용한 코드입니다. 질문의 코드를 최적화 할 수 없기 때문에 timed 루프를 약간 변경했습니다 (어셈블리 코드를 확인해야 함). .

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

컴파일러는 enum정수 로 변환해야합니다 . 컴파일 타임에 인라인되므로 프로그램이 컴파일되면 정수 자체를 사용한 것과 정확히 동일해야합니다.

테스트 결과가 다른 경우 테스트 자체에 문제가있을 수 있습니다. 또는 컴파일러가 이상하게 작동합니다.


이는 구현에 따라 다르며 enum과 int가 성능이 다르고 동일하거나 다른 어셈블리 코드를 가질 수 있지만 아마도 차선 컴파일러의 신호일 수 있습니다. 차이점을 얻는 몇 가지 방법은 다음과 같습니다.

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

참고URL : https://stackoverflow.com/questions/4851810/are-c-enums-slower-to-use-than-integers

반응형