MATLAB——多核并行计算初探

本文主要以并行语句parfor为例进行探索。

1. 适用条件

(1)每次循环之间是相互独立的;

(2)循环执行完之后的结果和循环执行的先后次序无关;

(3)不适用于频繁读写内存的算法。

2. 设置并行环境

%% 设置并行计算环境
poolobj = gcp('nocreate');
if isempty(poolobj)
    poolsize = 0;
    CoreNum = 4;                    % 设置CPU核心数量
    parpool('local', CoreNum);
else
    poolsize = poolobj.NumWorkers;
    disp('Already initialized');    % 并行环境已启动
end

%% 关闭并行计算环境
delete(gcp('nocreate'));

 如果长时间不运行并行计算,计算机会在30min后关闭并行环境。

3. 简单实例

原始程序:

clc;clear all;
tic
for i = 1:10000
    for j = 1:10000
        a(i,j) = i*j;        
    end
    disp(i)
end
toc

改进程序:

clc;clear all;
tic
% a = zeros(10000,10000);  % 如果预先分配内存,运行速度将会大大提升
core_number = 4;           % 想要调用的处理器个数
parpool('local',core_number);
parfor i = 1:10000
    for j = 1:10000
        a(i,j) = i*j;        
    end
    disp(i)
end
toc
delete(gcp('nocreate'));

在我的笔记本上运行原始程序大约需要390s;运行改进程序大约需要23s。

4. 注意事项

4.1 parfor-Loop中的变量应该为连续增加的整数

以下是三种反例:

parfor ii = 0:0.2:1    % 不是整数
parfor jj = 1:2:11     % 不是连续整数
parfor kk = 10:-1:1    % 不是递增整数

如果需要引用上述变量,可以参考如下方式进行修改:

ii = 0:0.2:1;
parfor id_ii = 1:numel(ii)
    iii = ii(id_ii);
    ...
end

4.2 parfor-Loop中的变量分类问题

parfor-Loop中的变量包括如下5个分类:

分类 描述

循环变量

parfor的循环索引
切片变量

需要读取或写入的parfor之外的变量,读取或写入位置与循环变量相关,且位置必须是固定的、不重合的,每次循环中只能读取由同一个索引值索引的切片。

如果 x[i] 和 x[i+1] 同时出现,则x不被识别为切片变量 。

如果切片变量x是输出变量(即在循环内被赋值),访问还必须是连续的(此时只能是Loop变量再加固定的平移量)。

切片变量不能在循环内动态变换大小。

广播变量 外部变量,在循环内部未被重新赋值,只需读取即可
简约变量 可在多次循环内对一个变量操作,该变量与迭代顺序无关
临时变量 在循环内部创建,循环结束后会清除调,但不在循环外部访问
a = 0;
c = pi;
z = 0;
r = rand(1,10)
parfor k = 1 : 100    % k为循环变量
    a = k;            % a为临时变量
    z = z + k;        % z为简约变量
    b(k) = r(k);      % r为切片变量(输入),b为切片变量(输出)
    if k <= c         % c为广播变量
       d = 2 * a;     % d为临时变量
    end
end

 切片变量举例

parfor i = 1:n
    x(i) = a(2*i);            % allowed
    y(i+2) = a(i) + b(i+1);   % allowed
    c(i+1) = c(i) + 1;        % not allowed,存在迭代
    z(2*i) = i;               % not allowed,访问不是连续的,不是加固定平移量
    a(i) = [];                % not allowed,变换了大小
    a(end+1) = 1;             % not allowed,变换了大小
end

(1)在parfor-Loop中,如果使用嵌套的for循环来索引切片数组,则不能在parfor循环的其他地方再次使用该数组。举例如下:

%% 无效代码,因为数组A是在嵌套的for循环中被切片和索引的
A = zeros(4,10);
parfor i = 1:4
    for j = 1:10
        A(i,j) = i + j;
    end
    disp(A(i,1))
end

%% 有效代码,v被分配到了嵌套循环的外部
A = zeros(4,10);
parfor i = 1:4
    v = zeros(1,10);
    for j = 1:10
        v(j) = i + j;
    end
    disp(v(1))
    A(i,:) = v;
end

(2)在parfor-Loop中,不能对变量进行分类赋值,举例如下:

%% 无效代码,因为变量x的不同部位有多个赋值,parfor不支持
parfor idx = 1:10
  x(1) = 7;
  x(2) = 8;
  out(idx) = sum(x);
end

%% 有效代码,parfor明确地识别变量x为一个临时变量
parfor idx = 1:10
  x = [7, 8];
  out(idx) = sum(x);
end

(3)通常不能使用结构体用作parfor中的切片输入或输出变量。

%% 无效代码,因为结构体a不能被分类
a.x = [];
parfor idx = 1:10
  a.x(idx) = 7;
end

%% 有效代码,将一个单独的变量tmpx赋值给结构体a.x
tmpx = [];
parfor idx = 1:10
  tmpx(idx) = 7;
end
a.x = tmpx;

4.3 parfor-Loop中的结构体问题

(1)创建临时结构体

在parfor-Loop中,不能使用点号创建结构体新内容,易产生分类错误,可使用struct函数创建结构体。

%% 无效代码,结构体temp形成了一个分类错误
parfor i = 1:4
   temp.myfield1 = rand();
   temp.myfield2 = i;
end 

%% 有效代码,将一个单独的变量tmpx赋值给结构体a.x
parfor i = 1:4
    temp = struct();
    temp.myfield1 = rand();
    temp.myfield2 = i;
end

%% 有效代码
parfor i = 1:4
    temp = struct('myfield1',rand(),'myfield2',i);
end

(2)切片结构体

在parfor-Loop中,不能使用循环变量来索引结构体中的元素。

%% 无效代码,因为使用循环变量索引结构体而形成了分类错误
parfor i = 1:4
    outputData.outArray1(i) = 1/i;
    outputData.outArray2(i) = i^2;
end 

%% 有效代码,在循环中使用了单独的切片数组避免了分类错误
parfor i = 1:4
    outArray1(i) = 1/i;
    outArray2(i) = i^2;
end
outputData = struct('outArray1',outArray1,'outArray2',outArray2); 

 

参考:

【1】 MATLAB官方帮助文档。

 

你可能感兴趣的:(MATLAB——多核并行计算初探)