主要内容

基准\ b

此示例显示如何在群集中求解线性系统。MATLAB®代码用于解决xa * x = b是非常简单的。最常见的是,使用矩阵左除法(也称为mldivide)或反斜杠操作符(\)来计算x(即,x = A \ b)。然而,基准测试矩阵对群体的性能并不直接。

基准测试最具挑战性的方面之一是避免落入寻找代表系统整体性能的单个数字的陷阱。我们将查看性能曲线,它可能帮助您识别集群上的性能瓶颈,甚至可能帮助您了解如何对代码进行基准测试,并能够从结果中得出有意义的结论。

相关例子:

此示例中显示的代码可以在此功能中找到:

功能结果= paralleldemo_backslash_bench (memoryPerWorker)

为群集选择适当的矩阵大小非常重要。我们可以通过指定每个工人可用的GB中的系统内存量作为此示例功能的输入来执行此操作。默认值非常保守;您应该指定适合您系统的值。

如果nargin == 0 memoryPerWorker = 8.00;GB的百分比%警告('pctexample:backslashbench:backslashbenchusingdefaultmemory',...%['每个worker可用的系统内存数量是',…%的未指定。使用保守的默认值',…%的%。每个工作人员2f g。'], memoryPerWorker);结尾

避免开销

为了准确衡量我们解决线性系统的能力,我们需要删除任何可能的开销来源。这包括获取当前并行池并暂时禁用死锁检测功能。

p =质量;如果isempty(p)错误('PCTExample:Backslashbench:poolclosed'...“此示例需要并行池。'...'使用parpool命令或设置手动启动池'...'你的并行偏好自动启动池。']);结尾poolsize = p.numworkers;pctRunOnAll'Mpisettings('''''''''''''');'
使用'bigMJS'配置文件启动并行池(parpool)…与12名工人相连。

基准功能

我们要基准矩阵左除法(\),而不是成本进入spmd块,创建矩阵所需的时间,或其他参数。因此,我们将数据生成与线性系统的求解分离开来,只测量完成后者所需的时间。我们使用二维块循环协分配器生成输入数据,因为这是求解线性系统最有效的分布方案。我们的基准测试包括测量所有工作人员完成求解线性系统所需的时间a * x = b.同样,我们试图消除任何可能的开销来源。

功能[a,b] = getData(n)fprintf('创建一个大小为%d-by-%d.\n'n, n);spmd%使用通常提供最佳性能的译码员%用于求解线性方程组。Codistr = CodistRibutor2dbc(codistributor2dbc.defaultlabgrid,...CodistRibutor2dbc.defaultblocksize,...'col');一个= codistributed。兰特(n, n, codistr);b = codistributed。兰特(n, 1, codistr);结尾结尾功能time = timeSolve(A, b)spmdtic;x = a \ b;我们不需要x的值。时间= gop(@max, toc);%全部完成的时间。结尾时间=时间{1};结尾

选择问题大小

就像许多其他并行算法一样,并行解线性系统的性能在很大程度上取决于矩阵的大小。我们的先验因此,预期是计算:

  • 对于小矩阵有点低效

  • 非常有效地对大矩阵

  • 如果矩阵太大以适合系统存储器,并且操作系统启动将存储器转换为磁盘

因此,对于多种不同矩阵大小的计算是值得注意的,以了解在这种情况下对“小”,“大”和“太大”均值的理解。根据之前的实验,我们期待:

  • “太小”的矩阵大小不是1000乘1000

  • “大”矩阵占用略低于每个工人可用的内存的45%

  • “太大”的矩阵会占用每个工作者可用的50%或更多的系统内存

这些是启发式,并且精确值可能会在释放之间发生变化。因此,我们使用跨越整个范围的矩阵大小来验证预期的性能。

注意,通过根据工人的数量来改变问题的规模,我们使用了弱尺度。其他基准测试示例,如PARFOR使用Blackjack的简单基准测试在群集中基准标记独立工作,也采用弱缩放。由于这些示例基准任务并行计算,它们的弱缩放包括使与工人数量成比例的迭代次数。然而,此示例是基准测试数据并行计算,因此我们将矩阵的大小限制与工人数量相关联。

%声明矩阵尺寸范围从1000×1000到45%的系统%每个工作者可用的内存。maxmusageperworker = 0.45 * Moderperworker * 1024 ^ 3;%的字节。maxMatSize =圆(√maxMemUsagePerWorker * poolSize / 8));linspace(1000, maxMatSize, 5) = round(linspace(1000, maxMatSize, 5));

比较性能:Gigaflops

我们使用每秒浮点运算的数量作为性能的度量,因为这允许我们在不同的矩阵大小和不同的工人数量下比较算法的性能。如果我们在足够大的矩阵大小范围内成功地测试了矩阵左除法的性能,我们预计性能图将类似于以下所示:

通过生成这样的图表,我们可以回答以下问题:

  • 最小的矩阵是如此之小,我们表现不佳?

  • 当矩阵如此之大时,我们是否看到了性能下降,它占据了总系统内存的45%?

  • 我们可以为特定数量的工人实现最好的性能?

  • 对于哪种矩阵大小,16名工人比8名工人更好?

  • 系统内存是否限制了性能峰值?

给定矩阵大小,基准函数创建矩阵一个和右侧b一次,然后解一个\ b多次获得准确的衡量标准所需的时间。我们使用HPC挑战的浮动操作计数,从而为N-BY-N矩阵计算,我们将浮点操作计数为2/3 * n ^ 3 + 3/2 * n ^ 2

功能gflops = benchFcn(n) numReps = 3;[A, b] = getData(n);时间=正;%我们解决了几次线性系统并计算了Gigaflops%基于最好的时间。为了ITR = 1:numreps tcurr = timesolve(a,b);如果fprintf() == 1'执行时间:%f',tcurr);别的流(', %F',tcurr);结尾Time = min(tcurr, Time);结尾流(' \ n ');flop = 2/3 * n ^ 3 + 3/2 * n ^ 2;gflops = flop / time / 1e9;结尾

执行标准

完成所有设置后,就可以直接执行基准测试了。然而,计算可能需要很长时间才能完成,所以我们在完成每个矩阵大小的基准测试时打印一些中间状态信息。

fprintf(['具有%D不同矩阵大小的启动基准范围范围\ n'...'从%d-by-%d到%d.\n'],...长度(迷水),Matsize(1),Matsize(1),Matsize(END),...matSize(结束));gflops = 0(大小(matSize));为了gflops(i) = benchFcn(matSize(i));流(“吉拍:% f \ n \ n”,gflops(i));结尾结果。matSize = matSize;结果。gflops = gflops;
使用从1000 × 1000到76146 × 76146的5种不同的矩阵大小开始基准测试。创建一个大小为1000 × 1000的矩阵。分析并传送文件给工人…完成。执行时间:1.038931,0.592114,0.5575135 Gigaflops: 1.161756创建一个大小为19787-by 19787的矩阵。执行时间:119.402579,118.087116,119.323904 Gigaflops: 43.741681创建一个大小为38573 × 38573的矩阵。执行时间:552.256063,549.088060,555.753578 Gigaflops: 69.685485创建一个大小为57360 × 57360的矩阵。执行时间:3580.232186,3726.588242,3113.261810 Gigaflops: 40.414533创建一个大小76146 × 76146的矩阵。执行时间:9261.720799,9099.777287,7968.750495 Gigaflops: 36.937936

绘制性能

现在我们可以绘制结果,并与上面显示的期望图进行比较。

无花果=图;ax =轴('父母'图);情节(ax, matSize / 1000 gflops);行= ax.Children;行。标志='+';ylabel(斧头,“吉拍”)Xlabel(斧头,“矩阵大小以千计”) titleStr = sprintf([]'解决\\ b以获取不同矩阵大小的'...'%d工人'],池化);标题(AX,Tittestr,'口译员''没有任何');

如果基准测试的结果没有你期望的那么好,下面是一些需要考虑的事情:

  • 基础实施是使用缩放标签,其具有高性能的经过验证的声誉。因此,算法或图书馆非常不可能导致效率低下,而是如下所示的使用方式。

  • 如果对于您的集群来说,这些矩阵太小或太大,那么最终的性能将会很差。

  • 如果网络通信缓慢,性能将受到严重影响。

  • 如果cpu和网络通信都非常快,但是内存数量有限,那么您可能无法使用足够大的矩阵进行基准测试,以充分利用可用的cpu和网络带宽。

  • 为了实现最终性能,重要的是使用为网络设置量身定制的MPI版本,并使工人以这样的方式运行,即尽可能多地通过共享内存发生。然而,除了该示例的范围之外,解释如何识别和解决这些类型的问题。

比较不同数量的工人

现在我们来看一下如何通过查看使用不同数量的工作人员运行本例获得的数据来比较不同数量的工作人员。该数据是在不同的集群上获得的。

其他例子如在群集中基准标记独立工作已经解释说,当为不同数量的工人进行基准并行算法时,通常使用弱缩放。也就是说,随着我们增加工人的数量,我们将问题规模按比例提高。在矩阵左部的情况下,我们必须显示额外的护理,因为分割的性能大大取决于矩阵的大小。以下代码为我们使用和所有不同数量的工人进行测试的所有矩阵大小创建了Gigaflops中的性能图表,这为我们提供了矩阵左部的性能特征的最详细图片在这个特定的集群上

s =负载('pctdemo_data_backslash.mat'“workers4”“workers8”...'工人16''工人32'“workers64”);无花果=图;ax =轴('父母'图);绘图(AX,S.Workers4.Matsize./1000,S.Workers4.gflops,...s.workers8.matSize。s.workers8.gflops / 1000,...s.workers16.matSize。s.workers16.gflops / 1000,...S.Workers32.Matsize./1000,S.Workers32.gflops,...s.workers64.matSize。/ 1000, s.workers64.gflops);行= ax.Children;集(线,{“标记”},{'+''o''v'“。”'*'});ylabel(斧头,“吉拍”)Xlabel(斧头,“矩阵大小以千计”)标题(斧头,...不同工人数下求解A\\b的比较数据);传奇('4工人''8工人''16工人'“32工人...64年的工人“位置”“西北”);

我们在上面看图表时,我们注意到的第一件事是64名工人允许我们解决比只有4名工人的更大的方程线性系统。此外,我们还可以看到,即使人们可以在4名工人上使用大小60,000乘以60,000的矩阵,我们将获得大约10个Gigaflops的性能。因此,即使这位工人有足够的内存来解决这么大的问题,64名工人仍然可能会大大超越。

看着4名工人的曲线的斜率,我们可以看到三个最大的矩阵大小之间只有适度的性能。将其与早期的预期性能的图表进行比较一个\ b对于不同的矩阵尺寸,我们得出结论,对于矩阵尺寸为7772 × 7772的4个工人,我们已经非常接近达到最佳性能了。

查看8个和16个worker的曲线,我们可以看到在最大的矩阵大小下性能下降,这表明可用的系统内存已接近或已经耗尽。然而,我们看到第二和第三大矩阵之间的性能增长非常有限,这表明某种稳定性。因此,我们推测,当使用8或16个工人时,如果我们增加系统内存并使用更大的矩阵进行测试,我们很可能不会看到Gigaflops的显著增长。

看着32和64名工人的曲线,我们看到第二大矩阵尺寸和第三大矩阵尺寸之间存在显着的性能。对于64名工人,两种最大矩阵大小之间也存在显着的性能。因此,我们猜测我们在达到峰值性能之前,我们在32和64名工人用完了系统内存。如果这是正确的,则将更多内存添加到计算机允许我们解决更大的问题并在那些较大的矩阵大小上执行更好。

加速

用线性代数算法(如反斜杠)来测量加速的传统方法是比较峰值性能。因此,我们计算每个工作线程的最大Gigaflops数。

peakPerf = [max(s.workers4.gflops), max(s.workers8.gflops),...马克斯(s.workers16.gflops)、马克斯(s.workers32.gflops),...max(s.workers64.gflops)];DISP(“4-64个工人在Gigaflops上的峰值表现”)DISP(峰值)DISP('从4名工人到8,16,32和64名工人时加速:')DISP(峰值(2:结束)/峰值(1))
当从4个工人到8,16,32和64工人时的加速:2.1269 3.7245 6.7244 13.4532

因此,我们得出结论,当工人数量增加16倍,从4个工人增加到64个工人时,我们得到了大约13.5的加速。如上所述,性能图表明,通过增加集群计算机上的系统内存,我们可能能够提高64个工人的性能(从而进一步提高速度)。

群集使用

使用16双处理器,Octa-Core计算机生成此数据,每个计算机具有64 GB内存,与千兆以太网连接。使用4名工人时,它们都在一台计算机上。我们使用了2台工人的2台计算机,为16台工人等4台计算机等。

重新启用死锁检测

既然我们已经结束了我们的基准测试,我们可以安全地重新启用当前并行池中的死锁检测。

pctRunOnAll“mpiSettings(“DeadlockDetection”、“on”);”
结尾
gflops: [1.1618 43.7417 69.6855 40.4145 36.9379] gflops: [1.1618 43.7417 69.6855 40.4145 36.9379]