Main Content

Simulink Accelerator がコードを再生成している原因の特定

Simulink® はアクセラレータ モードでのシミュレーションの開始時にモデルのシミュレーション ターゲットを再生成する場合がありますが、その理由は必ずしも明らかではありません。この例では、Simulink MATLAB® コマンドを使用して、Simulink がアクセラレータ モードのシミュレーション用にコードを再生成する理由を判断する方法を示しています。

Simulink のアクセラレータ モードは、シミュレーション ターゲットと呼ばれる実行可能バージョンのモデルを作成して、ノーマル モードでのシミュレーション中と同様に、モデルを解釈する代わりにこのターゲットを実行することにより、モデルのシミュレーションの速度を向上させます。

コード生成のプロセスは、最初にモデルをアクセラレータ モードでシミュレーションするときに発生します。また、シミュレーション間でモデルが変更された場合 (たとえば、ブロックの追加後) は特に、以降のシミュレーションでもコード生成が発生することがあります。コード生成は時間がかかるため、指定の時間範囲でのシミュレーション実行回数が最大となるよう、多くの場合にコード生成の回避が推奨されます。

Simulink はモデルのチェックサムを使用して、コードを再生成する必要があるかどうかを判定します。このチェックサムは、モデルとそのモデルに含まれているブロックの属性に基づいて、md5 チェックサム アルゴリズムを使用して計算される 4 つの整数の配列です。モデルが変更されたことによってチェックサムが変更されると、Simulink はアクセラレータ モード用のシミュレーション ターゲットを再生成します。

どのようなモデルの変更によってチェックサムが変更され、コードが再生成されたのか不明な場合もあります。この例では、指定されたモデルおよび構成で実行されるアクセラレータ モードでのシミュレーション用に Simulink がコードを再生成しなければならない理由を調査する方法を示しています。

モデル例を開く

この例ではシンプルなモデル slAccelDemoWhyRebuild を使用します。

model = 'slAccelDemoWhyRebuild';
open_system(model)

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

このモデルは、最初にアクセラレータ モードで実行したときは、予期したとおりコードを生成してコンパイルします。

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" に対して次の設定を行います。

  • [線形化時に出力制限とリセットの設定を無視] を [オン] に設定

  • [初期条件] を [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 は、以前に生成されたコードが現在のモデル コンフィギュレーションにまだ有効であるかどうかを判定するために、コードの生成に使用されたモデルのチェックサムと、現在のチェックサムを比較します。これらのチェックサムが等しい場合は、以前に生成されたコードがまだ有効であるため、Simulink アクセラレータ モードはそのコードを現在のシミュレーションに再利用します。チェックサムの値が異なっている場合は、Simulink アクセラレータ モードはコードを再生成してリビルドします。したがって、チェックサム計算の詳細を調べると、Simulink がコードを再生成した原因を解明できます。

チェックサムの詳細の取得

次のコマンドによって、モデルのチェックサム計算の詳細が取得されます。

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

最初の出力は、モデルのチェックサム値そのものです。2 つ目の出力は、チェックサム計算に入れられたものの詳細を示しています。

変更されたブロック パラメーターに元の値を設定し、この構成に関するチェックサムと詳細を取得してみましょう。

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

これらの 2 つのチェックサム値を比較するということは、Simulink Accelerator がコードを再生成するかどうかを判定するのと同じことです。Simulink Accelerator が実行時に毎回コードを再生成するという事実に基づいて予想したとおり、これらのチェックサム値は異なっています。

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

チェックサムが異なっていることがわかったので、次に問題となるのはその原因です。信号のデータ型、複数のブロック パラメーター値、ブロックの接続情報など、さまざまなものがチェックサム計算に入れられます。チェックサムが異なっている原因を理解するには、チェックサムの計算に使用された項目に関して、何が変更されているのかを確認する必要があります。2 番目の引数として返されたチェックサム詳細に、その情報が示されています。

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

チェックサム詳細は構造体配列であり、4 つのフィールドがあります。そのうちの 2 つのフィールドはモデルのチェックサムのコンポーネント チェックサムであり (ContentsChecksum および InterfaceChecksum と呼ばれる)、残りの 2 つのフィールドは対応するチェックサム詳細です。これらの詳細は、2 つのコンポーネント チェックサムの計算に入れられたさまざまな情報に対応しています。モデルの (構造的) チェックサムは ContentsChecksum および InterfaceChecksum の関数です。

まず、モデルの内容が異なっているのか、あるいはモデルのインターフェイスが異なっているのかを判断しましょう。

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

詳細を使用したチェックサム変更理由の判断

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 に示されているインデックスの項目が異なっていることがわかったので、2 つの配列 ContentsChecksumItems 内にあるそれらの項目を調べることができます。

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'

どちらの項目もハンドルは "slAccelDemoWhyRebuild/Integrator" です。これは、そのブロックのデータが変更されていることを示しています。どちらの項目も識別子は "IgnoreLimit" です。これは、変更されたのがブロック設定であったことを示し、その結果モデルのチェックサムが異なっています。ブロックの初期条件の設定は、チェックサムの詳細には表示されません。そのため、初期条件の設定のみが変更された場合は、リビルドは発生しないと予想されます。

連続シミュレーションでのリビルドの回避

このワークフローにおいてチェックサムに違いを生じさせるパラメーターがわかったため、このパラメーターを一定に保ち、連続シミュレーションでリビルドが発生するかどうかを確認することで、この結果を検証できます。

モデルをアクセラレータ モードで再度シミュレーションしてみましょう。上記のチェックサム計算でパラメーター ("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

次に、初期条件の設定のみを変更し、再度シミュレーションしてみましょう。今回はリビルドは発生しないと予想されます。

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

チェックサム解析から予想されるとおり、"初期条件" のパラメーターを変更しても、アクセラレータ モードでのシミュレーション用にコードが再生成されることはありません。

参考

| |

関連するトピック