如果这篇博客帮助到你,可以请我喝一杯咖啡~
CC BY 4.0 (除特别声明或转载文章外)
实现并行梯形数值积分的 MPI 算法
下为单节点求解的函数,其中e
是逼近的步长。由于单账号的运行时间权限是一个小时,这里我经过调参,取e=1e-2
是可以在时限内计算完毕的(约四十分钟)。
这里有几处优化细节,通过调整求值关系,使得每个点处的函数值不被重复计算;同时把梯形的高移动到循环外面,减少计算次数。
当然,由于这里的步长其实只有百分之一,因此最后求出来的积分其实精度不怎么高,和自己手算的结果相比只能保证五六位的有效位数。
用 MPI 的点对点通信函数完成梯形数值积分的并行算法
各进程将各自计算的结果myAns
发送到 0 号进程并求和ans
。
用 MPI 的集合通信函数完成梯形数值积分的并行算法
使用Reduce
操作将各线程的结果myAns
归约到 0 号进程的ans
。
将上面的算法对瑕积分扩展
假设被积函数 f(x)可能是无界函数,积分区间也很大,如
- f(x)=exp(bx)/sqrt(1+exp(cx)), 0<x<=L;
- 这时积分区间[0,L]划分不能是等长的,每个任务的小区间需要传递,并且参数 b,c,L 也需要传递。
这里选择使用自适应辛普森方法求解瑕积分,根据这篇论文,论证了加一个十五分之一的偏移收敛会比较快。
不过,在对老师给的这个函数(取b = c = 1
)进行瑕积分求解的时候,发现积分上限达到的时候,结果已经超过了double
类型的表示范围,因此这段代码仅用来验证瑕积分求解的正确性,下面进行多核测速的代码仍然使用的是梯形积分法。
填表
运行时间 T(单位 s)
详见下面的输出文件部分。
进程数\问题规模 | |||||||
---|---|---|---|---|---|---|---|
1 | 0.424 | 0.513 | 1.928 | 23.536 | 357.396 | 706.644 | 1441.164 |
2 | 0.426 | 0.553 | 1.403 | 11.794 | 182.182 | 354.155 | 706.369 |
4 | 0.466 | 0.542 | 1.019 | 6.230 | 92.010 | 179.062 | 353.818 |
8 | 0.543 | 0.487 | 0.716 | 3.667 | 46.014 | 92.823 | 177.530 |
16 | 0.541 | 0.576 | 0.719 | 2.177 | 23.253 | 46.102 | 93.670 |
32 | 0.898 | 0.747 | 0.826 | 1.663 | 12.633 | 23.169 | 45.404 |
64 | 1.464 | 1.067 | 1.116 | 1.638 | 6.812 | 12.324 | 23.790 |
128 | 1.110 | 1.076 | 1.176 | 1.416 | 4.105 | 6.952 | 12.525 |
256 | 1.534 | 1.188 | 1.196 | 1.319 | 2.848 | 4.110 | 6.936 |
512 | 1.802 | 1.704 | 1.695 | 1.754 | 2.210 | 2.626 | 3.478 |
可以看到:
- 随着问题规模增加,同进程数下运行时间不断增加
- 问题规模比较小的时候,进程越多并行时间开销越大
- 问题规模比较大的时候,运行时间减少
加速比 S
加速比 S=同等规模下的串行时间/并行时间。
进程数\问题规模 | |||||||
---|---|---|---|---|---|---|---|
1 | 1.00 | 1.00 | 1.00 | 1.00 | 1.00 | 1.00 | 1.00 |
2 | 1.00 | 0.93 | 1.37 | 2.00 | 1.96 | 2.00 | 2.04 |
4 | 0.91 | 0.95 | 1.89 | 3.78 | 3.88 | 3.94 | 4.07 |
8 | 0.78 | 1.05 | 2.69 | 6.41 | 7.77 | 7.61 | 8.12 |
16 | 0.78 | 0.89 | 2.68 | 10.81 | 15.37 | 15.33 | 15.39 |
32 | 0.47 | 0.69 | 2.33 | 14.15 | 28.29 | 30.50 | 31.74 |
64 | 0.29 | 0.48 | 1.72 | 14.38 | 52.47 | 57.34 | 60.58 |
128 | 0.38 | 0.48 | 1.63 | 16.62 | 87.06 | 101.65 | 115.06 |
256 | 0.28 | 0.43 | 1.61 | 17.84 | 125.49 | 171.93 | 207.78 |
512 | 0.24 | 0.30 | 1.14 | 13.42 | 161.71 | 269.10 | 414.37 |
可以看到:
- 随着问题规模增加,同进程数下加速比不断增加
- 随着进程数增加,同问题规模下加速比不断减少
效率 E
运行效率 E=加速比 S/并行线程数。
进程数\问题规模 | |||||||
---|---|---|---|---|---|---|---|
1 | 1.00 | 1.00 | 1.00 | 1.00 | 1.00 | 1.00 | 1.00 |
2 | 0.50 | 0.93 | 0.68 | 1.00 | 0.98 | 1.00 | 1.02 |
4 | 0.23 | 0.24 | 0.47 | 0.95 | 0.97 | 0.99 | 1.02 |
8 | 0.10 | 0.13 | 0.34 | 0.81 | 0.97 | 0.95 | 1.02 |
16 | 0.05 | 0.06 | 0.17 | 0.68 | 0.96 | 0.96 | 0.96 |
32 | 0.01 | 0.02 | 0.07 | 0.44 | 0.88 | 0.95 | 0.99 |
64 | 0.005 | 0.008 | 0.03 | 0.22 | 0.82 | 0.90 | 0.95 |
128 | 0.003 | 0.004 | 0.01 | 0.13 | 0.68 | 0.79 | 0.90 |
256 | 0.001 | 0.002 | 0.006 | 0.07 | 0.49 | 0.67 | 0.81 |
512 | 0.0005 | 0.0006 | 0.002 | 0.03 | 0.32 | 0.53 | 0.81 |
可以看到:
- 随着问题规模增加,同进程数下效率不断增加
- 随着进程数增加,同问题规模下效率不断减少
此外,部分数据出现了效率略大于 1 的情况,可能是由于系统运行时的「抖动」造成的,仍然在误差范围内,符合常识。
源代码integral.c
去掉#define WK_SIMPSON
前的注释符号即可选用自适应辛普森公式求解瑕积分。
去掉#define WK_P2P
前的注释符号即可选用点对点通信。
作业脚本integral.pbs
对于不同的核数需要不同的作业脚本,这里只给出 512 核对应的作业脚本integral.pbs
。
这里学习了一下从 PBS 的本地变量中获得 mpiexec 的运行配置的方法。
输出文件integral.o1761
对于不同的核数需要不同的输出文件,这里只给出 512 核对应输出文件integral.o1761
。
其他核数对应的输出文件见附件
integral.o1795
对应 256 核(nodes=8:ppn=32
)integral.o1796
对应 128 核(nodes=4:ppn=32
)integral.o1797
对应 64 核(nodes=2:ppn=32
)integral.o1798
对应 32 核(nodes=1:ppn=32
)integral.o1799
对应 16 核(nodes=1:ppn=16
)integral.o1800
对应 8 核(nodes=1:ppn=8
)integral.o1801
对应 4 核(nodes=1:ppn=4
)integral.o1802
对应 2 核(nodes=1:ppn=2
)integral.o1803
对应 1 核(nodes=1:ppn=1
)
完成正则采样排序 PSRS 的 MPI 算法
排序文件放在集群的 shared_dir 目录下,文件名为:psrs_data。文件采用二进制格式:头八个字节为排序数据个数(long int), 每个数据为 8 个字节。
请按要求使用 MPI 集合通信:具体要求见课件。
psrs.c
这里遇到一个问题,MPI 数据类型选择MPI_LONG_INT
时发现传过来的数据大小只有 4 个字节?于是使用MPI_BYTE
类型,而将我们的数据按照比特重新计算大小并传输。
psrs.pbs
作业调度脚本,直接使用能够获得的最大计算资源(512 核)进行计算。
psrs.o1529
作业脚本得到的输出文件。可以看到,这里一共使用了18.124634s
就成功对4294967295
个数进行了排序,其中有6.403623s
花费在文件读入上,也就是总共花了不到十二秒就完成了排序,还是明显优于单机串行排序的。
填表
去掉#define WK_GEN_DATA
前的注释,进行测试(不再从输入文件中得到数据,各进程随机生成指定问题规模规模的数据)。
此外,由于并行正则采样排序的限制,排序元素的数量至少要是进程数的平方倍。因此这里问题规模从开始(进程数最多有个)。
由于数据规模过大,进程数少的时候运行时间超过一小时被调度器kill
了,因此对应数据没有测出运行时间和相应加速比。
运行时间 T(单位 s)
详见下面的输出文件部分。
进程数\问题规模 | ||||||
---|---|---|---|---|---|---|
1 | 0.04 | 0.35 | 7.10 | 936.30 | x | x |
2 | 0.04 | 0.23 | 3.10 | 463.53 | x | x |
4 | 0.05 | 0.20 | 2.12 | 229.26 | 890.62 | x |
8 | 0.05 | 0.18 | 1.56 | 149.69 | 592.83 | x |
16 | 0.05 | 0.20 | 1.22 | 80.37 | 250.98 | 490.23 |
32 | 0.09 | 0.22 | 0.96 | 43.30 | 176.67 | 277.68 |
64 | 0.14 | 0.47 | 0.89 | 21.77 | 136.30 | 90.79 |
128 | 0.21 | 0.44 | 1.13 | 17.87 | 33.21 | 44.52 |
256 | 0.29 | 0.45 | 1.28 | 9.22 | 17.43 | 23.93 |
512 | 0.38 | 0.57 | 1.47 | 5.10 | 8.30 | 14.47 |
加速比 S
加速比 S=同等规模下的串行时间/并行时间。
进程数\问题规模 | ||||||
---|---|---|---|---|---|---|
1 | 1.00 | 0.35 | 1.00 | 1.00 | x | x |
2 | 1.00 | 1.52 | 2.29 | 2.02 | x | x |
4 | 0.80 | 1.75 | 3.35 | 4.08 | x | x |
8 | 0.80 | 1.94 | 4.55 | 6.25 | x | x |
16 | 0.80 | 1.75 | 5.81 | 11.65 | x | x |
32 | 0.44 | 1.59 | 7.40 | 21.62 | x | x |
64 | 0.29 | 0.74 | 7.98 | 43.00 | x | x |
128 | 0.19 | 0.80 | 6.28 | 52.40 | x | x |
256 | 0.14 | 0.78 | 5.55 | 101.55 | x | x |
512 | 0.11 | 0.61 | 4.83 | 183.58 | x | x |
效率 E
运行效率 E=加速比 S/并行线程数。
进程数\问题规模 | ||||||
---|---|---|---|---|---|---|
1 | 1.00 | 0.35 | 1.00 | 1.00 | x | x |
2 | 1.00 | 0.76 | 1.15 | 1.01 | x | x |
4 | 0.20 | 0.44 | 0.84 | 1.02 | x | x |
8 | 0.10 | 0.24 | 0.57 | 0.78 | x | x |
16 | 0.05 | 0.11 | 0.32 | 0.73 | x | x |
32 | 0.01 | 0.05 | 0.23 | 0.66 | x | x |
64 | 0.005 | 0.01 | 0.12 | 0.67 | x | x |
128 | 0.001 | 0.006 | 0.05 | 0.41 | x | x |
256 | 0.0005 | 0.003 | 0.02 | 0.40 | x | x |
512 | 0.0002 | 0.001 | 0.001 | 0.36 | x | x |