В связи с развитием многопроцессорности и многоядерности параллельные вычисления превратились из экзотики в повседневность, однако многие вычислительные среды в частности MatLab не поддерживают многопоточность по умолчанию и требуют дополнительных инструкций и модулей.
В статье рассматривается возможность параллельных вычислений в среде MatLab и Simulink, с помощью пакета Parallel Computing Toolbox.
MatLab и Simulink предоставляет большое количество великолепных инструмент для математических вычислений. Однако, не всегда эти вычисления происходят быстро. Для ускорения вычислений в MatLab есть такой инструмент, как Parallel Computing Toolbox, который позволяет производить параллельные вычисления.
1. Параллельное вычисление в цикле for (parfor)
Разберем чем отличается parfor от for. Если в том, как работает цикл for проблем не возникает, то с parfor есть вопросы. К примеру, действия, в отличии от стандартного цикла for, в parfor итерации цикла могут производиться не последовательно. Например, может быть ситуация, где тело цикла с индексом i = 205 может идти раньше, чем i = 160. Это возникает из-за разности производительности вычислительных потоков. Из-за этого же, так как задачи выполняются в разных пулах, невозможно воспользоваться значениями и результатами вычисления из предыдущих итераций.
Пример параллельных вычислений в цикле for рассмотрим на примере следующего расчета, который в однопоточном режиме производится достаточно долго.
n = 15; tic; for i = 1:500 a = fibonacci(n); end time = toc
На тестовой машине вычисление 15 число Фибоначчи 500 раз и работает занимает 7.67 секунд.
Для использования параллельных вычислений необходимо заменить for на parfor, а также запустить второй (или по числу желаемых потоков) pool для вычислений.
n = 15; tic; parfor i = 1:500 a = fibonacci(n); end time = toc
При первом запуске он включится автоматически, но это займет время. В случае если второй pool для вычислений уже запущен, то время на вычисление данной функции сокращается до 5.06 секунд. Ускорение расчета составляет примерно 35%, что значительно меньше ожидаемого результата при удвоении вычислительных ресурсов.
Рассмотрим другой процесс вычислений, который является более долгим:
tic; for i = 1:10 a = rand(3000, 3000); a = tf(a); end time = toc
и занимает 56.3 секунды в однопоточном режиме.
Заменим for на parfor с использованием двух вычислительных потоков
tic; parfor i = 1:10 a = rand(3000, 3000); a = tf(a); end time = toc
Время выполнения сокращается до 34 секунд, что соответствует повышению производительности на 65%.
Стоит отметить то, что использование многопоточных вычислений по числу доступных ядер может оказать взаимное негативное влияние на производительность других приложений и рекомендуется использовать для таких вычислений MatLab в монопольном режиме.
2. Параллельное вычисление Simulink
С другой стороны нередко возникает необходимость множественного моделирования одной системы с разными параметрами или разными начальными условиями.
Обычно для вычисления и получения данных из модели Simulink используется команда sim. Аналогично, для параллельного вычисления можно использовать команду parsim и специальный параметр SimulationInput, передаваемый в эту функцию. Которая запускает Simulink модель с определенными в этом объекте параметрами.
Рассмотрим на примере следующей модели:
Для того чтобы задать параметры мы необходимо определиться с количеством экспериментов:
Cf_sweep = Cf*(0.05:0.1:0.95); numSims = length(Cf_sweep);
Затем определить наши входные параметры
for i = numSims:-1:1 in(i) = Simulink.SimulationInput(mdl); in(i) = setBlockParameter(in(i), ['TestModel' '/Road-Suspension Interaction'], 'Cf', num2str(Cf_sweep(i))); end
Для запуска вычислений модели выполним следующую команду:
out = parsim(in, 'ShowProgress', 'on');
В поле вывода MatLab можно будет увидеть следующую информацию:
Основная суть которой в том, что у нас вычисления совершались параллельно и завершились.
Дальнейшая обработка данных происходит посредством обработки массива out, где каждое значения соответствует моделированию с параметрами в in с тем же самым индексом.
for i = numSims:-1:1 simOut = out(i); ts = simOut.logsout.get('vertical_disp').Values; plot(ts); end
Рассмотрим более конкретный пример параллельных вычислений — построение фазового портрета. Действительно, это как правило достаточно длительный процесс, иногда, требующий сотен и тысяч запусков модели.
В качестве рассматриваемой модели возьмем следующую:
Изначально скрипт построения фазовых портретов выглядел следующим образом:
hold on for y0=-5:1:5 for x0=-5:1:5 sim('fazPort') plot(x, y) end end hold off
Для возможности параллельного моделирования необходимо изменить модель: необходимо включить логирование данные, для дальнейшей обработки. Для этого заменяются блоки to workspace на блок out и ставится параметр log this signal для интересующих нас сигналов.
Кроме того скрипт запуска моделирования требует изменений:
% Генерируем массив начальных условий для системы [p,q] = meshgrid(-2:2, -5:5); pairs = [p(:) q(:)]; % Генерируем массив параметров параллельного моделирования for i = 55:-1:1 in(i) = Simulink.SimulationInput('fazPort'); in(i) = in(i).setVariable('y0',pairs(i,1)); in(i) = in(i).setVariable('x0',pairs(i,2)); end % Запускаем параллельное моделирование out = parsim(in, 'ShowSimulationManager', 'on') % Производим построения фазового портрета figure(2) hold on for i = 1:55 simOut = out(i); x = simOut.logsout.get(1).Values.Data; y = simOut.logsout.get(2).Values.Data; plot(x, y) end
На выходе получаем точно такой же фазовый портрет, но вычисленный в 2 потока. Так же, дополнительное окно SimulationManager показывает прогресс вычислений. В нем можно увидеть входные и выходные данные для каждого запуска.