File Exchange Pick of the Week

Our best user submissions

Persistent Data for Lookup Tables in Simulink

Posted by Richard Ruff,

Richard is a Consultant at MathWorks focused on Model Based Design, primarily in the Aerospace and Defense industry.

Richard’s pick this week is Persistent Data for Lookup Tables in Simulink by Jason Nicholson.

Pick

My pick this week is Persistent Data for Lookup Tables in Simulink – a method to speed up simulations that use large lookup tables.

As systems become more and more complex, the need for simulation increases. Simulation provides insight into the behavior
and performance of the system. However, as systems become more complex, the level of fidelity needed in a simulation also
increases, which often increases the time it takes for the simulation to run. This in turn slows the development process.

Persistent Data for Lookup Tables in Simulink addresses a specific issue related to simulation performance – as noted in the
File Exchange entry: “Loading large lookup tables in Simulink models can be the bottle neck for the simulation speed. This is more important when
you cannot load a lookup table into the base workspace or model workspace when you build a Simulink library. When you don’t
know how the library will be used, loading data into the base or model workspace can cause problems and thus it is bad practice
to do so. This set of files shows how to load a lookup table in the mask initialization of a subsystem block and then save
it to UserData. On subsequent runs, the data stored in UserData is used for the lookup table. The speed up is 10-50x.”

It should be noted that this approach is only valid when running multiple simulations of a Simulink model.

The download from the File Exchange contains two Simulink models, a Simulink library containing the two implementations of
the masked Lookup Table block (with and without persistent data), and a script that describes the application.

To test this, I extracted and ran the simulation execution code provided in RunSimulations.

NUMBER_OF_TIMING_RUNS = 10;
RUN_MODES = {'Normal', 'Accelerator'};
MODEL_1 = 'withoutPersistentLoading';
MODEL_2 = 'withPersistentLoading';
models = {MODEL_1, MODEL_2};

% models = {'FastRestartTest'};

% loop over the run modes
for iMode = 1:length(RUN_MODES)
    for iModel = 1:length(models)
        memoryBeforeRun = nan(NUMBER_OF_TIMING_RUNS, 1);
        memoryAfterRun = nan(NUMBER_OF_TIMING_RUNS, 1);
        load_system(models{iModel});
        for iSim = 1:NUMBER_OF_TIMING_RUNS
            memoryBeforeRun(iSim) = getfield(memory, 'MemUsedMATLAB')/1024^2;
            simOut = sim(models{iModel}, 'SimulationMode', RUN_MODES{iMode});
            if iSim == 1
                TimingInfo = simOut.SimulationMetadata.TimingInfo;
            else
                TimingInfo(iSim) = simOut.SimulationMetadata.TimingInfo;
            end
            memoryAfterRun(iSim) = getfield(memory, 'MemUsedMATLAB')/1024^2;
        end
        close_system(models{iModel});
        
        fprintf('Model "%s", in "%s" mode:\n', models{iModel}, RUN_MODES{iMode})
        outputTable = table(memoryBeforeRun, memoryAfterRun, ...
            vertcat(TimingInfo.InitializationElapsedWallTime), ...
            vertcat(TimingInfo.ExecutionElapsedWallTime), ...
            vertcat(TimingInfo.TerminationElapsedWallTime), ...
            vertcat(TimingInfo.TotalElapsedWallTime), ...
            'VariableNames', ...
            {'memoryBefore', 'memoryAfter', 'initializationTime', ...
            'executionTime', 'terminationTime', 'totalElapsedTime'});
        outputTable.Properties.VariableUnits = {'MB', 'MB', 'sec', 'sec', 'sec','sec'};
        disp(outputTable)
        fprintf('mean Initialization time = %g\n', mean(outputTable.initializationTime));
        fprintf('mean Total Elapsed time = %g\n', mean(outputTable.totalElapsedTime));
        fprintf('subsequent runs mean Initialization time = %g\n', mean(outputTable.initializationTime(2:end)));
        fprintf('subsequent runs mean Total Elapsed time = %g\n', mean(outputTable.totalElapsedTime(2:end)));
        fprintf('\n\n\n');
    end
end

The results are:

RunSimulations
Model "withoutPersistentLoading", in "Normal" mode:
    memoryBefore    memoryAfter    initializationTime    executionTime    terminationTime    totalElapsedTime
    ____________    ___________    __________________    _____________    _______________    ________________
       3972.2         3972.3             3.6936            0.000501          0.003477             3.6976     
       3972.3         3972.3             3.6783            0.000385          0.002951             3.6817     
       3972.3         3972.3             3.5621                   0           0.00351             3.5656     
       3972.3         3972.2             3.5239                   0          0.003008             3.5269     
       3972.2         3972.2             3.5635              0.0005          0.002925             3.5669     
       3972.2         3972.2             3.5189              0.0005          0.003007             3.5224     
       3972.2         3972.2             3.6783            0.000869          0.003016             3.6822     
       3972.2         3972.2             4.2864                   0           0.00401             4.2904     
       3972.2         3972.2             4.0488            0.000502          0.003008             4.0523     
       3972.2         3972.2             4.0197            0.000501          0.003009             4.0232     
mean Initialization time = 3.75736
mean Total Elapsed time = 3.76093
subsequent runs mean Initialization time = 3.76444
subsequent runs mean Total Elapsed time = 3.76797



Model "withPersistentLoading", in "Normal" mode:
    memoryBefore    memoryAfter    initializationTime    executionTime    terminationTime    totalElapsedTime
    ____________    ___________    __________________    _____________    _______________    ________________
       3081.1         3972.1              3.4181           0.000494          0.003146              3.4217    
       3972.1         3097.5             0.16842           0.001027          0.003958              0.1734    
       3097.5         3097.5            0.053643                  0          0.003551            0.057194    
       3097.5         3097.5            0.053183           0.000785           0.00251            0.056478    
       3097.5         3097.5            0.053181           0.000463          0.003542            0.057186    
       3097.5         3097.5            0.053642                  0          0.003364            0.057006    
       3097.5         3097.5            0.055127           0.000463          0.003007            0.058597    
       3097.5         3097.5            0.055593           0.000503          0.003005            0.059101    
       3097.5         3097.5            0.051575           0.000501          0.003099            0.055175    
       3097.5         3097.5            0.054585                  0           0.00305            0.057635    
mean Initialization time = 0.401701
mean Total Elapsed time = 0.405348
subsequent runs mean Initialization time = 0.0665499
subsequent runs mean Total Elapsed time = 0.0701974



Model "withoutPersistentLoading", in "Accelerator" mode:
    memoryBefore    memoryAfter    initializationTime    executionTime    terminationTime    totalElapsedTime
    ____________    ___________    __________________    _____________    _______________    ________________
       3081.1         3972.2             3.4136            0.000433          0.004012             3.4181     
       3972.2         3972.2              3.479            0.000351           0.00447             3.4839     
       3972.2         3972.2             3.5448            0.000357          0.004129             3.5493     
       3972.2         3972.2             3.5329              0.0005           0.00401             3.5374     
       3972.2         3972.2              3.584            0.000828          0.004224             3.5891     
       3972.2         3972.2             3.5119            0.000367          0.004014             3.5163     
       3972.2         3972.2             3.6458            0.000499           0.00438             3.6506     
       3972.2         3972.2             3.5952            0.000969           0.00513             3.6013     
       3972.2         3972.2             3.5499              0.0005          0.004259             3.5547     
       3972.2         3972.2             4.6684            0.000501          0.009526             4.6784     
mean Initialization time = 3.65256
mean Total Elapsed time = 3.65791
subsequent runs mean Initialization time = 3.67911
subsequent runs mean Total Elapsed time = 3.68456



Model "withPersistentLoading", in "Accelerator" mode:
    memoryBefore    memoryAfter    initializationTime    executionTime    terminationTime    totalElapsedTime
    ____________    ___________    __________________    _____________    _______________    ________________
         3081         3972.2              4.0312           0.000501          0.004511             4.0362     
       3972.2         3097.5             0.21853           0.000501          0.004513            0.22354     
       3097.5         3097.5            0.098708           0.000501           0.00401            0.10322     
       3097.5         3097.5             0.10075           0.000462           0.00437            0.10558     
       3097.5         3097.5             0.10271           0.000502          0.004011            0.10723     
       3097.5         3097.5             0.10353           0.000356           0.00429            0.10818     
       3097.5         3097.5            0.099265           0.000896          0.004016            0.10418     
       3097.5         3097.5             0.11075                  0          0.004832            0.11559     
       3097.5         3097.5             0.10829                  0          0.006519            0.11481     
       3097.5         3097.5             0.11425           0.000497          0.005016            0.11976     
mean Initialization time = 0.508801
mean Total Elapsed time = 0.513832
subsequent runs mean Initialization time = 0.117421
subsequent runs mean Total Elapsed time = 0.122453



As can be seen in the results, the model without the persistent data takes about 2.8 seconds to run each simulation. Running
it in Accelerator mode increases the time slightly to around 2.9 seconds. If we run the model with persistent data, the first
iteration takes a little more time than the simulations without persistent data, about 3.0 seconds. However, subsequent runs
with persistent data show a drastic improvement, running in a little over 0.5 seconds. Similar to the runs without persistent
data, the runs in Accelerator mode took slightly longer. This additional time is caused by having to verify the Accelerator
mode version is up to date and no changes have been made to the model between runs.

I was also curious to see how Jason’s solution would compare with the built-in capability Fast Restart. I modified the code used to generate the previous results to run the model without persistent data using Fast Restart.
Here’s the code for RunFastRestart.

NUMBER_OF_TIMING_RUNS = 10;

memoryBeforeRun = nan(NUMBER_OF_TIMING_RUNS, 1);
memoryAfterRun = nan(NUMBER_OF_TIMING_RUNS, 1);
load_system(models{iModel});
for iSim = 1:NUMBER_OF_TIMING_RUNS
    memoryBeforeRun(iSim) = getfield(memory, 'MemUsedMATLAB')/1024^2;
    simOut = sim('withoutPersistentLoading',  'FastRestart', 'on');
    if iSim == 1
        TimingInfo = simOut.SimulationMetadata.TimingInfo;
    else
        TimingInfo(iSim) = simOut.SimulationMetadata.TimingInfo;
    end
    memoryAfterRun(iSim) = getfield(memory, 'MemUsedMATLAB')/1024^2;
end
set_param(gcs, 'FastRestart', 'off');
close_system(models{iModel});

fprintf('Model "%s", using FastRestart\n', models{iModel})
outputTable = table(memoryBeforeRun, memoryAfterRun, ...
    vertcat(TimingInfo.InitializationElapsedWallTime), ...
    vertcat(TimingInfo.ExecutionElapsedWallTime), ...
    vertcat(TimingInfo.TerminationElapsedWallTime), ...
    vertcat(TimingInfo.TotalElapsedWallTime), ...
    'VariableNames', ...
    {'memoryBefore', 'memoryAfter', 'initializationTime', ...
    'executionTime', 'terminationTime', 'totalElapsedTime'});
outputTable.Properties.VariableUnits = {'MB', 'MB', 'sec', 'sec', 'sec','sec'};
disp(outputTable)
fprintf('mean Initialization time = %g\n', mean(outputTable.initializationTime));
fprintf('mean Total Elapsed time = %g\n', mean(outputTable.totalElapsedTime));
fprintf('subsequent runs mean Initialization time = %g\n', mean(outputTable.initializationTime(2:end)));
fprintf('subsequent runs mean Total Elapsed time = %g\n', mean(outputTable.totalElapsedTime(2:end)));

The results are:

RunFastRestart
Model "withPersistentLoading", using FastRestart
    memoryBefore    memoryAfter    initializationTime    executionTime    terminationTime    totalElapsedTime
    ____________    ___________    __________________    _____________    _______________    ________________
         3081         3988.5               3.513           0.000502          0.003007              3.5165    
       3988.5         3988.5            0.020555           0.000501          0.002004             0.02306    
       3988.5         3988.5            0.018047                  0          0.002006            0.020053    
       3988.5         3988.5            0.017049           0.000497          0.002035            0.019581    
       3988.5         3988.5            0.016983           0.000711          0.001507            0.019201    
       3988.5         3988.5            0.017546                  0          0.002065            0.019611    
       3988.5         3988.5            0.018549                  0          0.002006            0.020555    
       3988.5         3988.5            0.020056                  0          0.002465            0.022521    
       3988.5         3988.5            0.018049                  0          0.002006            0.020055    
       3988.5         3988.5            0.018042             0.0005          0.001923            0.020465    
mean Initialization time = 0.367784
mean Total Elapsed time = 0.370157
subsequent runs mean Initialization time = 0.0183196
subsequent runs mean Total Elapsed time = 0.0205669

As you can see, using Fast Restart actually improved the overall performance of the simulation runs with an average run time
around 0.018 seconds. The first run using Fast Restart still requires the loading of the data and this can be seen in the
execution time being very similar to the first simulation run without persistent data in normal mode shown previously.

Comments

As shown in the results, the speed up in simulation time using this method only applies to subsequent runs, as the first simulation
will still encounter the bottle neck associated with loading the data the first time. Furthermore, the results show that
using FastRestart provides some improvement in these simple cases but I would expect even better performance for larger and
more complex models. The advantage of Fast restart is that it doesn’t require the user to create custom masks for blocks
and their associated initialization code and it is applied to the entire model. However, FastRestart is not a valid approach
if you are using an older version of MATLAB (prior to R2014B release) or if you require running in Accelerator mode. It is
nice to know this approach is available if needed.

All in all, this is a great example of how to leverage functionality in Simulink to eliminate a bottle neck in simulation
execution time. Give it a try and let us know what you think here or leave a message for Jason.

Get the MATLAB code

Published with MATLAB® R2018a

Add A Comment

Your email address will not be published. Required fields are marked *

Preview: hide