【问题标题】:Vectors and Arrays in C++C++ 中的向量和数组
【发布时间】:2014-01-21 09:13:48
【问题描述】:

C++ 向量和普通数组之间的性能差异已被广泛讨论,例如herehere。通常讨论会得出结论,当使用[] 运算符访问并且编译器启用内联函数时,向量和数组在性能方面是相似的。这就是为什么预期的原因,但我遇到了一个似乎不正确的案例。下面几行的功能非常简单:获取一个 3D 体积,然后交换并应用某种 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=1VERSION=0 执行相同的操作,但输出如下:

  • 版本 2:经过 6.94905 秒。
  • 版本 1:经过 4.08626 秒
  • 版本 0:经过 1.97576 秒。

如果我在没有 OMP 的情况下编译(我只有两个内核),我会得到类似的结果:

  • 版本 2:经过 10.9895 秒。
  • 版本 1:经过 7.14674 秒
  • 版本 0:经过 3.25336 秒。

我总是使用 GCC 4.6.3 和编译选项 -fopenmp -finline-functions -O3 进行编译(当我在没有 omp 的情况下编译时,我当然会删除 -fopenmp)我做错了什么,例如在编译时?或者我们真的应该期待向量和数组之间的差异吗?

PS:我不能使用 std::array 因为我所依赖的编译器不支持 C11 标准。使用 ICC 13.1.2 我得到了类似的行为。

【问题讨论】:

  • 不使用omp怎么办?你确定这是合法的吗?
  • 好点。在没有 omp 的情况下得到类似的结果:10.9895、7.14674 和 3.25336(分别为 2、1、0 版)。我将它包括在问题中。
  • ideone 对于版本 0 和 1 给出了相同的结果。
  • @Genís 你应该担心,如果你的程序表现出未定义的行为,那么谈论性能是没有意义的。
  • @molbdnilo 我没有手动验证任何东西,但是他在版本 2 中没有得到异常这一事实几乎证明了没有越界访问。

标签: c++ arrays vector


【解决方案1】:

我试过你的代码,用chrono计算时间。

我使用 clang(3.5 版)和 libc++ 编译。

clang++ test.cc -std=c++1y -stdlib=libc++ -lc++abi -finline-functions -O3

VERSION 0 和 VERSION 1 的结果完全相同,没有太大区别。平均都是 3.4 秒(我用的是虚拟机,所以比较慢)。

然后我尝试了g++(4.8.1版),

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

结果显示,对于 VERSION 0,它是 4.4 秒(大约),对于 VERSION 1,它是 5.2 秒(大约)。

然后,我用 libstdc++ 尝试了 clang++。

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

瞧,结果又回到了 3.4 秒。

所以,纯粹是g++的优化“bug”。

【讨论】:

  • 好吧,我会说 4.4 到 5.2 秒之间的 18%。仍然很重要,但显然不如我得到的重要。我也尝试了 icc 并得到了相同的结果,但我正在安装 clang 并检查它。如果我得到你所得到的,你认为应该向 gcc(和 icc)人报告吗?
  • 我认为是这样,因为纯 [] 运算符读取向量应该具有与数组相同的性能。我认为 gcc 应该已经优化了,也许它不能优化像 (AT(img,eindex - csize) + AT(img,eindex + csize) + AT(img,eindex - psize) + ...) 这样的东西。您还应该注意到,对于您的代码,clang 比 gcc 具有更好的性能(3.4 对 4.4/5.2),我不确定这种简单循环是如何实现的。
  • @Genís 顺便说一句,在我的测试中,我使用了 GCC 4.8.1,这可能是差距比您的测试收敛的原因。
  • 我用clang编译(没有openmp),得到以下结果:7.2、4.8和4.8。当然,版本 1 和 0 的性能相同,但我会说它们的性能与另一个一样差。我认为问题在于,虽然 ICC 和 GCC 确实优化了版本 0,但 clang 无法做到这一点,所以这无法回答我的问题。顺便说一句,我可能是错的,因为我没有掌握它,但没有使用 gcc 或任何编译器进行实际编译?
  • clang 完全是一个从头开始编写的新编译器。我使用了 clang 3.5 版(我从 1 个月前的最新快照中自己构建了它)和 gcc 4.8.1 版。不同的编译器之间可能存在差异。你clang有7.2、4.8、4.8,你的gcc有10.9、7.1、3.2,为什么gcc有这么大的差距仍然很值得怀疑:7.1和3.2。
猜你喜欢
  • 2012-05-26
  • 1970-01-01
  • 2015-05-13
  • 2011-08-27
  • 2014-02-01
  • 1970-01-01
  • 2011-01-24
  • 2017-12-08
  • 1970-01-01
相关资源
最近更新 更多