MATLAB多核并行计算使用方法
对于在使用matlab中出现计算速度慢等情况,只有干等它跑出结果吗,可以使用多核进行并行计算加速matlab仿真的速度,好的东西当然有其局限性。
常用个人版CPU都是主打高频率,甚至超频来增加其工作速度,对于核心数不会特别追求,而对于工作站式的CPU,通常频率较低,核心和线程数低,而当这样的CPU来运行matlab程序,会出现如下问题
可以看到CPU的核心利用率很低,且大部分的内核都没有工作,只有少数的CPU在工作,这样的工作站对于matlab的运行速度甚至还没有个人版频率较高的CPU运行速度快,那这不是有力无处使,所以对面多核CPU,一定要使用其并行运算的能力。
matlab并行计算局限性
首先来介绍并行计算的局限性,matlab多核并行计算就是创建多N个启动并行工作池,然后将程序分成N个部分并行执行。下图可以看到我开启了40个并行工作池(前提是CPU的内核数大于40)。
从这上面可以理解,如果程序在运算过程中是前后关联的,这将不能用于并行计算。如何算作前后关联,举个例子:
第一张图是没有前后关联的,但是第二个存在着数组的前后关联,想象以下,如果第二种情况那去并行运算,当前循环结果依赖上一循环的结果,这种肯定无法拆分去做并行计算。
所以能进行并行计算很重要的要求是,循环可拆分,即每次循环的时候初始状态相同。
parpool和parfor
进行matlab并行计算主要依靠parpool和parfor,
parpool即是开启并行工作池,
parpool('Processes',16); %开启16个并行工作池
CoreNum=16; %设定机器CPU核心数量
if isempty(gcp('nocreate')) %如果并行未开启
parpool(CoreNum); %开启16个并行工作池
end
delete(gcp) % 关闭并行池
parfor的使用方法和普通的for循环不太相同,主要区别是,parfor循环中有5类变量:
-
loop variable(循环变量)
在上图中是parfor后面的i,在循环中禁止给i(循环变量)赋值。 -
Sliced input variable(切片变量)
一般是数组,被各个处理器分割成一个个slice,首先要在parfor外申明该变量,比如上面的r=rand(1,10); 每次循环只能访问切片变量一个 位置,且固定不能重复,比如r(i)=temp1; 或者 r(i+1)=temp2;
slice变量的下标一定要连续,例a(2*i)=temp;是不行的。 -
Broadcast variable(广播变量)
指外部变量,且在循环内未被重新赋值。如上面在外面定义的c。 -
Reduction variable
该变量遍历所有循环,并且跟运行结果无关。Matlab 会自动识别该类变量,并正确输出结果。如下例所示, Matlab 会按正确顺序输出 x2
x2 = [];
n = 10;
parfor i = 1:n
x2 = [x2, i];
i,
end
上面这段代码里, 因为将parfor拆分成很多分给并行计算池去计算,所以i 并不一定按照 1 到 10 的顺序输出,但 x2 的结果却必然是 1 到 10。
- temporary variable(临时变量)
临时变量不需要有sliced变量的限制,使用更自由。
但是在使用parfor时,这会弄混sliced变量和临时变量。临时变量在parfor里面申明,sliced变量是在parfor外面申明的。一定要有parfor会被拆分成很多份给并行计算池计算的概念,sliced变量能用是因为每个循环都只使用该变量的一个位置,而临时变量之所以能用是因为每次循环的开始就对其进行初始化,所以其初始状态相同,当然可以使用。例如下面b,每次parfor循环的初始状态都是1行4列的全0序列,所以当然能够使用。临时变量还一个最大的特点是只在parfor中有定义,在parfor外没有定义。
parfor i=1:10
b=zeros(1,4);
for j=1:4
b(i)=i+1;
end
a(i)=b; %a是sliced变量
end
一个简单的parfor并行计算程序。
parfor中变量的提取
因为parfor中定义的变量均是临时变量,在parfor外相当于未定义,所以需要有手段能将parfor中产生的数据提取出来。
比如里面定义的临时变量,可以将最后的结果赋值给已经在外面定义的切片变量,这样在外面可以使用,如下所示,proba可以是定义成数组,元组。这样每个parfor循环的数据都存到了proba中。
proba=cell(1,N)
parfor f = 1:N
%now use proba{f} inside the loop
proba{f}=(1/M)*ones(1, M);
% rest of the code
end
%get proba from whatever iteration you want
pi_proba = proba{N};