Отправка данных работникам

Я пытаюсь создать часть параллельного кода, чтобы ускорить обработку очень большого (пара сотен миллионов строк) массива. Чтобы распараллелить это, я нарезал свои данные на 8 (мое количество ядер) кусков и попробовал отправить каждому воркеру по 1 кусочку. Однако, глядя на использование моей оперативной памяти, кажется, что каждая часть отправляется каждому рабочему, эффективно умножая мое использование оперативной памяти на 8. Минимальный рабочий пример:

A = 1:16;
for ii = 1:8
    data{ii} = A(2*ii-1:2*ii);
end

Теперь, когда я отправляю эти данные рабочим, используя parfor, кажется, что я отправляю полную ячейку, а не только желаемую часть:

output = cell(1,8);
parfor ii = 1:8
    output{ii} = data{ii};
end

На самом деле я использую некоторую функцию в цикле parfor, но это иллюстрирует случай. Действительно ли MATLAB отправляет полную ячейку data каждому воркеру, и если да, то как заставить его отправлять только нужный кусок?


person Adriaan    schedule 19.08.2015    source источник
comment
Если ваши данные представляют собой переменную-срез, они будут срезаны, и только эти срезы быть переданы работникам; Вы используете нарезанные переменные в своем реальном коде?   -  person m.s.    schedule 19.08.2015
comment
Я использую массив ячеек в своем фактическом коде, как показано здесь. Я посмотрю на функцию срезанной переменной, спасибо.   -  person Adriaan    schedule 19.08.2015
comment
Возможно, делать нарезку вручную, отправляя отдельные задания для каждой части: de.mathworks.com/help/ distcomp/submit.html   -  person Daniel    schedule 19.08.2015
comment
Примечание: gather после цикла parfor здесь является избыточным — gather используется для преобразования массива distributed в обычный массив MATLAB.   -  person Edric    schedule 20.08.2015
comment
Нарезка ваших переменных определенно может быть подходящим способом. Мы не можем вам сильно помочь, если вы скажете, правильно вы нарезаете или нет, не видя вашего кода.   -  person Ikaros    schedule 01.09.2015
comment
@HamtaroWarrior Я делю это именно так, как представил. У меня есть массив Mx7 с M порядка сотен миллионов, и я разбиваю его на восемь частей (мое количество ядер), а затем сохраняю каждую часть в ячейке, как показано.   -  person Adriaan    schedule 01.09.2015


Ответы (3)


По своему личному опыту я обнаружил, что использование parfeval лучше с точки зрения использования памяти, чем parfor. Кроме того, ваша проблема кажется более хрупкой, поэтому вы можете использовать parfeval для отправки более мелких заданий рабочим MATLAB.

Допустим, у вас есть workerCnt воркера MATLAB, которым вы собираетесь обрабатывать jobCnt заданий. Пусть data будет массивом ячеек размером jobCnt x 1, и каждый из его элементов соответствует вводу данных для функции getOutput, которая выполняет анализ данных. Затем результаты сохраняются в массиве ячеек output размера jobCnt x 1.

в следующем коде задания назначаются в первом цикле for, а результаты извлекаются во втором цикле while. Логическая переменная doneJobs указывает, какое задание выполнено.

poolObj = parpool(workerCnt);
jobCnt = length(data); % number of jobs
output = cell(jobCnt,1);
for jobNo = 1:jobCnt
    future(jobNo) = parfeval(poolObj,@getOutput,...
        nargout('getOutput'),data{jobNo});
end
doneJobs = false(jobCnt,1);
while ~all(doneJobs)
    [idx,result] = fetchnext(future);
    output{idx} = result;
    doneJobs(idx) = true;
end

Кроме того, вы можете сделать еще один шаг вперед, если хотите сэкономить больше памяти. Что вы можете сделать, так это то, что после получения результатов выполненной работы вы можете удалить соответствующий член future. Причина в том, что этот объект хранит все входные и выходные данные функции getOutput, которые, вероятно, будут огромными. Но вам нужно быть осторожным, так как удаление элементов future приводит к смещению индекса.

Ниже приведен код, который я написал для этого porpuse.

poolObj = parpool(workerCnt);
jobCnt = length(data); % number of jobs
output = cell(jobCnt,1);
for jobNo = 1:jobCnt
    future(jobNo) = parfeval(poolObj,@getOutput,...
        nargout('getOutput'),data{jobNo});
end
doneJobs = false(jobCnt,1);
while ~all(doneJobs)
    [idx,result] = fetchnext(future);
    furure(idx) = []; % remove the done future object
    oldIdx = 0;
    % find the index offset and correct index accordingly
    while oldIdx ~= idx
        doneJobsInIdxRange = sum(doneJobs((oldIdx + 1):idx));
        oldIdx = idx
        idx = idx + doneJobsInIdxRange;
    end
    output{idx} = result;
    doneJobs(idx) = true;
end
person milaniez    schedule 04.09.2015

Комментарий от @m.s верен - когда parfor нарезает массив, каждому рабочему потоку отправляется только тот фрагмент, который необходим для итераций цикла, над которым он работает. Тем не менее, вы вполне можете увидеть увеличение использования ОЗУ сверх того, что вы изначально ожидали, поскольку, к сожалению, требуются копии данных, когда они передаются от клиента к рабочим процессам через механизм связи parfor.

Если вам нужны данные только по воркерам, то лучшим решением будет создание/загрузка/доступ к ним только по воркерам, если это возможно. Похоже, вам нужен параллелизм данных, а не параллелизм задач, для которого spmd действительно лучше подходит (как предполагает @Kostas).

person Edric    schedule 20.08.2015

Я бы предложил использовать команду spmd MATLAB.

Вы можете писать код почти так же, как это было бы для непараллельной реализации, а также иметь доступ к текущему рабочему процессу с помощью системной переменной labindex.

Посмотрите здесь:

http://www.mathworks.com/help/distcomp/spmd.html

А также на этот ТАК вопрос о spmd против parfor:

SPMD против Parfor

person Xxxo    schedule 19.08.2015
comment
Хотя использование SPMD может помочь коду, оно скорее обходит вопрос, чем отвечает на него. Я хочу знать, как правильно отправлять мои данные рабочим без нелепых накладных расходов, чтобы я мог использовать их и в других частях кода. - person Adriaan; 19.08.2015
comment
Использование spmd или parfor не имеет значения с точки зрения использования оперативной памяти, но код, использующий spmd, занимает примерно в 1,2 раза больше времени, чем реализация parfor. - person Adriaan; 19.08.2015
comment
Если вы используете ту же логику, что и в приведенном примере, значит, у вас что-то не так в коде. Поскольку для того, чтобы увидеть реальную ошибку, потребуется почти отладка вашего кода, я предложил быструю альтернативу для решения вашей проблемы. Если вам конкретно требуется parfor, вы должны опубликовать весь код. - person Xxxo; 20.08.2015
comment
Я не получаю ошибку, я просто подозреваю, что данные отправляются всем рабочим и тем самым умножаются. Таким образом, вопрос заключается не в отладке моего кода, а в понимании работы того, как Matlab распределяет данные по рабочим. - person Adriaan; 20.08.2015