2015-04-17 2 views
8

Я пишу код MatLab для выполнения 3 мерного интеграла: сПочему пакетный режим намного быстрее, чем parfor?

function [ fint ] = int3d_ser(R0, Rf, N) 
Nr = N; 
Nt = round(pi*N); 
Np = round(2*pi*N); 

rs = linspace(R0, Rf, Nr); 
ts = linspace(0, pi, Nt); 
ps = linspace(0, 2*pi, Np); 

dr = rs(2)-rs(1); 
dt = ts(2)-ts(1); 
dp = ps(2)-ps(1); 

C = 1/((4/3)*pi); 
fint = 0.0; 
for ir = 2:Nr 
    r = rs(ir); 
    r2dr = r*r*dr; 
    for it = 1:Nt-1 
    t = ts(it); 
    sintdt = sin(t)*dt; 
    for ip = 1:Np-1 
     p = ps(ip); 
     fint = fint + C*r2dr*sintdt*dp; 
    end 
    end 
end 

end 

для ассоциированного int3d_par (parfor) версии, открываю бассейн MatLab и просто заменить for с parfor. Я получаю довольно приличное ускорение, и я запускаю его на большее количество ядер (мои тесты от 2 до 8 ядер).

Однако, когда я запускаю же интеграцию в пакетном режиме с:

function [fint] = int3d_batch_cluster(R0, Rf, N, cluster, ncores) 

%%% note: This will not give back the same value as the serial or parpool version. 
%%%  If this was a legit integration, I would worry more about even dispersion 
%%%  of integration nodes per core, but I just want to benchmark right now so ... meh 

Nr = N; 
Nt = round(pi*N); 
Np = round(2*pi*N); 

rs = linspace(R0, Rf, Nr); 
ts = linspace(0, pi, Nt); 
ps = linspace(0, 2*pi, Np); 

dr = rs(2)-rs(1); 
dt = ts(2)-ts(1); 
dp = ps(2)-ps(1); 

C = 1/((4/3)*pi); 

rns = floor(Nr/ncores)*ones(ncores,1); 
RNS = zeros(ncores,1); 
for icore = 1:ncores 
    if(sum(rns) ~= Nr) 
    rns(icore) = rns(icore)+1; 
    end 
end 
RNS(1) = rns(1); 
for icore = 2:ncores 
    RNS(icore) = RNS(icore-1)+rns(icore); 
end 

rfs = rs(RNS); 
r0s = zeros(ncores,1); 
r0s(2:end) = rfs(1:end-1); 

j = createJob(cluster); 

for icore = 1:ncores 
    r0 = r0s(icore); 
    rf = rfs(icore); 
    rn = rns(icore); 
    trs = linspace(r0, rf, rn); 
    t{icore} = createTask(j, @int3d_ser, 1, {r0, rf, rn}); 
end 

submit(j); 
wait(j); 
fints = fetchOutputs(j); 

fint = 0.0; 
for ifint = 1:length(fints) 
    fint = fint + fints{ifint}; 
end 

end 

Я заметил, что это гораздо, гораздо быстрее. Зачем делать эту интеграцию в пакетном режиме, чем делать это в parfor?

Для справки, я тестирую код с N от небольших чисел, таких как 10 и 20 (для получения константы в полиномиальной аппроксимации времени выполнения) для больших чисел, таких как 1000 и 2000. Этот алгоритм будет масштабироваться по шкале, поскольку я назначаю число узлов интеграции в направлениях theta и phi должно быть постоянным кратным заданного N.

Для узлов в 2000 году версия parfor занимает около 630 секунд, тогда как то же количество узлов в пакетном режиме занимает около 19 секунд (где около 12 секунд - это просто служебная связь, которую мы также получаем для 10 узлов интеграции).

+2

Какой тип '' вы заменяете 'parfor'? Это может повлиять на эту структуру вложенных циклов (например, нанести удар по «parfor» и нанести несколько раз сбои в настройке/разрыве параллелизма или сделать структуру среза менее, чем идеальный). Код версии партии, по-видимому, «сгладил» структуру вложенных циклов к моменту ее получения к параллельным вызовам (т. Е. Путем предварительного расчета входных блоков и выполнения вложенных циклов внутри каждого блока), что может объяснять накладные расходы с меньшим параллелизмом. –

+1

Другое дело, что если я понимаю, где вы помещаете 'parfor', пакетная версия перемещает намного меньше данных вокруг: она просто передает параметры' r0', 'rf' и' rn' и промежуточные переменные строятся локально для каждого рабочего, но 'parfor' внутри' int3d_ser' приведет к тому, что подмножества временных переменных, созданных на master, будут упорядочены и отправлены каждому работнику. –

+0

.что произойдет, если вы возьмете функцию 'int3d_batch_cluster' и замените вызовы' createTask' символом 'parfor icore = 1: ncores' вокруг обычного вызова функции' int3d_ser'? Это скажет вам, является ли это механизмом 'parfor' как таковым или как ваш код неявно структурирует партии работ, которые должны быть отправлены рабочим. –

ответ

1

После выступления с поддержкой Mathworks у меня возникло фундаментальное непонимание того, как работает parfor. У меня создалось впечатление, что parfor действовал как openMP, тогда как пакетный режим действовал как mpi с точки зрения общей и распределенной памяти.

Оказывается, что parfor фактически использует распределенную память. Когда я создаю, скажем, 4 пакетные функции, накладные расходы на создание нового процесса происходят 4 раза. Я думал, что использование parfor приведет к тому, что накладные расходы произойдут всего один раз и что parfor будет проходить в том же пространстве памяти. Это не тот случай.

В моем примере кода выясняется, что для каждой итерации parfor я на самом деле налагаю накладные расходы на создание нового потока. При сравнении «яблок с яблоками», я должен действительно создавать одинаковое количество пакетных вызовов, поскольку я итерации в цикле parfor. Вот почему функция parfor занимала гораздо больше времени - я принимал много больше накладных расходов для многопроцессорной обработки.

+0

Означает ли это, что пакетный режим быстрее, чем parfor? –