c++ - 为什么 C++ 与动态数组的乘法比 std::vector 版本更好

标签 c++ arrays c++11 matrix openmp

我正在为具有不同数据结构和技术( vector 、数组和 OpenMP)的矩阵实现 C++ 乘法,我发现了一个奇怪的情况……我的动态数组版本运行得更好:

次数:

openmp mult_1: time: 5.882000 s

array mult_2: time: 1.478000 s

我的编译标志是:

/usr/bin/g++ -fopenmp -pthread -std=c++1y -O3

C++ vector 版

typedef std::vector<std::vector<float>> matrix_f;
void mult_1 (const matrix_f &  matrixOne, const matrix_f & matrixTwo, matrix_f & result) {
    const int matrixSize = (int)result.size();
    #pragma omp parallel for simd
    for (int rowResult = 0; rowResult < matrixSize; ++rowResult) {
        for (int colResult = 0; colResult < matrixSize; ++colResult) {
            for (int k = 0; k < matrixSize; ++k) {
                result[rowResult][colResult] += matrixOne[rowResult][k] * matrixTwo[k][colResult];  
            }
        }
    }
}

动态数组版本

void mult_2 ( float *  matrixOne, float * matrixTwo,  float * result, int size)  {
    for (int row = 0; row < size; ++row) {
        for (int col = 0; col < size; ++col) {
            for (int k = 0; k < size; ++k) {
                (*(result+(size*row)+col)) += (*(matrixOne+(size*row)+k)) * (*(matrixTwo+(size*k)+col));
            }
        }
    }
}

测试:

C++ vector 版

utils::ChronoTimer timer;
/* set Up simple matrix */
utils::matrix::matrix_f matr1 = std::vector<std::vector<float>>(size,std::vector<float>(size));
fillRandomMatrix(matr1);

utils::matrix::matrix_f matr2 = std::vector<std::vector<float>>(size,std::vector<float>(size));
fillRandomMatrix(matr2);

utils::matrix::matrix_f result = std::vector<std::vector<float>>(size,std::vector<float>(size));    
timer.init();
utils::matrix::mult_1(matr1,matr2,result);
std::printf("openmp mult_1: time: %f ms\n",timer.now() / 1000);

动态数组版本

utils::ChronoTimer timer;

float *p_matr1 = new float[size*size];
float *p_matr2 = new float[size*size];
float *p_result = new float[size*size];

fillRandomMatrixArray(p_matr1,size);
fillRandomMatrixArray(p_matr2,size);

timer.init();
utils::matrix::mult_2(p_matr1,p_matr2,p_result,size);
std::printf("array mult_2: time: %f ms\n",timer.now() / 1000);

delete [] p_matr1;
delete [] p_matr2;
delete [] p_result;

我正在查看以前的一些帖子,但找不到与我的问题相关的任何内容 link , link2 , link3 :

更新: 我用答案重构了测试,vector 的效果稍微好一些:

vector mult: time: 1.194000 s

array mult_2: time: 1.202000 s

C++ vector 版

void mult (const std::vector<float> &  matrixOne, const std::vector<float> & matrixTwo, std::vector<float> & result, int size) {
    for (int row = 0; row < size; ++row) {
        for (int col = 0; col < size; ++col) {
            for (int k = 0; k <size; ++k) {
                result[(size*row)+col] += matrixOne[(size*row)+k] * matrixTwo[(size*k)+col];
            }
        }
    }
}

动态数组版本

void mult_2 ( float *  matrixOne, float * matrixTwo,  float * result, int size)  {
    for (int row = 0; row < size; ++row) {
        for (int col = 0; col < size; ++col) {
            for (int k = 0; k < size; ++k) {
                (*(result+(size*row)+col)) += (*(matrixOne+(size*row)+k)) * (*(matrixTwo+(size*k)+col));
            }
        }
    }
}

此外,我的矢量化版本运行得更好(0.803 秒);

最佳答案

vector 的 vector 类似于锯齿状数组——每个条目都是一个指针的数组,每个指针指向另一个 float 组。

相比之下,原始数组版本是一个内存块,您可以在其中进行数学运算以找到元素。

使用单个 vector ,而不是 vector 的 vector ,并手动计算。或者,使用固定大小的 std::array vector 。或者编写一个辅助类型,它采用 float 的(一维) vector ,并为您提供它的二维 View 。

与一堆断开连接的缓冲区中的数据相比,连续缓冲区中的数据对缓存和优化更友好。

template<std::size_t Dim, class T>
struct multi_dim_array_view_helper {
  std::size_t const* dims;
  T* t;
  std::size_t stride() const {
    return
      multi_dim_array_view_helper<Dim-1, T>{dims+1, nullptr}.stride()
      * *dims;
  }
  multi_dim_array_view_helper<Dim-1, T> operator[](std::size_t i)const{
    return {dims+1, t+i*stride()};
  }
};
template<class T>
struct multi_dim_array_view_helper<1, T> {
  std::size_t stride() const{ return 1; }
  T* t;
  T& operator[](std::size_t i)const{
    return t[i];
  }
  multi_dim_array_view_helper( std::size_t const*, T* p ):t(p) {}
};
template<std::size_t Dims>
using dims_t = std::array<std::size_t, Dims-1>;
template<std::size_t Dims, class T>
struct multi_dim_array_view_storage
{
  dims_t<Dims> storage;
};
template<std::size_t Dims, class T>
struct multi_dim_array_view:
  multi_dim_array_view_storage<Dims, T>,
  multi_dim_array_view_helper<Dims, T>
{
  multi_dim_array_view( dims_t<Dims> d, T* t ):
    multi_dim_array_view_storage<Dims, T>{std::move(d)},
    multi_dim_array_view_helper<Dims, T>{
      this->storage.data(), t
    }
  {}
};

现在你可以这样做了:

std::vector<float> blah = {
   01.f, 02.f, 03.f,
   11.f, 12.f, 13.f,
   21.f, 22.f, 23.f,
};

multi_dim_array_view<2, float> view = { {3}, blah.data() };
for (std::size_t i = 0; i < 3; ++i )
{
  std::cout << "[";
  for (std::size_t j = 0; j < 3; ++j )
    std::cout << view[i][j] << ",";
  std::cout << "]\n";
}

live example

View 类中没有数据被复制。它只是提供平面数组的 View ,它是一个多维数组。

关于c++ - 为什么 C++ 与动态数组的乘法比 std::vector 版本更好,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35626375/

相关文章:

javascript - 比较两个对象数组

Java:如何在不使用 Sort() 的情况下基于自定义对象 ArrayList 创建排序字符串 ArrayList

c - 删除 char 数组中的空格

c++ - 线程连接问题

c++ - 如何修复 vs2013 上的 C3848 错误?

c++ - Box2D (C++) 重力井

C++ 使用自定义比较函数初始化 priority_queue

c++ - C/C++ 中的最小 double 值

c++ - 使用 wxWidgets 发布到 URL

c++ - 匿名聚合规则