Многоядерная симуляция и генерация кода областей Dataflow

Симуляция областей Dataflow

Симуляция областей dataflow использует многоядерную архитектуру CPU хоста-компьютера. Он автоматически разделяет вашу модель и симулирует подсистему с помощью нескольких потоков.

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

Генерация кода областей Dataflow

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

Типы параллелизма

В симуляции и генерации кода моделей с областями dataflow программное обеспечение идентифицирует возможные параллелизма в вашей системе и разделяет область dataflow с помощью следующих типов параллелизма.

Параллелизм задач

Параллелизм задачи достигает параллелизма путем разделения приложения на несколько задач. Параллелизм задачи включает распределение задач внутри приложения между несколькими узлами обработки. Одни задачи могут иметь зависимость данных от других, поэтому все задачи не выполняются точно в одно и то же время.

Рассмотрим систему, которая включает четыре функции. Функции F2a () и F2b () параллельны, то есть могут запускаться одновременно. В параллелизме задачи можно разделить расчет на две задачи. Функция F2b () запускается на отдельном узле обработки после того, как она получает Out1 данных из Задачи 1 и выводит назад в F3 () в Задаче 1.

Рисунок показывает временную схему для этого параллелизма. Задача 2 не запускается, пока она не получит Out1 данных из Задачи 1. Следовательно, эти задачи не выполняются полностью параллельно. Время, затрачиваемое на цикл процессора, известное как время цикла, является

t = tF1 + max (tF2a, tF2b) + tF3.

Моделирование выполнения трубопровода (конвейеризация)

Программа использует выполнение трубопровода модели, или конвейеризацию, чтобы обойти задачу параллелизма задачи, где потоки не выполняются полностью параллельно. Этот подход включает изменение системы, чтобы ввести задержки между задачами, где существует зависимость данных.

На этом рисунке система разделена на три задачи для выполнения на трех различных узлах обработки с задержками, введенными между функциями. На каждом временном шаге каждая задача принимает значение от предыдущего временного шага посредством задержки.

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

t = макс. (Task1, Task2, Task3) = макс. (tF1, tF2a, tF2b, tF3).

Конвейеризация может использоваться, когда можно ввести задержки искусственно в одновременно выполняемой системе. Результирующие накладные расходы из-за этого введения не должны превышать времени, сэкономленного при конвейеризации.

Разворачивание

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

Развертывание используется в сценариях, где можно обрабатывать каждую часть входных данных независимо, не влияя на выход, и блок не имеет статуса или содержит конечное число состояний.

Улучшите пропускную способность симуляции с многоядерной симуляцией

Этот пример показывает, как улучшить пропускную способность симуляции системы путем симуляции подсистемы с несколькими потоками. Чтобы включить автоматическое разбиение системы на разделы и многопоточную симуляцию, установите Domain подсистемы равной Dataflow. Для получения дополнительной информации о областях dataflow, смотрите Dataflow Domain

  1. Чтобы начать, откройте модель.

    addpath (fullfile(docroot, 'toolbox', 'dsp', 'examples'));
    ex_staple_counting
  2. Симулируйте модель и наблюдайте систему координат системы в блоке Frame Rate Display. Это количество указывает количество систем координат в секунду, которое Simulink® способен обрабатывать в стандартной симуляции.

  3. Чтобы включить многопоточное моделирование и улучшить пропускную способность симуляции, установите область подсистемы на dataflow.

    Если Property Inspector не отображается, на вкладке Modeling, под Design, выберите Property Inspector.

    При выбранной подсистеме на вкладке Execution Property Inspector выберите Set execution domain. Установите Domain равным Dataflow.

  4. Иногда можно увеличить доступность параллелизма в системе путем добавления Latency в систему. Чтобы выбрать оптимальное значение задержки, используйте Dataflow Simulation Assistant. Нажмите кнопку Dataflow assistant, чтобы открыть Dataflow Simulation Assistant.

  5. В дополнение к предложению значения задержки, помощник по симуляции Dataflow также предлагает настройки модели для оптимальной эффективности симуляции. В этом примере, чтобы улучшить эффективность симуляции, помощник по симуляции Dataflow предлагает отключить параметр Гарантировать быстродействие (Simulink).

    Чтобы принять предложенные настройки модели, рядом с Suggested model settings for simulation performance нажмите Accept all.

  6. Затем нажмите кнопку Analyze. Помощник по моделированию Dataflow анализирует подсистему для эффективности симуляции и предлагает оптимальную задержку для вашей Подсистемы Dataflow.

    Анализ dataflow является двухэтапным процессом. На первом этапе подсистема dataflow моделирует с помощью одного потока. Во время этой симуляции программное обеспечение выполняет анализ затрат. В следующий раз, когда модель компилируется, программное обеспечение автоматически разделяет подсистему на один или несколько потоков, чтобы использовать преимущества параллелизма в модели. Последующие симуляции являются многопоточными. Ассистент предлагает значение задержки, которое оптимизирует пропускную способность системы.

  7. Нажмите кнопку Accept, чтобы применить рекомендуемую задержку к системе. Помощник по моделированию Dataflow применяет задержку к модели и указывает количество потоков, которые модель будет использовать во время последующих симуляций. Задержка системы обозначается значком задержки на холсте модели на выходе подсистемы.

  8. Моделируйте модель еще раз. Наблюдайте улучшенную пропускную способность симуляции из многопоточного моделирования в блоке Frame Rate Display.

Сгенерируйте многоядерный код из подсистемы Dataflow

Сконфигурируйте модель для генерации многоядерного кода

Для генерации кода требуется Simulink Coder™ или Embedded Coder® лицензия. Поддерживаются одноядерные и многоядерные целевые системы.

Код, сгенерированный для одноядерных целей, генерирует невиртуальный подсистемный код.

Чтобы сгенерировать многоядерный код, вы должны сконфигурировать модель для параллельного выполнения. Если вы не конфигурируете свою модель для параллельного выполнения, сгенерированный код будет однопоточным.

  1. В Configuration Parameters > Solver > Solver selection выберите Fixed-step для Type и auto (Automatic solver selection) для Solver.

  2. Установите флажок Allow tasks to execute concurrently on target на панели Solver под Solver details. Установка этого флажка опциональна для моделей, на которые ссылаются в иерархии модели. Когда вы выбираете эту опцию для ссылочной модели, Simulink позволяет каждой скорости в ссылочной модели выполнять как независимую параллельную задачу на целевом процессоре.

  3. В Configuration Parameters> Code Generation> Interface> Advanced parameters, очистите флажок MAT-file logging.

  4. Щелкните Apply, чтобы применить настройки к модели.

Сгенерируйте многоядерный код

Чтобы сгенерировать многоядерный код, программное обеспечение выполняет анализ затрат и разделяет область dataflow на основе вашего заданного целевого объекта. Разбиение области dataflow может или не может совпадать с разбиением во время симуляции.

Сгенерированный код C содержит один void(void) функция для каждой задачи или потока, созданного подсистемой dataflow. Каждая из этих функций состоит из:

  • Код С, относящийся к блокам, которые были разделены на этот поток

  • Код, который генерируется для обработки передачи данных между потоками.

    Это может быть в форме задержек трубопровода или специфичной для цели реализации семафоров синхронизации данных.

Для генерации кода поддерживаются следующие многоядерные целевые системы.

  • Linux®, Windows®, и целевые системы рабочего стола Mac OS, использующие ert.tlc и grt.tlc.

  • Simulink Real-Time™ с использованием slrealtime.tlc.

  • Целевые устройства Embedded Coder, использующие Linux и VxWorks® операционные системы.

Код, сгенерированный для grt.tlc и ert.tlc целевые устройства рабочего стола многопоточны с использованием OpenMP в подсистеме dataflow. Код, сгенерированный для целей Embedded Coder, многопоточен с помощью потоков POSIX.

Если ваша система содержит блоки, которые не поддерживают многопоточное выполнение, сгенерированный код является однопоточным.

Чтобы создать модель и сгенерировать код, нажмите Ctrl+B.

В сгенерированном коде можно наблюдать вызовы потокового API и задержки конвейера, которые были вставлены в модель, чтобы создать больше параллелизма.

Следующий пример показывает, что существует две функции потока, сгенерированные подсистемой dataflow, ex_staple_counting_ThreadFcn0 и ex_staple_counting_ThreadFcn1, которые выполняются с использованием разделов OpenMP. Эти функции являются частью dataflow_subsystem_output/step() функция.

static void ex_staple_counting_ThreadFcn0(void)
 	{
 	  ...

	  if (pipeStage_Concurrent0 >= 2) {
	    /* Delay: '<S3>/TmpDelayBufferAtDraw Markers1Inport1' */
	    memcpy(&ex_staple_counting_B.TmpDelayBufferAtDrawMarkers1I_i[0],
	           &ex_staple_counting_DW.TmpDelayBufferAtDrawMarkers1I_i[0], 202176U *
	           sizeof(real32_T));
	
	    /* Delay: '<S3>/TmpDelayBufferAtDraw Markers1Inport2' */
	    line_idx_1 = (int32_T)ex_staple_counting_DW.CircBufIdx * 100;
	    memcpy(&ex_staple_counting_B.TmpDelayBufferAtDrawMarkers1Inp[0],
	           &ex_staple_counting_DW.TmpDelayBufferAtDrawMarkers1Inp[line_idx_1],
	           100U * sizeof(real_T));
...
        }
void ex_staple_counting_Concurrent0(void)
	{
...
	
	#pragma omp parallel num_threads(3)
	
	  {
	#pragma omp sections
	
	    {
	
	#pragma omp section
	
	      {
	        ex_staple_counting_ThreadFcn0();
	      }
	
	#pragma omp section
	
	      {
	        ex_staple_counting_ThreadFcn1();
	      }
	
	#pragma omp section
	
	      {
	        ex_staple_counting_ThreadFcn2();
	      }
	    }
	  }

См. также

Похожие темы