MATLAB并行编程

Original url:

http://blog.sina.com.cn/s/blog_6b597bfb01018wa5.html


1 并行问题的由来——从抛硬币说起
       举个简单的例子:抛100次硬币统计正面向上的次数。我们可以拿一个硬币重复地抛100次。但有人嫌麻烦,就想能不能再叫一个人带另外一个硬币过来,两个人同时抛,这样每个人就能只抛50次了,节约了时间,并行的思想初现。问题来了,必须保证这2个硬币完全相同以及抛硬币者的动作一致性,才能确保该并行试验的完备性。那么怎么能够保证这两个条件呢?几乎不可能。但我们的计算机软件就能保证试验对象的完全一致性,因为我们可以设置伪随机数的初始种子在每次重复试验的时候一模一样,计算机软件在整数域上的计算上只要不溢出,是100%正确的。
       回到现实,可能很多人都会说自己的仿真好慢好慢,有时候一个仿真可能要1个月。其实,究其根本,仿真之所以这么慢,并不是我们的算法复杂度有多高,根本原因是我们仿真的问题大多情况下是一个随机过程,我么需要不停地拿随机数据去验证算法是否正确。不管是电子、金融,航空航天,亦或是其它领域,我们所面对的问题都是一个随机过程。在概率论中就有一条很著名的公理:当试验的次数趋于无穷大的时候,事件发生的频率才等于概率。该理论思想也导致了蒙特卡洛仿真模型的诞生。所以,为了验证自己算法设计的准确性,我们一般都需要尝试很多次试验,即仿真很大的数据量,这样才能和我们自己推出的理论结果进行有意义的比较。
       由此,从这里切入并行的概念。我们可以让不同的人或者计算机同时进行相同的试验,这就叫并行。用更专业的解释:利用多核、多处理器或者计算机集群进行运算都可以叫做并行处理。目前我们电脑中常用的软件基本都是串行的,在数字IC/FPGA领域,很多的EDA软件都支持并行,比如,Quartus中的P&R就可以并行。当然,并不是所有软件能设计为并行,并行软件的设计是要有前提条件的,下一节会讲。
2 能够并行运行的条件
       若要并行,必须满足以下2个条件之一:
  • 每次循环之间是相互独立的;
  • 循环执行完之后的结果和循环执行的先后次序无关。
第1条的例子
for k = 1 : 100
       a(k) = k;
end
即每次循环的k之间是互不相关的。
第2条的例子
s = 0;
for k = 1 : 100
       s = s + k;
end
等差数列的和和加法的次序无关,可以从1顺序加到100,但也可以先加100,再加99……这种和循环执行的先后次序无关的操作在MATLAB中叫简约循环,也能在MATLAB中并行化。
       本人做通信相关,通信系统的仿真一般都要跑系统在不同SNR下的性能,其中每个SNR的仿真过程互不影响,满足以上并行的第一个条件。所以,这就是我们并行仿真的切入点。我们可以让不同的处理单元(Processing Unit)仿真不同SNR下的性能。
3 MATLAB并行开关
       以matlabpool为关键字的命令。关于matlabpool的用法,Help中一大堆,这里就不细讲了,列举几个常用的命令:
  • matlabpool open % 以默认配置方式打开MATLAB并行开关
  • matlabpool open size % 打开MATLAB并行开关并指明并行的核数为size
  • matlabpool close % 关闭MATLAB并行开关
       目前Intel的CPU很多都支持超线程(Hyper Threading,HT)技术,双核4线程或者4核8线程的CPU比比皆是,在windows下最大的并行度只能到核数,而不是线程数,而在Linux下有告诉我说可以最多开到线程数的,我也没试过,这里就抛砖引玉吧。常用的就以上3条命令。
4 MATLAB并行工作原理
       MATLAB并行基于client-worker模式。首先,MALTAB有个总体负责的client,它将任务合理分配给每个worker,worker的个数即为上一节matlabpool open size命令中的size。每个worker运行完之后将结果回传给client。这样,当client接收到所有的结果后,程序运行完成。
       打开了MATLAB并行开关之后,我们可以在任务管理器中看到MATLAB进程的数目是size+1,即为client和worker的数目和。在运行过程中,worker会满载CPU;但是client不会满载CPU,它只负责分配任务、传递数据和最后的数据采集。
5 在MATLAB中编写并行代码
       好了,现在我们知道什么样的代码能够并行执行了,也知道如何在MATLAB中打开并行开关了,那么接下来就介绍如何在MATLAB中编写最简单的并行代码:parfor
       parfor,从字面意思就可以看出,parallel for,即并行的for循环。
       parfor循环把变量分为五类:循环变量、广播变量、临时变量、分段变量以及简约变量。虽然有时候我们把for直接改成parfor后没有出现什么问题,但一旦遇到稍微复杂的代码,就会不知所措。搞清楚这五类变量不仅能够避免语法错误,更重要的是能够提高MATLAB的效率,减小代码的运行时间。举例如下:
a = 100;
s = 0;
parfor k = 1 : 100 % k为循环变量
    if a > 10 % 广播变量
        b = 10; % 临时变量
    end
    c(k) = k; % 分段变量,输入属性
    d = 1e3; % 临时变量
    s = s + k; % 简约变量
end
  • 循环变量:即代码中的k。
  • 广播变量:该变量不是由worker产生,而是当client把任务分配给worker时传给worker的,每个worker都会接收到此类变量。
  • 临时变量:在worker中产生,worker执行完任务之后clear掉,也不会传递给client的变量。
  • 分段变量:这是parfor中最为重要的变量,可以具有输入和输出两类属性。
  • 简约变量:之前已经介绍过了,不再赘述。
       这5种变量的详细说明在Help中都有,要写出来的话会花大量的篇幅,省略。
6 并行编程之优化
       从前面的描述已经知道,MATLAB的并行程序运行是基于client-worker模式,那么我们的优化目标也会集中在以下2方面:
  • 针对worker的优化;
  • 针对client和worker之间数据交互的优化。
       第一个问题,针对worker的优化,我的前一篇博文《MATLAB并行编程》已经说了一些技巧,这些技巧完全适用于此。
       第二个问题,针对client和worker之间数据交互的优化。我们要尽量减小client和worker之间传递的数据量以节省时间。

一个关键的问题:还用预分配内存吗?上一篇博文《MATLAB并行编程》
http://blog.sina.com.cn/s/blog_6b597bfb01018sjm.html
中曾经提到过要在循环之前预分配内存。但是到了并行编程,这个原则应当被摒弃。因为一旦在parfor之前预分配内存了,该分段变量就具备了输入属性,已在client中生成了,client再将其分段传递给不同的worker,这会消耗额外的时间。所以,我们的原则是:分段变量能不增加输入属性就不增加。
       假设单核运行某程序需要时间t,那么N核并行运行的程序的执行时间为t/N+e,这里的e代表的就是client和worker之间数据交互所占用的时间。只要我们这部分的优化做得好,是可以让e相对于t/N忽略不计的。
7 并行编程之调试
       一般推荐先用for循环写好,程序运行正常之后把for改成parfor,这时如果运气好的话可以直接并行运行,但往往会报一些warning或者error,这就要考验我们对于上几节所说的变量分类了。而且就算没报warning或者error,程序在运行过程中也可能出错,往往这种错误不像串行程序报出是在哪一行的错误,并行的错误一般都会报出“No Remote Error Stack”,按照字面意思就知道,没有栈信息。因为按照普通的代码,调用函数前会先将变量入栈以及保护现场,而到了并行编程,每个worker出错的具体情况是不会回传给client的。因此,我们很难通过返回到command window的错误信息判断程序到底出了什么错。
       在parfor循环内部是不能加断点的,这其实也很好理解,本来不同的循环变量针对的就是不同的worker,在parfor内部加断点的话,软件根本不知道编程人员想要看哪个worker的数据。
       不过,MATLAB还提供了另一种并行调试模式——pmode,主要命令下面3条:
pmode start % 开启并行调试(默认核数)
pmode start size % 开启size个核的并行调试
pmode exit % 退出并行调试模式
       但自己用过一段时间pmode,老实说,不好用。用该调试模式的话需要手动一行一行输m语言。而且在每个worker中的变量还需要手动传递到client,就是各种不方便。还是希望MATLAB以后能支持直接在Editor中的parfor循环内部加断点(虽然就像上段所说会有困难,但相信MathWorks公司的攻城狮一定能解决这个问题!),再加上一些labindex类似的调试语法规则,这样一定能大大提高程序设计人员的开发效率。
8 其它MATLAB并行编程
   除了基于parfor的并行,MATLAB还支持其他种类的并行:
  • SPMD(Single Program Multiple Data),即单指令多数据,即假使我们有一个很大的数据文件,用串行的代码可能要读取很久,现在将数据文件分割,用相同的程序读取不同的数据。这种并行方式很适合目前炒的火热的海量数据处理。
  • 基于cluster的并行。parfor只能在一台电脑上的单CPU中的不同核上进行并行运算,这明显还是太弱了,MATLAB也支持在一台电脑的多CPU上并行,甚至还能支持多台电脑并行处理,往更大的说,即基于cluster的并行。不过,这时client和worker之间的通信已经不再是核间通信了或者一台电脑内部的CPU之间的通信了,而是基于局域网的通信,局域网的速度特会大大影响并行代码的执行效率。不过现在千兆以太网随处可见,只要通信量不是特别大,估计整个程序的瓶颈还是在每个worker上的代码优化。
  • 基于GPU的加速。最近2年GPU加速(不仅仅是针对MATLAB平台)也是炒的沸沸扬扬,很幸运MATLAB也支持GPU加速,不过对这部分的内容丝毫不懂。

你可能感兴趣的:(VRlab)