本次教程写的匆忙,也许会有很多勘误欢迎指正
性能优化部分
HPL.dat字段说明
在本实验中,为了不让生成的测试报告太复杂,让N=1,只测试一个矩阵
HPLinpack benchmark input file //说明性语句
Innovative Computing Laboratory, University of Tennessee //说明性语句
HPL.out output file name (if any) //输出文件名
6 device out (6=stdout,7=stderr,file) //选择输出方式
1 # of problems sizes (N) //要测试的矩阵个数
41448 Ns //不小于上一行的大小,Ns^2*64=节点内存*节点数*8,Ns=0.8~0.9D
//每节点有2G内存,4节点,Ns的算法为Ns^2*64=1024*1024*1024*2*4*8,-〉N=32000~33000,再*0.8~0.9即可
6 # of NBs //分块方法的种数
32 64 96 126 192 256 NBs //分块的大小用196,232,256效果比较好
0 PMAP process mapping (0=Row-,1=Column-major) //处理器阵列 按列排列 or 按行排列
3 # of process grids (P x Q) //二维处理器网格(P×Q)P×Q=系统CPU数=进程数
2 1 4 Ps //P≤Q,这是一个测试经验值,一般来说,P的值尽量取得小一点
2 4 1 Qs
16.0 threshold //阈值
3 # of panel fact //LU分解相关
0 1 2 PFACTs (0=left, 1=Crout, 2=Right)
2 # of recursive stopping criterium
2 4 NBMINs (>= 1)
1 # of panels in recursion
2 NDIVs
3 # of recursive panel fact.
0 1 2 RFACTs (0=left, 1=Crout, 2=Right)
1 # of broadcast
0 BCASTs (0=1rg,1=1rM,2=2rg,3=2rM,4=Lng,5=LnM)
1 # of lookahead depth
0 DEPTHs (>=0)
2 SWAP (0=bin-exch,1=long,2=mix)
64 swapping threshold
0 L1 in (0=transposed,1=no-transposed) form
0 U in (0=transposed,1=no-transposed) form
1 Equilibration (0=no,1=yes)
8 memory alignment in double (> 0)
保持其他参数不变,只修改P×Q,Ps,Qs三个参数,P×Q是电脑的cpu数目,可以通过如下命令查询:
sudo cat /proc/cpuinfo|grep cores
查看电脑的cpu进程数
sudo cat /proc/cpuinfo | grep processor
作者我的电脑cpu拥有4个cpu核心,共8线程,也就是所谓的“4核心双线程”电脑,即cpu里封装了四个计算核心,每个核心支持同时运行2个进程,所以我的电脑P×Q的最大值就是8,但是我在运行如下指令:
mpirun -np 8 ./xhpl > info.txt
会报如下错误:
根据查阅各种资料,应该是openMpi对电脑性能的错估造成的,可通过如下命令解决:
mpirun -oversubscribe -np 8 ./xhpl > info.txt
实践证明当加入了-oversubscribe字段后,线程数可以写的很大,但我不建议这么做,因为进程数如果大于cpu支持的最大进程数,那么会触发操作系统的进程调度,会大幅度降低性能,所以我们一般不要超过最大进程数。
我们要通过调参来找出性能最佳的参数,在HPL.dat中,我们可以去修改的,有矩阵阶数Ns,块大小NB,以及进程数P×Q,我们可以分为下面三组实验进行:
(1)改变测试线程数
分别在进程数为1,2,4,8四种情况下执行,注意要同步修改HPL.dat下的P×Q,Ps,Qs,画出相应的表格和曲线,根据控制变量法,在一组实验中要保持Ns和NB不变,可以在不同组实验中改变Ns和NB,为了减少实验数量,我们不改变Ns和NB,固定他们不变,测定进程数目的改变对性能的影响
在Ns=19200,NB=256(可变,多测几组效果更佳)的情况下,填写下面的表格:
进程数 | P | Q | 峰值性能 |
---|---|---|---|
1 | 1 | 1 | |
2 | 1 | 2 | |
2 | 2 | 1 | |
4 | 1 | 4 | |
4 | 2 | 2 | |
4 | 4 | 1 | |
8 | 1 | 8 | |
8 | 2 | 4 | |
8 | 4 | 2 | |
8 | 8 | 1 | |
16 | 1 | 16 | |
16 | 2 | 8 | |
16 | 4 | 4 | |
16 | 8 | 2 | |
16 | 16 | 1 |
得出在不同线程数下最佳的P,Q值:
线程数 | 最佳P | 最佳Q |
---|---|---|
1 | ||
2 | ||
4 | ||
8 | ||
16 |
记录其中峰值性能最高的一组P,Q
(2)分块优化性能
在HPL.dat文件中,NB表示分块大小,先要修改NB种数,再根据种数写分块大小:
4 # of NBs //分块方法的种数
1 2 3 4 NBs //分块的大小用196,232,256效果比较好
HPL默认值是有四种分块,可以分为1x1,2x2,3x3,4x4四种,这些分块可能太小了,根据资料中的经验值,分块采用192,232,256效果比较好,我们不妨采用等步长的方式,来确定最佳的分块大小。
①保持进程数目不变,测定不同Ns值下块大小对性能的影响
在上面得到的峰值性能最高的一组P,Q,完成表格:
Gflops | NB=32 | NB=64 | NB=96 | NB=128 | NB=160 | NB=192 | NB=224 | NB=256 |
---|---|---|---|---|---|---|---|---|
Ns=1920 | ||||||||
Ns=3840 | ||||||||
Ns=5760 | ||||||||
Ns=7680 |
绘制曲线
②保持Ns不变,测定不同进程数(P,Q取最佳)下块大小对性能的影响
Gflops | NB=32 | NB=64 | NB=96 | NB=128 | NB=160 | NB=192 | NB=224 | NB=256 |
---|---|---|---|---|---|---|---|---|
1线程 | ||||||||
2线程 | ||||||||
4线程 | ||||||||
8线程 | ||||||||
16线程 |
绘制曲线
(3)改变Ns
选取P,Q(峰值性能最高的一组P,Q)
Gflops | Ns=4000 | Ns=7000 | Ns=10000 | Ns=13000 | Ns=16000 | Ns=19000 | Ns=41448(这是我电脑的最佳理论值) |
---|---|---|---|---|---|---|---|
NB=32 | |||||||
NB=64 | |||||||
NB=96 | |||||||
NB=128 | |||||||
NB=160 | |||||||
NB=192 | |||||||
NB=224 | |||||||
NB=256 |
经过三个实验,可以初步粗略得到如何调参可以使性能最优
下面是作者实际实验的数据:
先介绍下如何跑测试
跑测试运行的指令格式如下:
mpirun -np 2 ./xhpl > 1920-32-1-2.txt
注意:为了写报告以及管理实验数据方便,txt文件的命名采用格式Ns-NB-P-Q.txt
mpirun是mpi执行可执行文件的一个程序,-np后跟着进程数(一定等于PxQ),./xhpl是hpl编译生成的一个默认的测试程序,当然我们也可以自己去书写带有mpi的c或者c++代码,编译后用mpirun执行
我们在测试的时候要不断的改参数,十分的麻烦,为此,作者写了一个python脚本,便于读者完成自动化测试,代码如下:
import os
from tqdm import tqdm #这个库需要用pip先安装一下
class MPIRun:
def __init__(self,Ns,NB,P,Q):
self.Ns = Ns
self.NB = NB
self.P = P
self.Q = Q
self.HPLDAT = '''HPLinpack benchmark input file
Innovative Computing Laboratory, University of Tennessee
HPL.out output file name (if any)
6 device out (6=stdout,7=stderr,file)
1 # of problems sizes (N)
%d Ns
1 # of NBs
%d NBs
0 PMAP process mapping (0=Row-,1=Column-major)
1 # of process grids (P x Q)
%d Ps
%d Qs
16.0 threshold
3 # of panel fact
0 1 2 PFACTs (0=left, 1=Crout, 2=Right)
2 # of recursive stopping criterium
2 4 NBMINs (>= 1)
1 # of panels in recursion
2 NDIVs
3 # of recursive panel fact.
0 1 2 RFACTs (0=left, 1=Crout, 2=Right)
1 # of broadcast
0 BCASTs (0=1rg,1=1rM,2=2rg,3=2rM,4=Lng,5=LnM)
1 # of lookahead depth
0 DEPTHs (>=0)
2 SWAP (0=bin-exch,1=long,2=mix)
64 swapping threshold
0 L1 in (0=transposed,1=no-transposed) form
0 U in (0=transposed,1=no-transposed) form
1 Equilibration (0=no,1=yes)
8 memory alignment in double (> 0)'''%(Ns,NB,P,Q)
def run(self):
HPLDAT_FILE = open("./HPL.dat","w",encoding="utf-8")
HPLDAT_FILE.write(self.HPLDAT)
HPLDAT_FILE.close()
cmd = "mpirun -np %d ./xhpl > %d-%d-%d-%d.txt"%(self.P*self.Q,self.Ns,self.NB,self.P,self.Q)
print("\r",cmd,end="",flush=True)
os.system(cmd)
if __name__=="__main__":
script_deal = open("./script.txt","r",encoding="utf-8")
lines = script_deal.readlines()
for line in tqdm(lines):
runlist = [int(i) for i in line.split(" ")]
Ns,NB,P,Q = runlist[0],runlist[1],runlist[2],runlist[3]
mpirun = MPIRun(Ns,NB,P,Q)
mpirun.run()
使用程序前,首先要编写script.txt,记录需要做的实验参数,例子如下:
26214 192 1 2
26214 224 1 2
26214 256 1 2
这个例子就是要做三组测试,每行对应着一组实验的参数,将script.txt和上述python脚本一起放在你的test文件夹下,执行即可开始测试(python脚本十分容易理解,读者看看就会用了)
在这里作者建议读者不要一次性把所有实验都做完,因为有的测试需要的时间非常的长(可能长达1h或更长),建议结合自己的时间安排,合理安排测试数量,不要浪费时间(写这个脚本的目的也是能让你们睡觉的时候也能跑测试)
然后作者通过几天断断续续的测试,得到了如下的实验结果:
(1)改变测试进程数
在Ns=19200, NB=256的条件下进行实验:
进程数 | P | Q | 峰值性能GFLOPS |
---|---|---|---|
1 | 1 | 1 | 48.452 |
2 | 1 | 2 | 72.299 |
2 | 2 | 1 | 69.891 |
4 | 1 | 4 | 61.060 |
4 | 2 | 2 | 49.735 |
4 | 4 | 1 | 18.128 |
8 | 1 | 8 | 62.019 |
8 | 2 | 4 | 63.353 |
8 | 4 | 2 | 61.770 |
8 | 8 | 1 | 56.499 |
16 | 1 | 16 | 59.354 |
16 | 2 | 8 | 60.713 |
16 | 4 | 4 | 58.937 |
16 | 8 | 2 | 53.201 |
16 | 16 | 1 | 41.334 |
线程数 | 最佳P | 最佳Q |
---|---|---|
1 | 1 | 1 |
2 | 1 | 2 |
4 | 1 | 4 |
8 | 2 | 4 |
16 | 2 | 8 |
//绘图代码,复制到echarts官网可直接画出图,记得修改data数组
var data = [
[[1,1,48.452,'48.452'],[1,2,72.299,'72.299'],[2,1,69.891,'69.891'],[1,4,61.060,'61.060'],[2,2,49.735,'49.735'],[4,1,18.128,'18.128'],[1,8,62.019,'62.019'],[2,4,63.353,'63.353'],[4,2,61.770,'61.770'],[8,1,56.499,'56.499'],[1,16,59.354,"59.354"],[2,8,60.713,"60.713"],[4,4,58.937,"58.937"],[8,2,53.201,"53.201"],[16,1,41.334,"41.334"]],
];
option = {
xAxis: {
name:"P"
},
yAxis: {
scale: true,
name:"Q"
},
series: [{
data: data[0],
type: 'scatter',
symbolSize: function (data) {
return data[2];
},
emphasis: {
focus: 'self'
},
labelLayout: {
y: 20,
align: 'center',
hideOverlap: true,
moveOverlap: 'shiftX'
},
labelLine: {
show: true,
length2: 5,
lineStyle: {
color: '#bbb'
}
},
label: {
show: true,
formatter: function (param) {
return param.data[3];
},
minMargin: 10,
position: 'top'
}
}]
};
根据上图所示,圆的半径反映了性能的大小,可见,当P越小,圆的半径普遍较大,所以我们让最佳的P,Q取P=1,Q=x(当然实际上一些P=2的情况会更优,但P=1性能也很好,这里我们采取网友们的普遍结论),而且我们观察到,P=1,Q=2时性能是极佳的
(2)分块优化性能
①保持进程数目不变,测定不同Ns值下块大小对性能的影响
由上个实验所得出的结论,P=1,Q=2时,性能最好,我们就在这个条件下进行试验
Gflops | NB=32 | NB=64 | NB=96 | NB=128 | NB=160 | NB=192 | NB=224 | NB=256 |
---|---|---|---|---|---|---|---|---|
Ns=1920 | 55.492 | 64.157 | 63.433 | 61.824 | 60.856 | 58.970 | 57.621 | 57.816 |
Ns=3840 | 41.972 | 62.389 | 64.163 | 66.049 | 65.053 | 64.897 | 62.576 | 63.057 |
Ns=5760 | 44.493 | 69.654 | 70.535 | 72.185 | 71.514 | 70.456 | 72.007 | 72.132 |
Ns=7680 | 41.082 | 56.983 | 71.761 | 72.803 | 64.299 | 71.132 | 71.813 | 69.620 |
对于作者的计算机,在块大小为128时,性能很高,并且这种高几乎不随Ns的变化(Ns取不同值)而变化
②保持Ns不变,测定不同进程数(P,Q取最佳)下块大小对性能的影响
我们选取Ns=5760,进行以下实验
Gflops | NB=32 | NB=64 | NB=96 | NB=128 | NB=160 | NB=192 | NB=224 | NB=256 |
---|---|---|---|---|---|---|---|---|
1x1 | 39.280 | 42.643 | 40.980 | 42.232 | 42.240 | 40.860 | 42.536 | 43.965 |
1x2 | 44.493 | 69.654 | 70.535 | 72.185 | 71.514 | 70.456 | 72.007 | 72.132 |
1x4 | 14.986 | 22.929 | 29.961 | 33.501 | 34.481 | 36.444 | 36.837 | 37.956 |
1x8 | 28.070 | 39.357 | 38.165 | 36.604 | 35.635 | 34.797 | 34.465 | 34.918 |
1x16 | 21.992 | 26.386 | 25.316 | 26.027 | 25.020 | 24.365 | 24.515 | 27.128 |
对于作者的计算机,无论在哪种进程配置下,分块为128时的性能都是极佳的,看来我们可以说128就是作者计算机的最佳分块大小。
(3)改变Ns
首先计算自己计算机的最佳Ns理论值,满足:
N
s
2
×
64
=
1024
×
1024
×
1024
×
内
存
大
小
(
G
B
为
单
位
)
×
8
Ns^2×64=1024×1024×1024×内存大小(GB为单位)×8
Ns2×64=1024×1024×1024×内存大小(GB为单位)×8
如果计算机的内存是8GB,那么Ns的最佳理论值应该是26214
在P=1,Q=2的条件下,进行实验
Gflops | Ns=4000 | Ns=7000 | Ns=10000 | Ns=13000 | Ns=16000 | Ns =19000 | Ns=26214(这是作者电脑的最佳理论值) |
---|---|---|---|---|---|---|---|
NB=32 | 47.859 | 44.144 | 45.674 | 46.315 | 46.370 | 47.614 | 45.270 |
NB=64 | 69.577 | 70.660 | 67.506 | 71.035 | 69.953 | 68.433 | 63.181 |
NB=96 | 70.789 | 72.262 | 68.908 | 67.287 | 71.174 | 74.202 | 65.766 |
NB=128 | 70.655 | 74.478 | 77.857 | 79.504 | 72.060 | 74.230 | 68.085 |
NB=160 | 67.129 | 72.630 | 75.576 | 76.190 | 68.405 | 74.453 | 68.958 |
NB=192 | 68.208 | 75.330 | 76.797 | 78.838 | 75.632 | 69.769 | 69.856 |
NB=224 | 67.667 | 75.131 | 77.571 | 72.232 | 72.225 | 69.388 | 68.200 |
NB=256 | 67.652 | 73.735 | 78.481 | 73.045 | 73.943 | 70.420 | 69.858 |
观察上图,可见,在Ns=13000时,性能极佳,当然在NB不同时会有偏移。
由此观之,作者的计算机在P=1,Q=2,Ns=13000,NB=128的时候会取到最佳性能,当然Ns和NB由于实验粒度太大,得到的只是粗略的估计值,只作为参考。