出于数据模拟的目的,我正在寻找一种有效的方法来对稀疏矩阵进行加权求和。 基本上我有一个Nx x Ny x Nz双值的数据立方体,其中Nx和Ny大约为4000,Nz是几百万。所有Nx×Ny子矩阵都非常稀疏(大约40个数据块)。 我现在想通过将所有矩阵相加并对它们进行加权来减少Z方向上的数据立方体。该过程如图所示。对于我的模拟,所有矩阵都保持不变,只有权重会发生变化并生成不同的Nx x Ny数据集。
这就是我所尝试的:C ++中稀疏矩阵的简单实现,以及简单的总结。
#ifndef SPARSEARRAY3D_H
#define SPARSEARRAY3D_H
#include <vector>
struct data{
unsigned short int x;
unsigned short int y;
int z;
double value;
};
class sparsearray3d
{
public:
sparsearray3d();
void createRandomData(int Nx, int Ny, int Nz, int bwidthX, int bwidthY);
void sumData();
int Nx,Ny,Nz;
std::vector<data> dd;
std::vector<std::vector<double> > image;
std::vector<double> weights;
};
#endif // SPARSEARRAY3D_H
sparsearray3d.cpp
#include "sparsearray3d.h"
#include <stdlib.h> /* srand, rand */
#include <stdio.h> /* printf, scanf, puts, NULL */
sparsearray3d::sparsearray3d()
{
this->Nx = 0;
this->Ny = 0;
this->Nz = 0;
}
void sparsearray3d::createRandomData(int Nx, int Ny, int Nz, int bwidthX = 5, int bwidthY = 5)
{
// create random data
this->weights.resize(Nz);
this->image.resize( Nx , std::vector<double>( Ny , 0. ) );
this->Nx = Nx;
this->Ny = Ny;
this->Nz = Nz;
for(int i=0; i<Nz; ++i)
{
int x0 = rand() % (Nx-bwidthX);
int y0 = rand() % (Ny-bwidthY);
this->weights.push_back((double) rand() / (RAND_MAX));
for(int j=0; j<bwidthX; ++j)
{
for(int k=0; k<bwidthY; ++k)
{
this->dd.push_back({x0+j,y0+k,i,((double) rand() / (RAND_MAX))});
}
}
}
printf("Vector size: %4.2f GB \n", this->dd.size()*sizeof(data) * 1E-9);
}
void sparsearray3d::sumData()
{
std::vector<data>::iterator it;
#pragma omp parallel for
for(it = this->dd.begin(); it < this->dd.end(); ++it)
{
this->image[it->y][it->x] += it->value * this->weights[it->z];
}
}
的main.cpp
#include <iostream>
#include "sparsearray3d.h"
#include <sys/time.h>
using namespace std;
int main()
{
struct timeval start, end;
sparsearray3d sa;
gettimeofday(&start, NULL);
sa.createRandomData(4096, 4096, 2000000, 4, 16);
gettimeofday(&end, NULL);
double delta = ((end.tv_sec - start.tv_sec) * 1000000u +
end.tv_usec - start.tv_usec) / 1.e6;
cout << "random array generation: " << delta << endl;
gettimeofday(&start, NULL);
sa.sumData();
gettimeofday(&end, NULL);
delta = ((end.tv_sec - start.tv_sec) * 1000000u +
end.tv_usec - start.tv_usec) / 1.e6;
cout << "array addition: " << delta << endl;
return 0;
}
这已经很好了,上面的例子在这里运行~0.6s。 我想知道的第一件事是,为什么#pragma omp parallel for的速度只提高了2倍,尽管使用了4个CPU。
这个问题似乎非常适合大规模并行化。 Cuda / OpenCL能在这里提供帮助吗?但是,我在某处读到,Cuda / OpenCL的矩阵添加效率不高。 (虽然我没有NVIDIA卡)。 或者,我读一点关于图及其与矩阵的关系。用一些图算法可以解决这个问题吗?
编辑: 我试图给艾根一枪;但是,我没能创建大量的矩阵。下面的代码需要比我的代码更多的内存(并且N~20000000失败,因为我的内存不足)。不确定我做得对,但这就是我从特征文档中理解它的方式。
#include <vector>
#include <eigen3/Eigen/Sparse>
int main()
{
int N=100000;
std::vector<Eigen::SparseMatrix<double> > data;
data.resize(N);
for (int i=0; i<N; ++i)
{
data[i].resize(4096,4096);
data[i].reserve(4*16);
}
return 0;
}
另外,以下列方式总结稀疏矩阵比我的代码要慢得多:
Eigen::SparseMatrix<double> sum(4096,4096) ;
sum.reserve(4096*4096);
for(int i=0; i<N; ++i)
sum+=data[i];
答案 0 :(得分:1)
你所处理的是相对一般的线性代数矩阵 - 向量乘法的情况(一些相关的计算机科学缩写来搜索&#34; DGEMM&#34;或&#34; SpMV&# 34)。因此,您的第一个选择是尝试高度优化的并行线性代数库,如英特尔 MKL ,另请参阅: Parallel linear algebra for multicore system
其次,如果您想自己优化和并行化您的算法,那么您可能首先需要学习一些相关的文章(例如: - http://cscads.rice.edu/publications/pdfs/Williams-OptMultiCore-SC07.pdf - http://pcl.intel-research.net/publications/ics26-liuPS.pdf )
第三,如果你不想要或没有时间浏览书籍,文章或图书馆,但想要自己从头开始试验所有事情,就要考虑很少的事情:
最后(也许它是第一项),我不认为你真的拥有&#34;稀疏矩阵&#34; - 这里的数据结构,因为你真的没有尝试过避免&#34;零&#34; (他们仍然坐在你的矩阵向量中)。我不知道你是否已经采用了它的数据结构(所以你的例子只是简化了实际代码......)。
平行线性代数是计算机科学中的一个重要课题;每隔一年,一些专家就会产生新的情报。并且每个下一代CPU和有时候的协处理器都经过了更好的优化,以加速并行化线性代数内核。所以有很多东西要探索。