在Matlab下使用parfor实现多核并行计算

导师这几天要回国进行工作检查了,可是博主的实验还没有做完,而且每一个实验都特别地耗时(一个张量分解需要是5个小时),可是CPU和内存的利用率没有达到100%,听师兄说matlab里面内置了parfor可以做一些并行的运算,可能速度会快一些于是就用了一下。

parfor的简介

parfor就是paralle+for,也就是并行的for循环,它的大致意思是会给你自动构造几个matlab的执行进程,并行地处理你的数据。这里的数目最大的数值是你的CPU的核数,比如说楼主的电脑是四核的,在任务管理器里面就看到了4个matlab的进程。

Matlab进程

当你需要简单计算的多次循环迭代时,例如针对不同的参数对实验结果的影响等,parfor循环就很有用。parfor将循环迭代分组,那么每个worker执行迭代的一部分。当迭代耗时很长的时候parfor循环也是有用的,因为workers可以同时执行迭代,但是当你的CPU的利用率如果已经达到了100%,此时你这种并行是没有意义的,速度不会加快的。另外这个博客上指出了Matlabparfor的使用条件即:

  1. 大量的简单计算的循环。
  2. 大量或少量的复杂计算的循环
  3. 各个任务之间不会出现数据的依赖,比如说循环内部的变量之间不要存在数据传递等等。

parfor的使用

假如说函数$f$是一个非常耗时的函数,然后你想把矩阵$A$中的每一个元素传递到函数$f$中进行运算,运算结果保存在矩阵$B$里面,那么你可以这样操作。

	parfor i = 1:length(A)
	   B(i) = f(A(i));
	end

这样矩阵A各个元素的计算就可以并行操作而且可以节省很多时间。

在parfor里面保存数据文件

Matlab默认是不允许在parfor里面使用save函数的,这个是因为

Transparency is violated by the SAVE command because in general MATLAB cannot determine which variables from

the workspace will be saved to a file.

也就是说Matlab不知道要把工作区里面的那个数据变量保存到内存中,但是在有些时候我们想保存一些中间的结果,那这个就很难办了。一种解决方案是我们把保存文件的操作放在另外一个函数里面进行操作,然后在当前的parfor循环体里面调用这个函数即parsave,另外一种方法是我们不适用save命令自己实现保存操作。

使用parsave保存数据

前面我们讲到了我们把把保存文件的操作放在另外一个函数里面进行操作,然后在当前的parfor循环体里面调用这个函数,我们把这个函数起名为即parsave,它的具体代码如下:

	function parfor_save(varargin)
	    fname=varargin{1};    
	    for i=2:nargin
	       eval([inputname(i),'=varargin{i};']);  
	       if i==2
	            save('-mat',fname,inputname(i));
	       else
	           save('-mat',fname,inputname(i),'-append');
	       end        
	end

我们在parfor里面采用下面的方式进行调用:

	parfor ii = 1:4
	x = rand(10,10);
	y = ones(1,3);
	parsave(sprintf('output%d.mat', ii), x, y);
	end

但是在新版的matlab比如说matlab 2015b里面如果直接使用会抛出这个错误

Error using parsave (line 27)

Transparency violation error.

See Parallel Computing Toolbox documentation about Transparency

同样是Workspace Transparency的的错误,这个是因为在新版的matlab里面对Workspace Transparency的检查更加严格了,如果我们想保存数据可以自己实现save函数操作。

自己实现save函数进行数据保存

这个意思就是指我们自己调用matfile 函数实现save的操作,在matlab里面敲help matfile我们可以得到下面的doc

matfile Save and load parts of variables in MAT-files.

MATOBJ = matfile(FILENAME) constructs an object that can load or save

parts of variables in MAT-file FILENAME. MATLAB does not load any data

from the file into memory when creating the object. FILENAME can

include a full or partial path, otherwise matfile searches along the

MATLAB path. If the file does not exist, matfile creates the file on

the first assignment to a variable.

MATOBJ = matfile(FILENAME,’Writable’,ISWRITABLE) enables or disables

write access to the file. ISWRITABLE is logical TRUE (1) or FALSE (0).

By default, matfile opens existing files with read-only access, but

creates new MAT-files with write access.

Access variables in MAT-file FILENAME as properties of MATOBJ, with dot

notation similar to accessing fields of structs. The syntax for loading

part of variable VARNAME into variable SMALLERVAR is

SMALLERVAR = MATOBJ.VARNAME(INDICES)

Similarly, the syntax for saving NEWDATA into variable VARNAME is

MATOBJ.VARNAME(INDICES) = NEWDATA

Specify part of a variable by defining indices for every dimension.

Indices can be a single value, an equally spaced range of increasing

values, or a colon (:), such as:

MATOBJ.VARNAME(100:500, 200:600)

MATOBJ.VARNAME(:, 501:1000)

MATOBJ.VARNAME(1:2:1000, 80)

即我们可以用matfile命令去加载或者写一个mat文件,具体的变量使用方法和matlab的struct一样,我们使用matfileparfor里面保存文件的具体代码如下:

	parfor ii = 1:4
	m=matfile(sprintf('output%d.mat', ii),'writable',true)
	x = rand(10,10);
	y = ones(1,3);
	m.x=x;
	m.y=y;
	end

这样就可以实现在并行环境下的save操作了。但是这样就不会violate workspace Transparency了吗?个人感觉这个可能在以后的版本中同样会被封,先这样使用再说吧。

Matlab 2014版本下使用Parfor保存的变量为空

朋友最近反应说在Ubuntu平台下的Matlab 2014版本上运行parfor 保存的变量居然为空。其实这个是一个matlab的bug。在StackOverflow(https://stackoverflow.com/questions/19981111/matlab-variables-empty-after-parfor-loop)上有相关 的解决方案即:采用matlab -nodisplay -nodesktop -r "scriptname" 命令来运行matlab即可。