Этот пример иллюстрирует, как использовать преимущества выполнения многопоточного кода на многоядерном процессоре с использованием графического разбиения. Этот пример требует, чтобы Simulink ® Coder™ сгенерировал многопоточный код.
Одна из целей основанного на модельно-ориентированном проектировании состоит в том, чтобы создать реалистичные модели физических систем и симулировать эти модели в реальном времени, например, чтобы проверить контроллеры, используя оборудование в цикле (HIL). Однако, поскольку в модель объекта управления добавлено больше функций, вычислительные требования могут превысить ресурсы, доступные одноядерными системами обработки.
Разбиение объекта и контроллера на отдельные части является одним из способов удовлетворения вычислительных потребностей сложных моделей. С помощью Simulink ® можно разделить объект с помощью блоков Model, а затем назначить код, сгенерированный каждой подмоделью, потокам для выполнения в реальном времени в системе HIL, такой как Simulink Real-Time™. Чтобы увидеть, как это работает, давайте будем использовать наш хост-компьютер как stand-in для окружения выполнения в реальном времени и генерировать многопоточный код в реальном времени для следующей модели.
slexMulticoreSolverExample
Рисунок выше показывает, что код, сгенерированный для модели, разделен на два потока. В этом примере целевой объект принимается как многоядерный процессор Symmetric, так что потоки не связаны с каким-либо конкретным ядром. Операционная система отвечает за оптимальное использование ядер при планировании выполнения потоков. В идеале, чтобы обеспечить максимальную гибкость, количество потоков (Nt) должно быть больше, чем количество ядер (Nc). Дважды кликните кнопку 'Generate Code and Profile Report', чтобы сгенерировать многопоточный код, профилировать его выполнение и визуализировать результаты. Визуализация показывает карту заполнения ядра, как использовались ядра на каждом временном шаге выполнения. Можно видеть, что потоки плавают между ядрами, как это считается лучше всего планировщиком операционной системы. Такое планирование хорошо, когда операционной системе также нужно запустить другие процессы.
Simulink ® Coder™ генерирует код таким образом, чтобы два потока могли выполняться одновременно и, возможно, на двух разных ядрах. Это означает, что значения сигналов для и должны быть синхронизированы между двумя потоками. Simulink ® предлагает несколько опции для обработки этого требования, как показано здесь:
Используя скрипт ниже, мы моделируем и демонстрируем эффект детерминированных режимов, чтобы понять, как Simulink ® обрабатывает синхронизацию.
Эталонное решение (ode3) - Simulink ® сконфигурирован, чтобы предоставить эталонное решение путем синхронизации данных на каждом главном и незначительном временном шаге.
Удержание нулевого порядка - каждый поток решает подсистему уравнений с помощью собственного решателя, синхронизируя при этом данные только на основных временных шагах.
Линейная экстраполяция - В дополнение к режиму удержания нулевого порядка каждый решатель экстраполирует данные с помощью линейных предсказаний, чтобы компенсировать ошибки задержки данных.
Для большинства систем, где точки синхронизации сглажены, линейный режим экстраполяции обеспечивает хороший компромисс между узкими местами связи и численной точностью.
h = figure; hVal = ishold; hold on; mdl = 'slexMulticoreSolverExample'; dt = get_param(mdl, 'DataTransfer'); modes = { ... 'Ensure deterministic transfer (minimum delay)', ... 'None', 'k:', ... 'Ensure deterministic transfer (maximum delay)', ... 'Zero Order Hold', 'm', ... 'Ensure deterministic transfer (maximum delay)', ... 'Linear', 'b' ... }; for i=1:3:length(modes) dt.DefaultTransitionBetweenContTasks = modes{i}; dt.DefaultExtrapolationMethodBetweenContTasks = modes{i+1}; out = sim(mdl); plot(out.logsout.get('x1').Values.Time, ... out.logsout.get('x1').Values.Data, ... modes{i+2}); end legend('Reference solution (ode3)', ... 'Zero Order Hold Extrapolation', ... 'Linear Extrapolation');
close_system('slexMulticoreSolverExample',0); close_system('slexMulticoreSolverMdlref',0); if ~hVal, hold off; end delete(h);