Matlab并行计算
在利用matlab进行计算时,会遇到循环次数过大,或者是单次计算量过大的问题,比如需要计算的数值阵列数据量过大,利用传统的编程方式,跑一次程序几个小时。如果遇到这种情况,则可以尝试一下MATLAB并行计算,传统的计算方式都是串行计算。并行计算之所以可行,取决于两方面因素:a)现在大家的计算机是多核的,至少也是双核了吧,有的可能8核都有,这是很重要的硬件基础。b)MATLAB本身提供了很好的并行计算函数,加上你的聪明智慧,设计合理的软件,这样就有了软件基础了。
I. 如何并行计算
并行运算其实就是主线程和子线程的一个任务分配和汇总的实现。这种实现过程需要三个基本步骤:1、需要创建几个workers。2、把任务划分,然后分配给workers。3、整合结果,释放workers。
首先打开MATLAB命令窗口,输入matlabpool open
就OK了。当然也可以配置使用核心数:matlabpool open local 4
。最好就是有几个核心就开几个,这样效率比较高。
当程序运行完成后,释放workers:matlabpool close
。
II. 并行编程方法
具体实现parallel program主要是通过parfor(parallel for)和SPMD(single program, multiple data)完成的。parfor,spmd不可以相互或者自身嵌套。其他关于spmd vs. parfor的可以参考这个帖子。
II.I. parfor(parallel for)
parfor只用于matlab并行循环。当你需要简单计算的多次循环迭代时,例如蒙特卡洛(Monte Carlo)模拟,parfor循环就很有用。parfor将循环迭代分组,那么每个worker执行迭代的一部分。当迭代耗时很长的时候parfor循环也是有用的,因为workers可以同时执行迭代。这种循环代替有几点说明:
使用parfor前提必须开启matlabpool,否则等于for。
要求各个循环的内容是独立的(independent)。parfor不能像for一样多层内嵌。parfor不能调用与上一个循环结果相关的变量,否则就等与for了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19% 都可以把for循环改成parfor
total = 0.0;
big = - Inf;
for i = 1 : n
total = total + x(i);
big = max ( big, x(i) );
end
for i = 1 : n
angle = ( i - 1 ) * pi / ( n - 1 );
t(i) = cos ( angle );
end
% 这种依赖前一个循环的结果则不能转换成parfor
% 还有有用到break continue return 这些类型都不能使用parfor
dx = 0.25;
x = zeros (1,n);
for i = 2 : n
x(i) = x(i-1) + dx;
end所谓透明(transparency),即指parfor循环体中不能出现类似eval一类的函数。一个程序并行时要共享内存,而eval语句可能使程序进入错误的workspace,因此不要用eval,改用不同index赋值。
1
2
3
4
5
6
7
8
9
10
11% 不能出现类似eval一类的函数
parfor i=1:10
eval(['disp(num2str(i))'])
end
%改用不同index赋值
matlabpool local 2;
c = 1:5;
parfor i = 1:length(c)
a(i) = c(i);
end当parfor的循环体中存在对同一个矩阵的不同部分的操作时,会报错。原因在于matlab的parfor功能不允许循环体中出现对矩阵的某些部分独立地计算。 笼统说来,解决方法是将循环体中计算或者修改的内容记录在一个temp矩阵中,parfor循环全部完成后,再简单地用for将temp中的内容赋值给实际需要的矩阵即可1。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27%% 直接将for改称parfor是会出错的。
clear
A1=magic(6);
x=unique(ceil(rand(1,10)*36));
parfor i=1:length(x)
A1(x(i))=1000+round(rand(1)*36);
end
%% 对这个特定问题,甚至不需要循环
clear
A1=magic(6);
x=unique(ceil(rand(1,10)*36));
A1(x)=A1(x)+1000+round(rand(1,length(x))*36);
%% 解决方法是将循环体中计算或者修改的内容记录在一个temp矩阵中
matlabpool local 2
clear
A2=magic(6);
x=unique(ceil(rand(1,10)*36));
temp=[];
parfor i=1:length(x)
temp=[temp [x(i);round(rand(1)*36)+1000]];
end
for i=1:length(x)
A2(temp(1,i))=temp(2,i);
end
matlabpool close
II.I.I. 案例
1 | tic |
II.II. SPMD(single program, multiple data)
Spmd中的“Single program”方面指的是同一段代码运行在不同的多个lab上,就是说同一段程序应用于不同的样本(数据),所以一般针对随机抽样的并行 。你在一个Matlab客户端上运行一个程序,被标志为spmd模块的其他部分运行在其他lab上。当这些块运行完毕后,你的程序继续在客户端运行。 “Multiple data”方面指的是虽然spmd语句在所有的lab上运行相同的代码,但每一个lab可以有不同的,独有的数据。所以多数据集可以在多个lab上同时被容纳。
1 | %% SPMD |
另外,SPMD也可以用于可替代parfor的块并行,在不同lab(worker)上对相同或不同的数据执行不同的并行操作。
1 | %example3 - deal with same Data by different parameters |
III. BUG调试
matlabpool Java exception occurred: java.net.UnknownHostException: Your_Host_Name at java.net.InetAddress.getLocalHost(Unknown Source)
: 在/etc/hosts
这个文件里增加一行我们本地ip和主机名即可:127.0.0.1 Your_Host_Name
,Your_Host_Name这个每个人电脑都不同。