说明:
1.转载请联系本人
2.代码在最后问题描述
稀疏矩阵向量乘法(Sparse Matrix-Vector Multiplication, SpMV)在许多科学计算程序中都有广泛的应用。
如下所示,数据矩阵A是稀疏的,输入向量x和输出向量y是稠密的。
y = Ax
要求
- 根据内存大小测不同规模和不同稀疏程度的稀疏矩阵的处理速度(GFLOPS/s)和带宽(GB/s),并给出计算公式。
- 请计算系统的理论峰值,如果没有达到理论峰值,尝试给出原因。
思路和方法
老师在/hw2目录下提供的代码已经能够达到比较好的存储访问了,所以在这个基础上添加OpenMP代码以及尝试其他的优化,如循环优化即可。
实验
结果及分析
不同规模和不同稀疏程度矩阵处理速度公式:
假设稀疏矩阵中不为0的个数为k
处理速度公式 = 2*k/1000000000/time
带宽计算公式 = (sizeof(int)*dim + sizeof(int)*k + sizeof(float)*k + sizeof(float)*dim*2)/1000000000/time
系统理论峰值(即浮点数理论峰值)
集群理论浮点峰值
= CPU主频(GHz)× CPU每时钟周期执行浮点运算次数 × 节点数 × 8(每节点双路四核)
=4.2*4*8=134.4GFLOPS/s
峰值带宽: B=F×D/8=2133MHz*64bit/8=17.064GHz
没有达到理论峰值的原因是,程序并不只是在做浮点数运算或只是在访问内存,以及操作系统的线程调度,和服务器本身的不稳定性等等...
1.尝试parallel shared/private以及dynamic
问题1: 发现在initMatrix里面加omp效果不佳.
解决: 可能会因为并行访问同一数值,导致访问错误和数组越界等问题有关
问题2: 发现使用shared/private之后速度较慢,GFLOPS较低
解决: 与privaet是否使用无关,主要是这样会导致并行区域开多,速度会下降
2.尝试parallel guided 和减少for循环依赖
代码如下
for(int i=1; i<=dim; i++)
tmp[i-1] = row[i]; // 循环分离,减少依赖
for(int numOfTimes=0; numOfTimes<ITERATIONS; numOfTimes++){
#pragma omp parallel for num_threads(thread_num)schedule(guided)
for(unsigned short i=0; i<dim; i++){
float t = 0;
for(int j=row[i]; j<tmp[i]; j++){
unsigned short colNum = col[j];
t += data[j] * vec[colNum];
}
result[i] = t;
}}
复制代码
得到的数据结果如下: (线程数量:10,结果是实验三次取平均)
Dim规模 | GFLOPS/s | (CSR)GB/s | Time(s) |
---|---|---|---|
500 | 0.084 | 0.44 | 0.000063 |
1000 | 0.505 | 2.32 | 0.000040 |
5000 | 2.393 | 9.86 | 0.000209 |
10000 | 2.598 | 10.55 | 0.000770 |
20000 | 2.544 | 10.25 | 0.003087 |
30000 | 2.509 | 10.09 | 0.007173 |
40000 | 2.454 | 9.85 | 0.013037 |
3.尝试采用三元组进行测试
结果:得到的速度较CSR存储方式的速度要慢,虽然代码设计较CSR简单
4.将部分变量改为unsigned short
结论
1.使用OpenMP中guided进行for循环调度能达到最好的效果。
2. CSR的存储格式较三元组的存储格式更优,访问更快。
3. 采用循环分离等优化方式能够提高GFLOPS的大小。
4. 变量如果提前定义,有可能会得出错误的结果。另外private的调用也会降低GFLOPS的大小
参考
[1]《稀疏矩阵存储格式总结+存储效率对比:COO,CSR,DIA,ELL,HYB》
[2]《稀疏矩阵向量乘法》
代码地址
个人GitHub:Icarusintheworld