MATLAB 并行运算测试效果评估
1 MATLAB 并行计算机制
MATLAB 提供了并行计算工具箱(Parallel Computing Toolbox),可以将一个 MATLAB 会话
(session,即 client)的计算工作分配到其他的 MATLAB 会话(也称为 worker)。在没有使
用并行计算工具箱的情况下,MATLAB 的计算工作是由 client 执行的;当使用了并行计算
工具箱后,MATLAB 能够创建多个 worker(MATLAB 确定了最大上限是 8 个),由 client
将计算工作分配到 worker 并行执行。
并行计算工具箱有三种典型的使用场景:
1、Parallel for-Loops (parfor):将 for 循环中的执行语句分配到不同的 worker 并行执行。要
保证执行结果正确,需要保证 for 循环中的代码是能够彼此互不依赖。
Parfor 实现的是代码级别的并行处理机制。
2、Batch Jobs:即批任务执行机制,一个 worker 负责一个 job。一个任务通常是一个 M 文
件或者一个函数。Batch Jobs 实现的是任务(函数、文件为载体)级别的并行处理机制。
3、Large Data Sets:当处理的数据量太大超出内存负荷后,可以将此大数据分配到不同的
worker,每一个 worker 只包含其中一部分数据,但是用户仍然可以将此大数据作为一个整
体来操作。
本文评估了 parfor 工作效率。Parfor 工作原理如下图所示,MATLAB client 启动了 3 个 worker
来并行执行 parfor 中的代码,一个 CPU 核对应一个 worker。本测试所用 PC 的 CPU 只有两
个核,因此最多只能启动两个 worker。
除了并行计算工具箱(Parallel Computing Toolbox),matlab 还支持分布式计算引擎(MATLAB
Distributed Computing Engine(Server),简称 MDCE),可以扩展应用程序,将其应用到一个
集群上的多台计算机,如下图所示。
本文基于 MDCE 在一个 PC 上创建 job 进行计算,一个 job 最多启动了 10 个 worker 进行并
行计算。
2 parfor 测试
2.1 测试环境
PC 机硬件配置:
CPU:intel 双核 E4600 2.4GHz;
内存:2GB 物理内存。
PC 机操作系统:WINXP SP3。
MATLAB 版本:7.9.0(R2009b)。
测试方法:执行 fft 计算,分别执行 for 循环的语句以及 parfor 循环的语句进行对比,for
循环和 parfor 循环的代码完全一致,唯一区别的是 for 不是并行计算,而 parfor 启动了
2 个 worker 进行并行计算。
测试代码:
%%% 打开并行计算的工作资源池,1 个资源池对应本地 CPU 的一个核。本机有两个核,
取值为 2。
testExamples = 5;
loopTimes = 1e5 ;
testStep = loopTimes;
tElapsed = [1:1:testExamples];
tElapsedPal = [1:1:testExamples];
Fs = 1000;
T = 1/Fs;
L = 1000;
t = (0:L-1)*T;
% Time vector
% Sum of a 50 Hz sinusoid and a 120 Hz sinusoid
x = 0.7*sin(2*pi*50*t) + sin(2*pi*120*t);
% Sampling frequency
% Sample time
% Length of signal
%% 非并行计算测试
isOpen = matlabpool('size');
if isOpen > 0
matlabpool close;
end
for j=1:testExamples
timeStart = tic;
for i=1:loopTimes
% Sinusoids plus noise
y = x + 2*randn(size(t));
NFFT = 2^nextpow2(L); % Next power of 2 from length of y
Y = fft(y,NFFT)/L;
f = Fs/2*linspace(0,1,NFFT/2+1);
end
tElapsed(j) = toc(timeStart);
%% 增加循环次数
loopTimes = loopTimes + testStep;
end
%% 并行计算测试
isOpen = matlabpool('size');
if isOpen <= 0
matlabpool('open', 'local', 2);
else
matlabpool close;
matlabpool('open', 'local', 2);
end
loopTimes = 1e4;
for j=1:testExamples
timeStart = tic;
parfor i=1:loopTimes
% Sinusoids plus noise
y = x + 2*randn(size(t));
NFFT = 2^nextpow2(L); % Next power of 2 from length of y
Y = fft(y,NFFT)/L;
f = Fs/2*linspace(0,1,NFFT/2+1);
end
tElapsedPal(j) = toc(timeStart);
%% 增加循环次数
loopTimes = loopTimes + testStep;
end
%%plot([1:1:testExamples], tElapsed);
plot([1:1:testExamples], tElapsed, [1:1:testExamples], tElapsedPal);
title('FFT 循环的时间开销(单位:秒)');
xlabel(strcat('FFT 循环次数','(','单位:',num2str(testStep),'次)'));
ylabel('FFT 时间开销(单位:秒)');
2.2 测试结论
通过 parfor 的并行计算方式,可以充分利用 CPU 的多核处理能力,并行处理效率随 CPU 核
数量增加而增大。在本测试环境中,执行相同计算量的情况下,两个核并行处理效率约为 1
个核的 1.7~1.9 倍。
在多核并行处理情况下,两个核的负载基本均衡,说明 matlab 能够较为平均的调度 CPU 多
核资源。
2.3 测试数据
1)串行计算结果(10*n 万次 fft 运算,n=[1,2,3,4,5])
55
50
45
40
35
30
25
20
15
)
(
秒
:
位
单
销
开
间
时
T
F
F
10
1
1.5
FFT循 环 的 时 间 开 销 (单 位 : 秒 )
2.5
2
FFT循 环 次 数 (单 位 : 100000次 )
3
3.5
4
4.5
5
串行计算时 matlab 的进程只有一个:
2)并行计算结果(10*n 万次 fft 运算,n=[1,2,3,4,5]),2 个 worker
30
25
20
15
10
5
)
(
秒
:
位
单
销
开
间
时
T
F
F
0
1
1.5
FFT循 环 的 时 间 开 销 (单 位 : 秒 )
2.5
2
FFT循 环 次 数 (单 位 : 100000次 )
3
3.5
4
4.5
5
Matlab 启动了两个 worker,两个 worker 的 CPU 占有率一直保持 50%左右。
3)并行计算结果和串行计算(2 个 worker)结果对比图(10*n 万次 fft 运算,n=[1,2,3,4,5])
蓝色线为非并行计算结果,绿色线为并行计算结果。
60
50
40
30
20
10
)
(
秒
:
位
单
销
开
间
时
T
F
F
0
1
1.5
FFT循 环 的 时 间 开 销 (单 位 : 秒 )
2.5
2
FFT循 环 次 数 (单 位 : 100000次 )
3
3.5
4
4.5
5
4)并行计算结果和串行计算(2 个 worker)结果对比图(100*n 万次 fft 运算,n=[1,2,3,4,5,
6,7,8,9,10]),蓝色线为非并行计算结果,绿色线为并行计算结果。
1200
1000
800
600
400
200
)
(
秒
:
位
单
销
开
间
时
T
F
F
0
1
2
3
FFT循 环 的 时 间 开 销 (单 位 : 秒 )
4
5
8
9
10
FFT循 环 次 数 (单 位 : 1000000次 )
6
7
3 MDCE 测试
3.1 测试环境
PC 机硬件配置:
CPU:intel 双核 E4600 2.4GHz;
内存:2GB 物理内存。
PC 机操作系统:WINXP SP3。
MATLAB 版本:7.9.0(R2009b)。
测试方法:执行 fft 计算,分别执行串行语句以及多任务调度机制的语句进行对比。
测试代码:
testExamples = 10;
LPTIMES = 1e4;
TASKNUM = 1 ;
loopTimes = LPTIMES;
testStep = loopTimes;
tElapsed = [1:1:testExamples];
tElapsedPal = [1:1:testExamples];
start = 1;
%% 非并行计算测试
for j=1:testExamples
timeStart = tic;
ret = fcFFT(start, start + loopTimes - 1);
tElapsed(j) = toc(timeStart);
%% 增加循环次数
loopTimes = loopTimes + testStep;
end
%% 并行计算测试
loopTimes = LPTIMES;
jm1 = findResource('scheduler', 'type', 'jobmanager', 'name', 'myjm');
for j=1:testExamples
timeStart = tic;
job1 = createJob(jm1); %% 15ms
set(job1, 'FileDependencies', {'fcFFT.m'}); %% 15ms
%% 创建 TASKNUM 个并行任务
for k=1:TASKNUM
createTask(job1,
@fcFFT,
1,
{start+loopTimes/TASKNUM*(k-1),
start+loopTimes/TASKNUM*k-1}); %% 8ms
end
submit(job1); %% 2ms
waitForState(job1, 'finished'); %% TASKNUM = [1:1:10]; TimeCost = [2.9, 1.9, 2.4, 2.7, 3.1,
3.4, 3.8, 4.2, 4.5, 4.9]s
results = getAllOutputArguments(job1); %% 和内存、cache 命中有关。TASKNUM=1
时,TimeCost=【5ms~20ms】;TASKNUM=5 时,TimeCost=【13ms~27ms】;TASKNUM=10
时,TimeCost=【20ms~100ms】
%% destroy(job1);
tElapsedPal(j) = toc(timeStart);
%% 增加循环次数
loopTimes = loopTimes + testStep;
end
%%plot([1:1:testExamples], tElapsed);
%%plot([1:1:testExamples], tElapsedPal);
plot([1:1:testExamples], tElapsed, [1:1:testExamples], tElapsedPal);
title('FFT 循环的时间开销(单位:秒)');
xlabel(strcat('FFT 循环次数','(','单位:',num2str(testStep),'次)'));
ylabel('FFT 时间开销(单位:秒)');
function retvalue = fcFFT(loopStart, loopEnd)
Fs = 1000;
T = 1/Fs;
L = 1000;
t = (0:L-1)*T;
% Sampling frequency
% Sample time
% Length of signal
% Time vector