Main Content

Determine Why Simulink Accelerator Is Regenerating Code

Sometimes Simulink® regenerates the simulation target for a model at the beginning of a simulation in Accelerator Mode but it is not always clear why regeneration happens. This example shows how to use Simulink MATLAB® commands to determine why Simulink regenerates code for Accelerator Mode simulations.

Simulink's Accelerator Mode speeds up simulation of your model by creating an executable version of the model, called a simulation target, and running this target instead of interpreting the model, as is done during Normal Mode simulation.

The process of code generation happens during the first time that you simulate your model in Accelerator Mode. Additionally, code generation may happen on subsequent simulations, especially if the model changes between simulations (for example, after addition of a block). Code generation takes times and it is often desirable to avoid it, in order to maximize the number of simulations within a given time span.

Simulink uses the model's checksum to determine if the code needs to be regenerated. This checksum is an array of four integers computed by using an md5 checksum algorithm based on attributes of the model and the blocks that it contains. Any change in the model that changes the checksum causes Simulink to regenerate the simulation target for Accelerator Mode.

Sometimes, it is not clear which model change triggered a checksum change and hence code regeneration. This example shows how to investigate why Simulink needs to regenerate code for Accelerator Mode simulation for a given model and its configuration.

Open an Example Model

We will use a simple model, slAccelDemoWhyRebuild, throughout this example.

model = 'slAccelDemoWhyRebuild';
open_system(model)

set_param(model,'AccelVerboseBuild','on');

The first time the model runs in Accelerator mode, it generates and compiles code as expected.

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

If the simulation is run again, without any changes to the model, we expect that Simulink can reuse the existing code and does not need to regenerate the code. Execute the same commands to verify.

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

Now we change some parameters in the model. We will set the following settings for the block 'Integrator':

  • Set 'Ignore limit and reset when linearizing' to 'on'

  • Set 'Initial Condition' to '5'

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

When we run the simulation again, we see that Simulink regenerates the code.

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

We would like to know why.

To determine if the previously generated code is still valid for the current model configuration, Simulink compares the checksum of the model as used to generate the code to the current checksum. If they are equal, the previously generated code is still valid and Simulink Accelerator Mode reuses it for the current simulation. If the values differ, Simulink Accelerator Mode regenerates and rebuilds the code. Thus, examining the details of the checksum computation can reveal why Simulink regenerated the code.

Get Checksum Details

The following command gets the model checksum computation details:

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

The first output is the model checksum value itself. The second output gives details of what went into the checksum computation.

Let us set the modified block parameters to their original values and get the checksum and details for that configuration.

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

Comparing these two checksum values is equivalent to determining if the Simulink Accelerator will regenerate code. Note that the checksum values are different, as we expect based on the fact that Simulink Accelerator regenerates code every time it runs.

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

Now that we know that the checksums differ, the next question is why. Many things go into the checksum computation, including signal data types, some block parameter values, and block connectivity information. To understand why the checksums differ, we need to see what has changed about the items used in computing the checksum. The checksum details returned as the second argument give that information.

csdet1
csdet1 = struct with fields:
          ContentsChecksum: [1x1 struct]
         InterfaceChecksum: [1x1 struct]
     ContentsChecksumItems: [189x1 struct]
    InterfaceChecksumItems: [49x1 struct]

The checksum details is a structure array with four fields. Two of the fields are the component checksums of the model checksum (these are called ContentsChecksum and InterfaceChecksum) and the other two are the corresponding checksum details. These details correspond to various information which went into to the computation of the two component checksums. The model [structural] checksum is a function of ContentsChecksum and InterfaceChecksum.

First, let's determine if the difference lies in the model's contents or the model's interface.

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

Use Details to Determine Why Checksum Changed

Now that we know the change is in the ContentsChecksum, we can look at the ContentsChecksumItems to see what has changed.

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

Now that we know the differences are in the items at the indices listed in idxForDifferences, we can look at those items in the two ContentsChecksumItems arrays.

blk1 = csdet1.ContentsChecksumItems(idxForDifferences(1)).Handle
blk1 = 
'slAccelDemoWhyRebuild/Integrator'
blk2 = csdet2.ContentsChecksumItems(idxForDifferences(1)).Handle
blk2 = 
'slAccelDemoWhyRebuild/Integrator'
id1 = csdet1.ContentsChecksumItems(idxForDifferences(1)).Identifier
id1 = 
'IgnoreLimit'
id2 = csdet2.ContentsChecksumItems(idxForDifferences(1)).Identifier
id2 = 
'IgnoreLimit'

The Handle for both items is 'slAccelDemoWhyRebuild/Integrator', which indicates the block with the changing data. The identifier for both is 'IgnoreLimit' which tells us that this was the block setting which changed, resulting in a different checksum for the model. The setting for the Initial Condition of the block does not appear in the checksum detail. Therefore, we expect that if only the setting for the Initial Condition is modified, that no rebuild will occur.

Avoid Rebuild on Successive Simulations

Now that we found the parameter which causes the checksums to differ for this workflow, we can validate the findings by keeping that parameter constant and seeing whether rebuild occurs on successive simulations.

Let's simulate the model in Accelerator mode again. We expect it to rebuild for this simulation because we changed the parameter ('IgnoreLimit') for the checksum computation above.

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

Now let's only change the Initial Condition setting and simulate again. We expect that no rebuild should happen this time.

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

As expected from the checksum analysis, changing the parameter for 'Initial Condition' does not cause a regeneration of code for simulation in Accelerator mode.

See Also

| |

Related Topics