Подтвердить что ты не робот

Векторы и массивы в С++

Различия в производительности между векторами С++ и явными массивами широко обсуждались, например здесь и здесь. Обычно обсуждения заключают, что векторы и массивы схожи по производительности при доступе с помощью оператора [], а компилятор включен в встроенные функции. Вот почему ожидалось, но я прошел через случай, когда кажется, что это не так. Функциональность строк ниже довольно проста: 3D-объем берется, и он заменяет и применяет какую-то трехмерную маленькую маску определенное количество раз. В зависимости от макроса VERSION тома будут объявлены как векторы и доступны через оператор at (VERSION=2), объявлены как векторы и доступны через [] (VERSION=1) или объявлены как простые массивы.

#include <vector>
#define NX 100
#define NY 100
#define NZ 100
#define H  1
#define C0 1.5f
#define C1 0.25f
#define T 3000

#if !defined(VERSION) || VERSION > 2 || VERSION < 0 
  #error "Bad version"
#endif 

#if VERSION == 2
  #define AT(_a_,_b_) (_a_.at(_b_))
  typedef std::vector<float> Field;
#endif 

#if VERSION == 1
  #define AT(_a_,_b_) (_a_[_b_])
  typedef std::vector<float> Field;
#endif 

#if VERSION == 0
  #define AT(_a_,_b_) (_a_[_b_])
  typedef float* Field;
#endif 

#include <iostream>
#include <omp.h>

int main(void) {

#if VERSION != 0 
  Field img(NX*NY*NY);
#else
  Field img = new float[NX*NY*NY];
#endif 


  double end, begin;
  begin = omp_get_wtime();  

  const int csize = NZ;
  const int psize = NZ * NX;
  for(int t  = 0; t < T; t++ ) {

    /* Swap the 3D volume and apply the "blurring" coefficients */
    #pragma omp parallel for
    for(int j = H; j < NY-H; j++ ) { 
      for( int i = H; i < NX-H; i++ ) {
        for( int k = H; k < NZ-H; k++ ) {
          int eindex = k+i*NZ+j*NX*NZ;
          AT(img,eindex) = C0 * AT(img,eindex) +
              C1 * (AT(img,eindex - csize) +
                    AT(img,eindex + csize) + 
                    AT(img,eindex - psize) + 
                    AT(img,eindex + psize) );
        }
      }
    }
  }

  end = omp_get_wtime();
  std::cout << "Elapsed "<< (end-begin) <<" s." << std::endl;

 /* Access img field so we force it to be deleted after accouting time */
 #define WHATEVER 12.f
 if( img[ NZ ] == WHATEVER ) { 
   std::cout << "Whatever" << std::endl;
 }


#if VERSION == 0
  delete[] img;
#endif 

}

Можно было бы ожидать, что код будет работать так же с VERSION=1 и VERSION=0, но вывод выглядит следующим образом:

  • ВЕРСИЯ 2: Истекший 6.94905 с.
  • ВЕРСИЯ 1: Истек 4.08626 с
  • ВЕРСИЯ 0: Истекший 1,97576 с.

Если я компилирую без OMP (у меня есть только два ядра), я получаю похожие результаты:

  • ВЕРСИЯ 2: Истекший 10.9895 с.
  • ВЕРСИЯ 1: Истекший 7.14674 s
  • ВЕРСИЯ 0: Истекший 3.25336 с.

Я всегда компилирую с GCC 4.6.3 и варианты компиляции -fopenmp -finline-functions -O3 (я, конечно, удаляю -fopenmp при компиляции без omp). Есть ли что-то, что я делаю неправильно, например, при компиляции? Или мы действительно должны ожидать разницу между векторами и массивами?

PS: Я не могу использовать std:: array из-за компилятора, от которого я зависим, что не поддерживает стандарт C11. С ICC 13.1.2 я получаю подобное поведение.

4b9b3361

Ответ 1

Я пробовал ваш код, использовал хронограф для подсчета времени.

И я скомпилировал с clang (версия 3.5) и libС++.

clang++ test.cc -std = С++ 1y -stdlib = libС++ -lС++ abi -finline-functions -O3

Результат точно такой же для ВЕРСИИ 0 и ВЕРСИИ 1, нет большой разницы. В среднем они равны 3.4 секундам (я использую виртуальную машину, поэтому она медленнее.).

Затем я попробовал g++ (версия 4.8.1),

g++ test.cc -std = С++ 1y -finline-functions -O3

Результат показывает, что для версии VERSION 0 она равна 4.4 с (примерно), для версии 1, она составляет 5,2 секунды (примерно).

Затем я попробовал clang++ с libstdС++.

clang++ test.cc -std = С++ 11 -finline-functions -O3

voila, результат снова возвращается к 3.4 секундам.

Итак, это чисто оптимизационная "ошибка" g++.