高斯消去法SSE并行化研究报告 一、 问题描述 题目:高斯消去法(LU分解)串行算法如下面伪代码所示,计算模式如图1所示(第k步时第k行的除法运算,和后续行减去第k行的运算)。设计实现SSE算法,加速计算过程。提交研究报告(问题描述、SSE算法设计与实现、实验及结果分析)和源码(只将程序文件和工程文件提交,不要将编译出的目标文件和可执行文件也打包提交)。 1.procedure LU (A) 2. begin 3. for k := 1 to n do 4. for j := k+1 to n do 5. A[k, j] := A[k, j]/A[k, k]; 6 A[k, k] := 1.0; 7. endfor; 8. for i := k + 1 to n do 9. for j := k + 1 to n do 10. A[i, j] := A[i, j] - A[i,k] × A[k, j ]; 11. endfor; 12. A[i, k] := 0; 13. endfor; 14.endfor; 15. end LU 综合以上分析一下,作业的目的就是找到上面的算法可以并行执行的部分,将这部分用SSE实现,并且测试所用时间,再改变问题规模,观察随着问题规模的变化,所用时间与串行时间比例的变化。最后分析一下判断并行算法的性能是否足够好。 二、 SSE算法设计和实现 算法设计: 分析此算法,首先外层循环无法并行化,因为在不同的循环中,相同位置的元素都会改变。内部的将对角线元素变为1和下面元素变为0都是可以并发执行的。由此获得解题思路。但是在实际操作的工程中发现并行运行时间有时候要比串行运行时间多,所以又进行了部分并行(只对置零操作进行并行),发现有时候要比全部并行的效果好。 实现: 1) 基本串行算法: voidbasic_change() {//基本的串行算法 for (int k =0; k < MAX; k++) { for (int j =k; j < MAX; j++) A[k][j] /= A[k][k]; A[k][k] = 1; for (int i =k + 1; i < MAX; i++) { for (int j =k + 1; j < MAX; j++) A[ i][j] = A[ i][j] - A[ i][k] *A[k][j]; A[ i][k] = 0; } } } 2) 并行算法: voidpara_change_impr() {//并行算法 __m128 t1,t2, t3; for (int k =0; k < MAX; k++) { //此处需要并行处理 int off= (MAX - k - 1) % 4; for (int j =k + 1; j < k + 1 + off; j++) A[k][j] /= A[k][k]; t2=_mm_loadu_ps(A[k] + k); for (int j =k + 1 + off; j < MAX; j += 4) { t1 =_mm_loadu_ps(A[k] + j); t1 =_mm_div_ps(t1,t2); _mm_store_ss(A[k] + j, t1); } A[k][k] = 1; for (int i =k + 1; i < MAX; i++) { //此处不再以1位单位而是以4为单位进行循环 for (int j =k + 1; j < k + 1 + off; j++) A[ i][j] = A[ i][j] - A[ i][k] *A[k][j]; for (int j =k + 1 + off; j < MAX; j += 4) { t2 =_mm_loadu_ps(A[ i] + k); t3 =_mm_loadu_ps(A[k] + j); t1 =_mm_loadu_ps(A[ i] + j); t2 =_mm_mul_ps(t2, t3); t1 =_mm_sub_ps(t1, t2); _mm_store_ss(A[ i] + j, t1); } A[ i][k] = 0; } } } 3) 部分并行: voidpara_change() {//并行算法 __m128 t1,t2, t3; for (int k =0; k < MAX; k++) { for (int j =k + 1; j < MAX; j++) A[k][j] /= A[k][k]; A[k][k] = 1; //此处需要并行处理 for (int i =k + 1; i < MAX; i++) { //此处不再以1位单位而是以4为单位进行循环 int off= (MAX - k - 1) % 4; for (int j =k + 1; j < k + 1 + off; j++) A[ i][j] = A[ i][j] - A[ i][k] *A[k][j]; for (int j =k + 1 + off; j < MAX; j += 4) { t2 =_mm_loadu_ps(A[ i] + k); t3 =_mm_loadu_ps(A[k] + j); t1 =_mm_loadu_ps(A[ i] + j); t2 =_mm_mul_ps(t2, t3); t1 =_mm_sub_ps(t1, t2); _mm_store_ss(A[ i] + j, t1); } A[ i][k] = 0; } } } 三、 实验及结果分析 注:实验结果为每次运行5次程序取平均值 根据实验数据绘制以下图表: 从图表中可以明显看出来,在运算的数据较少的时候,并行算法和串行算法的速率几乎没有区别,串行算法甚至比并行算法快。但是随着数据量的增长,可以明显看到并行速度要比串行的速度快很多。注意其中的数据量为512和1024的位置,要比之前快的还要明显。尤其是数据量为1000和1024的对比。说明当数据量为4的整数倍的时候,SSE并行算法发挥的用更大一些。 这张图中看的更明显一些。在数据量为512的时候,速率比要比在1000的时候高。当数据量为1024的时候,只是比1000多了24个数据,但是速率明显的提升了。 从图中可以看出,在数据量为1000左右的时候,部分并行是要快于全部并行的。 总结: 此并行算法对串行算法进行了性能上的优化。在数据量越来越多的时候,优化的效果也越来越好。并且当数据量为4的整数倍的时候,优化效果尤其明显。 附加: 在做完实验之后,我在想并行算法中,如果先处理大部分数据,之后处理小于4个的数据,效率会不会有变化,所以添加了以下实验: 观察可以看到,除了在数据过小的时候结果有一些偏差以外,其他情况两种并行算法的方式都是差不多的。但同时可以看出来先处理大部分数据再处理小部分数据要快一些。 在全部完成后,使用codeblock重复以上实验步骤,使用了-O2加速(因为-O3本身采取的就是SSE方式,所以采用了-O2加速)。 得到如下实验结果: 得到以下图表: 从表格和图表中可以看出,在数据较小的时候,由于并行本身的花销,串行算法要优于并行算法。随着数据量的增加,串行算法的运行时间线性增加,但并行算法的固定开销却是稳定在一定范围内的,甚至在数据量达到一定规模的时候,这个开销可以忽略。所以时间比越来越大,最后甚至大于三倍。再看部分并行和全部并行。在数据量处于中间范围的时候,可以看出部分并行要比全部并行的加速比大,推测还是并行开销的问题。当数据量逐渐增加的时候,在图中可以看出在数据量大于1000的时候,全部并行的加速比要大于部分并行的加速比。以后可以根据数据规模选择并行程度。 |