Определение, почему акселератор Simulink регенерирует код

Sometimes Simulink® регенерирует цель симуляции для модели в начале симуляции в Accelerator Mode(TM), но не всегда ясно, почему регенерация происходит. Этот пример показывает, как использовать команды Simulink® MATLAB®, чтобы определить, почему Simulink регенерирует код для симуляций Режима Accelerator.

Режим Accelerator Simulink ускоряет симуляцию вашей модели путем создания выполняемой версии модели, названной целью симуляции, и выполнения этой цели вместо того, чтобы интерпретировать модель, как сделан во время симуляции Режима normal mode. Режим Accelerator создает цель симуляции путем генерации кода С из модели и вызова MEX-функции MATLAB®, чтобы скомпилировать и динамически соединить сгенерированный код с Simulink.

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

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

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

Создайте временную работающую директорию

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

originalDir = pwd;
tempDir = tempname;
mkdir(tempDir)
cd(tempDir)

Откройте модель в качестве примера

Мы будем использовать простую модель, slAccelDemoWhyRebuild, в этом примере.

model = 'slAccelDemoWhyRebuild';
open_system(model)
set_param(model,'AccelVerboseBuild','on');

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

simOutput = evalc(['sim(''',model,''')']);
if ~isempty(strfind(simOutput,'Building the Accelerator target for model'))
    disp('Built Simulink Accelerator mex file')
else
    disp('Did not build Simulink Accelerator mex file')
end
Built Simulink Accelerator mex file

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

simOutput = evalc(['sim(''',model,''')']);
if ~isempty(strfind(simOutput,'Building the Accelerator target for model'))
    disp('Built Simulink Accelerator mex file')
else
    disp('Did not build Simulink Accelerator mex file')
end
Did not build Simulink Accelerator mex file

Теперь мы изменяем некоторые параметры в модели. Мы установим следующие настройки для блока 'Integrator': - устанавливают, 'Игнорируют предел и сбрасывают при линеаризации' к 'on' - установленное 'Начальное Условие' к '5'

set_param([model,'/Integrator'],'IgnoreLimit','on');
set_param([model,'/Integrator'],'InitialCondition','5');

Когда мы запускаем симуляцию снова, мы видим, что Simulink регенерирует код.

simOutput = evalc(['sim(''',model,''')']);
if ~isempty(strfind(simOutput,'Building the Accelerator target for model'))
    disp('Built Simulink Accelerator mex file')
else
    disp('Did not build Simulink Accelerator mex file')
end
Built Simulink Accelerator mex file

Мы хотели бы знать почему.

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

Получите детали контрольной суммы

Следующая команда получает образцовые детали вычисления контрольной суммы:

[cs1,csdet1]=Simulink.BlockDiagram.getChecksum(model);

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

Давайте установим измененные параметры блоков на их исходные значения и получим контрольную сумму и детали для той настройки

set_param([model,'/Integrator'],'IgnoreLimit','off');
set_param([model,'/Integrator'],'InitialCondition','0');
[cs2,csdet2]=Simulink.BlockDiagram.getChecksum(model);

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

if (cs1 ~= cs2)
    disp('Checksums are different')
else
    disp('Checksums are the same')
end
Checksums are different

Теперь, когда мы знаем, что контрольные суммы отличаются, следующий вопрос состоит в том почему. Много вещей входят в вычисление контрольной суммы, включая типы данных сигнала, некоторые значения параметров блоков и информацию о возможности соединения блока. Чтобы понять, почему контрольные суммы отличаются, мы должны видеть то, что изменилось об элементах, используемых в вычислении контрольной суммы. Детали контрольной суммы, возвращенные в качестве второго аргумента, дают ту информацию.

csdet1
csdet1 = 

  struct with fields:

          ContentsChecksum: [1x1 struct]
         InterfaceChecksum: [1x1 struct]
     ContentsChecksumItems: [181x1 struct]
    InterfaceChecksumItems: [47x1 struct]

Детали контрольной суммы являются массивом структур с четырьмя полями. Два из полей являются контрольными суммами компонента образцовой контрольной суммы (они называются ContentsChecksum и InterfaceChecksum), и другие два являются соответствующими деталями контрольной суммы. Эти детали соответствуют различной информации, которая вошла к вычислению двух контрольных сумм компонента. Образцовая [структурная] контрольная сумма является функцией ContentsChecksum и InterfaceChecksum.

Во-первых, давайте определим, заключается ли различие в содержимом модели или интерфейсе модели

if (csdet1.ContentsChecksum.Value ~= csdet2.ContentsChecksum.Value)
    disp('Contents checksums are different')
else
    disp('Contents checksums are the same')
end
if (csdet1.InterfaceChecksum.Value ~= csdet2.InterfaceChecksum.Value)
    disp('Interface checksums are different')
else
    disp('Interface checksums are the same')
end
Contents checksums are different
Interface checksums are the same

Используйте детали, чтобы определить почему измененная контрольная сумма

Теперь, когда мы знаем, что изменение находится в ContentsChecksum, мы можем посмотреть на ContentsChecksumItems, чтобы видеть то, что изменилось.

idxForDifferences=[];
for idx = 1:length(csdet1.ContentsChecksumItems)
    if (~strcmp(csdet1.ContentsChecksumItems(idx).Handle, ...
                csdet2.ContentsChecksumItems(idx).Handle))
        idxForDifferences=[idxForDifferences,idx];
        disp(['Handles different for item ',num2str(idx)]);
    end
    if (~strcmp(csdet1.ContentsChecksumItems(idx).Identifier, ...
                csdet2.ContentsChecksumItems(idx).Identifier))
        disp(['Identifiers different for item ',num2str(idx)]);
        idxForDifferences=[idxForDifferences,idx];
    end
    if(ischar(csdet1.ContentsChecksumItems(idx).Value))
        if (~strcmp(csdet1.ContentsChecksumItems(idx).Value, ...
                    csdet2.ContentsChecksumItems(idx).Value))
            disp(['String Values different for item ',num2str(idx)]);
            idxForDifferences=[idxForDifferences,idx];
        end
    end
    if(isnumeric(csdet1.ContentsChecksumItems(idx).Value))
        if (csdet1.ContentsChecksumItems(idx).Value ~= ...
            csdet2.ContentsChecksumItems(idx).Value)
            disp(['Numeric values are different for item ',num2str(idx)]);
            idxForDifferences=[idxForDifferences,idx];
        end
    end
end
String Values different for item 40

Теперь, когда мы знаем, что различия находятся в элементах в индексах, перечисленных в idxForDifferences, мы можем посмотреть на те элементы в двух массивах ContentsChecksumItems

blk1 = csdet1.ContentsChecksumItems(idxForDifferences(1)).Handle
blk2 = csdet2.ContentsChecksumItems(idxForDifferences(1)).Handle
id1 = csdet1.ContentsChecksumItems(idxForDifferences(1)).Identifier
id2 = csdet2.ContentsChecksumItems(idxForDifferences(1)).Identifier
blk1 =

    'slAccelDemoWhyRebuild/Integrator'


blk2 =

    'slAccelDemoWhyRebuild/Integrator'


id1 =

    'IgnoreLimit'


id2 =

    'IgnoreLimit'

Указатель для обоих элементов является 'slAccelDemoWhyRebuild/Integrator', который указывает на блок с изменяющимися данными. Идентификатором для обоих является 'IgnoreLimit', который говорит нам, что это было установкой блока, которая изменилась, приведя к различной контрольной сумме для модели. Установка для Начального Условия блока не появляется в деталях контрольной суммы. Поэтому мы ожидаем, что, если только установка для Начального Условия изменяется, которые не восстанавливают, произойдет.

Избегайте восстанавливания на последовательных симуляциях

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

Давайте моделируем модель в Режиме Accelerator снова. Мы ожидаем, что он восстановит для этой симуляции, потому что мы изменили параметр ('IgnoreLimit') для вычисления контрольной суммы выше.

simOutput = evalc(['sim(''',model,''')']);
if ~isempty(strfind(simOutput,'Building the Accelerator target for model'))
    disp('Built Simulink Accelerator mex file')
else
    disp('Did not build Simulink Accelerator mex file')
end
Built Simulink Accelerator mex file

Теперь давайте только изменим настройки Initial Condition и моделировать снова. Мы ожидаем, что не восстанавливают, должен произойти на этот раз.

set_param([model,'/Integrator'],'InitialCondition','-3');
simOutput = evalc(['sim(''',model,''')']);
if ~isempty(strfind(simOutput,'Building the Accelerator target for model'))
    disp('Built Simulink Accelerator mex file')
else
    disp('Did not build Simulink Accelerator mex file')
end
Did not build Simulink Accelerator mex file

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

Чистка

Закройте модель и удалите сгенерированные файлы.

bdclose(model)
clear([model,'_acc'])
cd(originalDir)
rmdir(tempDir,'s')