Я придумал несколько удовлетворительное решение, поэтому, если кто -то заинтересован, я подумал, что поделюсь им. Я все еще был бы признателен комментариями о том, как улучшить/точно настроить подход.
По сути, я решил, что единственным разумным способом является создание (очень) рудиментарной модели планировщика для параллельной петли:
function c=est_cost_para(cost_blocks,cost_it,num_cores)
% Estimate cost of parallel computation
% Inputs:
% cost_blocks: Estimate of cost per block in arbitrary units. For
% consistency with the other code this must be in the reverse order
% that the scheduler is fed, i.e. cost should be ascending!
% cost_it: Base cost of iteration (regardless of number of entries)
% in the same units as cost_blocks.
% num_cores: Number of cores
%
% Output:
% c: Estimated cost of parallel computation
num_blocks=numel(cost_blocks);
c=zeros(num_cores,1);
i=min(num_blocks,num_cores);
c(1:i)=cost_blocks(end-i+1:end)+cost_it;
while i<num_blocks
i=i+1;
[~,i_min]=min(c); % which core finished first; is fed with next block
c(i_min)=c(i_min)+cost_blocks(end-i+1)+cost_it;
end
c=max(c);
end
Параметр cost_it
Ибо пустая итерация - это грубое сочетание множества различных побочных эффектов, которые могут быть разделены: стоимость пустой итерации в for
/parfor
-loop (также может быть разным на блок), а также время запуска. передача данных parfor
-loop (и, вероятно, больше). Моя главная причина объединить все состоит в том, что я не хочу оценивать/определять более детальные затраты.
Я использую приведенную выше рутину, чтобы определить отсечение следующим образом:
% function i=cutoff_ser_para(cost_blocks,cost_it,num_cores)
% Determine cut-off between serial an parallel regime
% Inputs:
% cost_blocks: Estimate of cost per block in arbitrary units. For
% consistency with the other code this must be in the reverse order
% that the scheduler is fed, i.e. cost should be ascending!
% cost_it: Base cost of iteration (regardless of number of entries)
% in the same units as cost_blocks.
% num_cores: Number of cores
%
% Output:
% i: Number of blocks to be calculated serially
num_blocks=numel(cost_blocks);
cost=zeros(num_blocks+1,2);
for i=0:num_blocks
cost(i+1,1)=sum(cost_blocks(end-i+1:end))/num_cores + i*cost_it;
cost(i+1,2)=est_cost_para(cost_blocks(1:end-i),cost_it,num_cores);
end
[~,i]=min(sum(cost,2));
i=i-1;
end
В частности, я не раздуваю/не изменяет значение est_cost_para
что предполагает (кроме cost_it
) самое оптимистичное планирование. Я оставляю это, как и в основном потому, что не знаю, что будет работать лучше всего. Чтобы быть консервативным (то есть избегать кормления слишком больших блоков в параллельный цикл), можно, конечно, добавить некоторый процент в качестве буфера или даже использовать мощность> 1, чтобы надуть параллельные затраты.
Обратите внимание и это est_cost_para
вызывается с последовательно меньшим количеством блоков (хотя я использую имя переменной cost_blocks
Для обеих подпрограмм одна является подмножеством другого).
По сравнению с подходом в моем сложном вопросе я вижу два основных преимущества:
- Относительно сложная зависимость между данными (как количество блоков, так и их стоимость) и количество ядер гораздо лучше с помощью моделируемого планировщика, чем возможно с одной формулой.
- Рассчитая стоимость всех возможных комбинаций последовательного/параллельного распределения, а затем, получая минимум, нельзя быть «застрявшим» слишком рано, читая в данных с одной стороны (например, прыжок, который до сих пор большой по сравнению с данными, но мало по сравнению с общим).
Конечно, асимптотическая сложность выше, вызывая est_cost_para
с его петлей все время, но в моем случае (num_blocks<500
) это абсолютно незначительно.
Наконец, если приличное значение cost_it
не легко представляется, можно попытаться рассчитать его, измеряя фактическое время выполнения каждого блока, а также чисто параллельную его часть, а затем пытаясь соответствовать полученным данным для прогнозирования затрат и получить обновленную стоимость cost_it
Для следующего вызова рутины (с использованием разницы между общей стоимостью и параллельными затратами или вставленной стоимостью нуля в установленную формулу). Надеемся, что это должно «сходиться» к наиболее полезному значению cost_it
для рассматриваемой проблемы.