struct xnode
{
float *mat;
};
void testScaling( )
{
int N = 1000000; ///total num matrices
int dim = 10;
//memory for matrices
std::vector<xnode> nodeArray(N);
for( int k = 0; k < N; ++k )
nodeArray[k].mat = new float [dim*dim];
//memory for Y
std::vector<float*> Y(N,0);
for( int k = 0; k < N; ++k )
Y[k] = new float [dim];
//shared X
float* X = new float [dim];
for(int i = 0; i < dim; ++i ) X[i] = 1.0;
//init mats
for( int k = 0; k < N; ++k )
{
for( int i=0; i<dim*dim; ++i )
nodeArray[k].mat[i] = 0.25+((float)i)/3;
}
int NTIMES = 500;
//gemv args
char trans = 'N';
int lda = dim;
int incx = 1;
float alpha =1 , beta = 0;
//threads
int thr[4];
thr[0] =1 ; thr[1] = 2; thr[2] = 4; thr[3] = 8;
for( int t = 0; t<4; ++t )//test for nthreads
{
int nthreads = thr[t];
double t_1 = omp_get_wtime();
for( int ii = 0; ii < NTIMES; ++ii )//do matvec NTIMES
{
#pragma omp parallel for num_threads(nthreads)
for( int k=0; k<N; ++k )
{
//compute Y[k] = mat[k] * X;
GEMV(&trans, &dim, &dim, &alpha, nodeArray[k].mat, &lda, X, &incx, &beta, Y[k], &incx);
//GEMV(&trans, &dim, &dim, &alpha, nodeArray[0].mat, &lda, X, &incx, &beta, Y[k], &incx);
}
}
double t_2 = omp_get_wtime();
std::cout << "Threads " << nthreads << " time " << (t_2-t_1)/NTIMES << std::endl;
}
//clear memory
for( int k = 0; k < N; ++k )
{
delete [] nodeArray[k].mat;
delete [] Y[k];
}
delete [] X;
}
上述代码并行化了大小为dim的N个矩阵的矩阵向量积,并将结果存储在N个输出向量中。将500个产品的平均值作为每个矩阵向量乘积的时间。上例中的矩阵矢量产品都具有相同的大小,因此线程应该完美平衡 - 我们应该实现接近理想8x的性能缩放。以下是观察结果(机器 - Intel Xeon 3.1Ghz.2处理器,每个8核,启用超线程,Windows,VS2012,英特尔MKL,英特尔OMP库)。
dim = 10 N = 1000000
主题1 - 时间0.138068s
主题2 - 时间0.0729147s
线程4 - 时间0.0360527s
主题8 - 时间0.0224268s(8threads上6.1x)
dim = 20 N = 1000000
主题1次0.326617
主题2次0.185706
主题4次0.0886508
主题8次0.0733666(8个主题为4.5x)
注意 - 我在这个案例上运行了VTune。它显示CPUTime 267.8秒,开销时间43秒,旋转时间 - 8秒。开销时间全部花在libiomp函数(intel库)上。 8Threads / 1Thread缩放对于这种情况很差。
接下来 - 在gemv for循环中,我们将nodeArray [k] .mat更改为nodeArray [0] .mat(请参阅注释语句),以便只有第一个矩阵用于所有矩阵矢量产品。
dim = 20 N = 1000000
线程1次0.152298(串行时间减半)
主题2次0.0769173
主题4次0.0384086
线程8次0.019336(8个线程7.87x)
因此我得到了几乎理想的缩放 - 为什么这种行为? VTune说,CPU时间的很大一部分花在同步和线程开销上。这里似乎负载平衡和线程同步之间没有关系。随着矩阵大小的增加,粒度应该增加,并且线程开销应该成比例地小。但随着我们从10号增加到20号,缩放正在减弱。当我们使用nodeArray [0] .mat(仅第一个矩阵)来执行所有矩阵向量产品时,缓存只更新一次(因为编译器在优化期间知道这一点)并且我们接近理想的缩放。因此,同步开销似乎与某些缓存相关的问题有关。我已经尝试了许多其他的东西,比如设置KMP_AFFINITY和不同的负载分配,但这并没有给我任何东西。
我的问题是:
1.我不清楚缓存性能如何影响openMP线程同步。有人可以解释一下吗?
2.可以做些什么来改善扩展并减少开销?
谢谢