一、基本概念和并行原理
我们知道,只有当循环体内的操作相互独立时才可以用parfor关键字进行并行。但是有一类特殊的循环操作不受这个条件限制,这类操作我们称为简约操作。
注意:简约操作所对应的循环体并不独立,但是执行结果与执行顺序无关。
示例(求1到1000之和)
a=0; N=1000; %给a和N赋值
for i=1:N %for循环变量i从1到N开始递增
a=a+i; %循环体执行的是a=a+i,a+i每次都替换a
end %循环结束
(a=a +i这是循环体,里面涉及到循环变量a+i ,等号后边的a 每次都是变换的,与循环变量有关并不是相互独立的,但是我们知道这1000个数相加,先加哪个和后加哪个是不影响结果的,所以这类操作仍然用并行循环来完成,而这个约定与之前的约定矛盾了,所以这类操作都归为简约操作。)
在执行for循环时,循环操作依赖于上次执行结果,但是最终结果和执行顺序无关。
Matlab对简约操作进行了处理,允许采用parfor关键字对简约操作对应的循环进行并行。最终结果与for循环结果一致。
将这个示例可以推广,只要是求和问题满足(其中f(i)是关于i的表达式)。
Matlab中简约操作的并行原理:
(把执行任务由客户端发给各个work,各个work执行相同的程序副本,变量i是不一样的,执行完之后,各个work在把执行结果发给客户端,客户端再进行汇总,得出结果)
二、简约操作并行效率分析
对于不同的函数f,对应的计算量不同,从而影响简约操作的并行效率。我们只分析一种简单情形:
示例
function [tparfor,tfor]=partry16(nn)
a=0;
mypool=parpool; %打开并行计算池
tic; %计时开始
parfor kk=1:nn %并行循环开始执行
a=a+kk; %a加循环变量kk赋给等号前面的a,关于数的累加运算
end %结束并行循环
tparfor=toc; %计时结束,把并行循环的运行时间赋给tparfor
delete(mypool) %关闭并行计算池
display(strcat('partry16',':','parfor:',num2str(tparfor),'seconds'));
%显示并行循环执行的时间
b=0;
tic; %计时开始
for kk=1:nn %串行循环开始
a=a+kk; %累加运算
end %结束串行循环
tfor=toc; %计时结束,把串行循环的运行时间赋给tfor
display(strcat('partry16',':','for:',num2str(tfor),'seconds'));
%显示串行循环的执行时间
可以发现,随着循环次数的增加,parfor关键字的并行效率开始提高。
三、简约操作的执行过程
初始计算时,初值不会从client传输到worker;
遇到关键字parfor,循环被分段分配到worker;
各worker分段独立执行循环体内的操作;
各个worker计算完后结果传输至client;
client将各个结果进行综合得到最终结果。
四、简约操作与简约变量的特征
在Matlab程序设计中,称满足简约操作的操作符为简约操作符。简约操作符的操作对象称为简约变量(只有在parfor中才有意义)。
简约操作是一种特殊的操作,其操作结果与操作顺序无关。下面我们给出一些简约操的表达形式: x=x+expr; x=x-expr; x=x.*expr; x=x*expr x=x&expr; x=x|expr;x=[x,expr]; x=[x;expr]; x={x,expr}; x={x;expr}; x=min(x,expr); x=max{x,expr}; x=union(x,expr); x=intersect(x,expr)。以上操作和变量可以调换位置。
简约变量在parfor中应该满足一些要求
1、简约变量只能出现在简约赋值操作的表达式中
简约操作可以由上面给的各种操作符引导,也可以自己定义。
简约操作的表达式:x=f(x,expr) 其中x为简约变量,expr为表达式,f为符合简约操作的函数。
注意:如果f为变量,需要在parfor之前赋值,不能在parfor中赋值。
在Matlab操作符中,&&和||操作符不能进行简约操作。
%错误用法
f = @(x,k)x * k; %@定义一种运算,x与k相乘。(循环体外)
parfor i = 1:n
a = f(a,i) %执行运算
f = @times; %又用@定义了一种运算。这里是错误点,@这种运算是不能在并行循环中去定义,它只能在并行循环外去定义。
end
%正确用法
f = @(x,k)x * k ;
partfor i = 1 : n
a = f(a,i)
end
2、在同一parfor中,对简约变量的操作必须一致。
%错误用法
parfor i = 1:n
if A>5*k
A = A +i; %执行A+i的替换
else
A = [A,4+i]; %执行A的增长,与前面A = A +i;操作不一致
end %if语句结束
end
%正确用法
parfor i = 1:n
if A>5*k
A = A +i;
else
A = a +i+5*k;
end %if语句结束
end
3、如果简约变量的操作是‘*’或者‘[]’,x在操作符前面或者后面都可以,但是位置必须恒定。
%错误用法
parfor i = 1:n
if A > 5*k
A = [A ,4+i];
else
A =[r(i),A]; %这是错误点
end
%正确用法
parfor i = 1:n
if A > 5*k
A = [A ,4+i];
else
A =[A,r(i)];
end
4、简约变量赋值应满足结合律和交换律
结合律:f(a,f(b,c))=f(f(a,b),c)
交换律:f(a, b)=f(b,a)
在Matlab中,*、[ ]、{ }均不满足交换律,但是Matlab对于这些运算符做了特殊处理,可以保证这些操作执行结果的正确性。
五、程序示例
结合律
加法
function [ ]=partry21(N)
data=peaks; %peaks函数生成一些数据
data=data(:);
if N>length(data(:))
N=length(data(:));
end %容错,若输入参数过大,则赋给N
out=0; %输出,初始值为0
for kk=1:N-1
out=myadd(out,data(kk)); %利用串行循环对数据做加法运算
end
display(strcat('for,myadd,最终结果:',num2str(out))); %显示输出结果
out=0;
mypool=parpool;
parfor kk=1:N-1
out=myadd(out,data(kk));
end
delete(mypool)
display(strcat('parfor,myadd,最终结果:',num2str(out)));
myadd.m
function [out]=myadd(a,b)
out = a+b;
可以看出加法符合结合律
减法
function [ ]=partry21(N)
data=peaks;
data=data(:);
if N>length(data(:))
N=length(data(:));
end
out=0;
for kk=1:N-1
out=mysub(out,data(kk));
end
display(strcat('for,mysub,最终结果:',num2str(out)));
out=0;
mypool=parpool;
parfor kk=1:N-1
out=mysub(out,data(kk));
end
delete(mypool)
display(strcat('parfor,mysub,最终结果:',num2str(out)));
mysub.m
function [out]=mysub(a,b)
out = a-b;
可以看出减法不符合结合律。
交换律
function [ ]=partry22(N)
data=[rand(N,1) (1:N).']; %生成一个随机向量,第二列的列向量为1到N
maxv=[-1 0]; %取一个初始向量
mypool=parpool;
parfor ii=1:N
maxv=mymax(maxv,data(ii,:)); %两个向量相比较,取最大
end
disp(maxv);
delete(mypool)
maxv=[-1 0];
for ii=1:N
maxv=mymax(maxv,data(ii,:));
end
disp(maxv);
mymax.m
function [ out ] = mymax(in1,in2)
if in1(1)>in2(1)
out = in1;
else
out = in2;
end
可以看出满足交换律。