メインコンテンツ

parfor 内での関数 sim の使用

メモ

parfor ループ内で sim 関数を呼び出して並列シミュレーションを実行することは推奨されません。並列シミュレーションを実行するには、parsim 関数を使用してください。詳細については、並列シミュレーションの実行を参照してください。

parfor コマンドを使用すると、モデルの同時並列シミュレーションを実行できます。このコンテキストでは、並列とは、同じモデルの複数のシミュレーションが同時に実行され、各同時シミュレーションが異なるワーカーで実行されることを意味します。シミュレーションを並列に実行することは、多くの場合、異なる入力または異なるパラメーター設定で同じモデルの複数のシミュレーションを実行する場合に役立ちます。たとえば、パラメーターのスイープやモンテカルロ解析を実行するとき、シミュレーションを並列に実行することで、シミュレーション時間を節減できます。parfor を使用して並列シミュレーションを実行する場合、モデルを小さい連結部分に分割して、異なるワーカーで同じシミュレーションの個々の部分を実行することはサポートされていません。

parfor 内の sim では、ノーマル、アクセラレータ、およびラピッド アクセラレータの各シミュレーション モードがサポートされています。他のシミュレーション モードの場合、有益な結果を生成するには、ワークスペース アクセスの問題とデータの同時実行の問題に対処しなければなりません。具体的には、シミュレーションでは別の名前の出力ファイルとワークスペース変数を作成する必要があります。そうしないと、シミュレーションを実行するたびに同じワークスペース変数とファイルが上書きされたり、変数とファイルを同時に書き込もうとして競合が発生する可能性があります。

アクセラレータ モードおよびラピッド アクセラレータ モードでのシミュレーションの実行について詳しくは、以下を参照してください。

parfor (Parallel Computing Toolbox) も参照してください。

メモ

parfor ステートメント内でモデルを開く場合、一時ファイルが残らないようにコマンド bdclose all を使用してモデルを再び閉じてください。

parfor 内の sim によるノーマル モードのシミュレーション

このコード フラグメントは、ノーマル モードでの simparfor の使用方法を示しています。parfor でシミュレーションを実行する前に、モデルの変更を保存します。保存されたモデルのコピーは、parfor でのシミュレーション時に並列ワーカーに配布されます。

% 1) Load model and initialize the pool.
openExample('sldemo_suspn_3dof');
mdl = 'sldemo_suspn_3dof';
load_system(mdl);
parpool;

% 2) Set up the iterations that we want to compute.
Cf                  = evalin('base','Cf');
Cf_sweep            = Cf*(0.05:0.1:0.95);
iterations          = length(Cf_sweep);
simout(iterations)  = Simulink.SimulationOutput;

% 3) Need to switch all workers to a separate tempdir in case 
% any code is generated for instance for StateFlow, or any other 
% file artifacts are  created by the model.
spmd
    % Set up tempdir and cd into it
    currDir = pwd;
    addpath(currDir);
    tmpDir = tempname;
    mkdir(tmpDir);
    cd(tmpDir);
    % Load the model on the worker
    load_system(mdl)
end

% 4) Loop over the number of iterations and perform the
% computation for different parameter values.
parfor idx=1:iterations   
    set_param([mdl '/Road-Suspension Interaction'],'MaskValues',...
        {'Kf',num2str(Cf_sweep(idx)),'Kr','Cr'});
    simout(idx) = sim(mdl,'SimulationMode','normal');
end

% 5) Switch all of the workers back to their original folder.
spmd
    cd(currDir);
    rmdir(tmpDir,'s');
    rmpath(currDir);
    close_system(mdl,0);
end

close_system(mdl,0);
delete(gcp('nocreate'));

parfor 内の simMATLAB Parallel Server によるノーマル モードのシミュレーション

parfor 内の simMATLAB® Parallel Server™ を使用してノーマル モードのシミュレーションを実行するには、parfor 内の sim によるノーマル モードのシミュレーションのコードに次の変更を加えます。

  • parpool コマンドを変更して並列プールに名前を付け、プールを参照するために使用できるオブジェクトを作成します。

    p = parpool('clusterProfile');

  • 並列プールを作成した後、モデルの実行に必要なファイルをプールに添付して、ワーカーに配布します。

    files = dependencies.fileDependencyAnalysis(mdl);
    p.addAttachedFiles(files);

  • MATLAB Parallel Server クラスターがない場合は、ローカル クラスターを使用します。詳細については、クラスターの検出とクラスター プロファイルの使用 (Parallel Computing Toolbox)を参照してください。

コードを実行する前にクラスターを開始します。

% 1) Load model and initialize the pool.
openExample('sldemo_suspn_3dof');
mdl = 'sldemo_suspn_3dof';
load_system(mdl);
parpool;

% 2) Set up the iterations that we want to compute.
Cf                  = evalin('base','Cf');
Cf_sweep            = Cf*(0.05:0.1:0.95);
iterations          = length(Cf_sweep);
simout(iterations)  = Simulink.SimulationOutput;

% 3) Need to switch all workers to a separate tempdir in case 
% any code is generated for instance for StateFlow, or any other 
% file artifacts are  created by the model.
spmd
    % Set up tempdir and cd into it
    addpath(pwd);
    currDir = pwd;
    addpath(currDir);
    tmpDir = tempname;
    mkdir(tmpDir);
    cd(tmpDir);
    % Load the model on the worker
    load_system(model);
end

% 4) Loop over the number of iterations and perform the
% computation for different parameter values.
parfor idx=1:iterations   
    set_param([mdl '/Road-Suspension Interaction'],'MaskValues',...
        {'Kf',num2str(Cf_sweep(idx)),'Kr','Cr'});
    simout(idx) = sim(model,'SimulationMode','normal');
end

% 5) Switch all of the workers back to their original folder.
spmd
    cd(currDir);
    rmdir(tmpDir,'s');
    rmpath(currDir);
    close_system(model,0);
end

close_system(mdl,0);
delete(gcp('nocreate'));

parfor 内での sim によるラピッド アクセラレータ シミュレーション

parfor ループ内でラピッド アクセラレータ シミュレーションを実行すると、ビルド前の実行可能ファイルの並列ワーカーへの自動配布にスピードが加わります。その結果、このモードでは、ブロック線図の更新フェーズの重複がなくなります。

parfor ループ内で sim 関数を使用して並列ラピッド アクセラレータ シミュレーションを実行するには、次のようにします。

  • モデルのシミュレーション モードをラピッド アクセラレータに設定します。

  • parfor でシミュレーションを実行する前に、モデルの変更を保存します。保存されたモデルのコピーは、parfor でのシミュレーション時に並列ワーカーに配布されます。

  • ラピッ ド アクセラレータ ターゲットが既にビルドされ、最新であることを確認します。

  • sim の呼び出しで名前と値の引数 RapidAcceleratorUpToDateCheck'off' として指定して、ラピッド アクセラレータ ターゲットの最新チェックを無効にします。

2 番目の条件を満たすには、モデルのリビルドを必要としないシミュレーション間でのみパラメーターを変更できます。つまり、パラメーター値を変更するには、モデルの構造的チェックサムは不変でなければなりません。したがって、調整可能なブロック線図パラメーターと、シミュレーション間の調整可能な実行時ブロック パラメーターのみ変更可能です。変更後にリビルドを必要としない調整可能なパラメーターについては、リビルドが必要な変更かどうかの判別を参照してください。

ラピッド アクセラレータ ターゲットの最新チェックを無効にするには、このコードで示すように sim 関数を使用します。

parpool;
% Load the model and set parameters
mdl = 'vdp';
load_system(mdl);
% Build the rapid accelerator target
rtp = Simulink.BlockDiagram.buildRapidAcceleratorTarget(mdl);
% Run parallel simulations
parfor i=1:4
   simOut{i} = sim(mdl,'SimulationMode','rapid',...
               'RapidAcceleratorUpToDateCheck','off',...
               'SaveTime','on',...
               'StopTime',num2str(10*i));
   close_system(mdl,0);
delete(gcp('nocreate'));

この例では、buildRapidAcceleratorTarget 関数の呼び出しによってラピッド アクセラレータ ターゲットが一度生成されます。その後、RapidAcceleratorUpToDateCheck オプションを off にして sim を呼び出すと、ラピッド アクセラレータ ターゲットは再生成されず、データの同時実行の問題が解決されます。

ラピッド アクセラレータの最新チェックを無効にすると、[ブロック パラメーター] ダイアログ ボックスの使用、set_param 関数の使用、または MATLAB 変数の値の変更などによってモデルのブロック パラメーター値に加えた変更はシミュレーションに影響を及ぼしません。新しいパラメーター値をシミュレーションに渡すには、名前と値の引数 RapidAcceleratorParameterSets を使用します。

ワークスペースのアクセスの問題

MATLAB ワーカーのワークスペース アクセス

既定では、parfor 内で sim を呼び出すと、並列プールが自動的に開き、シミュレーションを並列で実行できます。あるいは、parpool 関数を使用して最初に MATLAB ワーカーを開くこともできます。次に、parfor 関数は、これらの MATLAB ワーカー セッションで parfor ループ内のコードを実行します。ただし、MATLAB ワーカーは、モデルとその関連ワークスペース変数が読み込まれた MATLAB クライアント セッションのワークスペースへのアクセス権がありません。したがって、モデルを読み込んで、その関連ワークスペース変数を parfor ループの外側と前で定義した場合は、モデルが読み込まれず、 parfor 反復が実行される MATLAB ワーカー セッションでワークスペース変数も定義されません。これは通常、モデルのパラメーターまたは外部入力をクライアント セッションのベース ワークスペースで定義する場合です。これらのシナリオからワークスペースのアクセスの問題が生じます。

透過性の違反

srcWorkspacecurrent に設定して parfor 内で sim を呼び出すと、シミュレーションでは透過的なワークスペースである parfor ワークスペースが使用されます。このソフトウェアは、透過性の違反に対してエラーを発行します。透過的なワークスペースの詳細については、parfor ループまたは spmd ステートメント内での透過性の確保 (Parallel Computing Toolbox)を参照してください。

データ ディクショナリのアクセス

モデルがデータ ディクショナリにリンクされている場合、ディクショナリ内に保存した変数やオブジェクトにアクセスするコードを parfor で記述するには、アクセスの問題を回避するために、関数 Simulink.data.dictionary.setupWorkerCacheSimulink.data.dictionary.cleanupWorkerCache を使用する必要があります。例については、並列シミュレーションを使用したバリアント制御のスイープを参照してください。詳細については、データ ディクショナリとはを参照してください。

ワークスペースのアクセス問題の解決

モデルが MATLAB クライアント セッションでメモリに読み込まれると、そのモデルはその MATLAB セッションでのみ表示およびアクセス可能となり、ワーカーの MATLAB セッションのメモリではアクセスできなくなります。同様に、MATLAB クライアント セッションで定義されるモデル (パラメーターや外部入力) に関連付けられているワークスペース変数は、ワーカー セッションで自動的に使用可能になることはありません。次のオプションを使用して、モデルが読み込まれ、モデルで参照されているワークスペース変数が各ワーカーの MATLAB セッションで定義されるようにしなければなりません。

  • parfor ループで、関数 sim を使用してモデルを読み込み、反復ごとに変わるパラメーターを設定します。あるいは、モデルを読み込んでから、get_param 関数および set_param 関数を使用して parfor ループ内でパラメーターを取得および設定します。

  • parfor ループで、MATLAB 関数 evalinassignin を使用して変数に値を代入します。

または、ワークスペース変数をモデル ワークスペースで定義すると、管理しやすくなります。これらの変数は、モデルがワーカー セッションに読み込まれると使用できるようになります。モデル ワークスペースの使用には制限があります。たとえば、Auto 以外のストレージ クラスを使用する Simulink.Signal オブジェクトをモデル ワークスペースに保存することはできません。モデル ワークスペースの詳細は、モデル ワークスペースを参照してください。

sim 関数の引数としてのパラメーター値の指定

反復のたびに変わるパラメーターを設定するには、parfor ループで関数 sim を使用します。

mdl = 'vdp';
load_system(mdl)
    
%Specify parameter values. 
paramName = 'StopTime';
paramValue = {'10' '20' '30' '40'};
    
% Run parallel simulations
parfor i=1:4
    simOut{i} = sim(mdl,...
                    paramName,paramValue{i},...
                    'SaveTime','on');
end
    
close_system(mdl,0);

または、モデルを読み込んでから set_param 関数を使用して parfor ループで paramName を設定する方法もあります。

assignin を使用して変数値を指定

assignin または evalin 関数を使用して、モデルまたはシミュレーションの変数の値を MATLAB ワーカーに渡すことができます。次のコードは、この手法を使用して変数値を MATLAB ワーカーの該当するワークスペースに読み込む方法を示しています。

parfor i = 1:4
    assignin('base','extInp',paramValue{i});
    simOut{i} = sim(model,'ExternalInput','extInp');
end

並列シミュレーションを使用したバリアント制御のスイープ

並列シミュレーションを使用してデータ ディクショナリに保存したバリアント制御 (値が Simulink.VariantExpression オブジェクトのバリアント条件に影響を与える Simulink.Parameter オブジェクト) をスイープするには、このコードをテンプレートとして使用します。モデル、データ ディクショナリおよびバリアント制御の名前と値をアプリケーションに一致するように変更します。

ブロック パラメーターの値、あるいはブロック パラメーターを設定するために使用するワークスペース変数の値をスイープするには、データ ディクショナリへのプログラム インターフェイスの代わりに Simulink.SimulationInput オブジェクトを使用します。ブロック パラメーター値の最適化、推定およびスイープを参照してください。

並列シミュレーションを実行するには、Parallel Computing Toolbox™ ライセンスがなければなりません。

% For convenience, define names of model and data dictionary
model = 'mySweepMdl';
dd = 'mySweepDD.sldd';

% Define the sweeping values for the variant control
CtrlValues = [1 2 3 4];

% Grant each worker in the parallel pool an independent data dictionary 
% so they can use the data without interference
spmd 
    Simulink.data.dictionary.setupWorkerCache
end

% Determine the number of times to simulate
numberOfSims = length(CtrlValues);

% Prepare a nondistributed array to contain simulation output
simOut = cell(1,numberOfSims);

parfor index = 1:numberOfSims
    % Create objects to interact with dictionary data
    % You must create these objects for every iteration of the parfor-loop
    dictObj = Simulink.data.dictionary.open(dd);
    sectObj = getSection(dictObj,'Design Data');
    entryObj = getEntry(sectObj,'MODE'); 
    % Suppose MODE is a Simulink.Parameter object stored in the data dictionary
    
    % Modify the value of MODE
    temp = getValue(entryObj);
    temp.Value = CtrlValues(index);
    setValue(entryObj,temp);

    % Simulate and store simulation output in the nondistributed array
    simOut{index} = sim(model);
    
    % Each worker must discard all changes to the data dictionary and
    % close the dictionary when finished with an iteration of the parfor-loop
    discardChanges(dictObj);
    close(dictObj);
end

% Restore default settings that were changed by the function
% Simulink.data.dictionary.setupWorkerCache
% Prior to calling cleanupWorkerCache, close the model

spmd
    bdclose(model)
    Simulink.data.dictionary.cleanupWorkerCache
end

メモ

データ ディクショナリが開いている場合は、コマンド Simulink.data.dictionary.cleanupWorkerCache を使用できません。開いているデータ ディクショナリを特定するには、Simulink.data.dictionary.getOpenDictionaryPaths を使用します。

データの同時実行の問題

データの同時実行の問題は、ソフトウェアがデータの入出力のために同じファイルに同時にアクセスしようとするシナリオに関するものです。Simulink® では、この問題は主に、同じモデルの同時シミュレーション中に parfor ループが不連続であった結果として発生します。最も一般的な事例は、並列計算中に Stateflow® チャート、参照モデル、または MATLAB Function ブロックのシミュレーション ターゲットのコードが生成または更新されるときに発生します。この場合、原因は、ソフトウェアが複数のワーカー セッションのターゲット データに同時にアクセスしようとすることです。同様に、To File ブロックも並列シミュレーション中に同じファイルにデータを記録しようとしてエラーを引き起こすことがあります。サードパーティのブロックセットまたはカスタムの S-Function が、コードやファイルの同時生成中に、データの同時実行の問題を引き起こす可能性があります。

データの同時実行の副次的な原因は、ネットワーク ポートの保護されていないアクセスです。この種のエラーは、たとえば、シミュレーション中にブロックが TCP/IP を使用して他のアプリケーションと通信する場合に発生します。そのような例の 1 つが、Siemens® ModelSim™ HDL シミュレーターで使用する HDL Verifier™ です。

データの同時実行の問題の解決

parfor の主要件は、parfor 本体の異なる反復の独立性です。この制約は、現在のシミュレーションに以前のシミュレーションのシミュレーション ターゲットが再利用または更新されるインクリメンタル コード生成を使用したシミュレーションの主要件に合いません。シミュレーション ターゲットの生成を伴うモデルの並列シミュレーション (アクセラレータ モードのシミュレーションなど) 中に、シミュレーション ターゲットに対する同時実行アクセス (更新) が試行されます。ただし、このようなデータの同時実行の問題は、一時フォルダーを parfor ループ内に作成し、複数行の MATLAB コードをループに追加して次の手順を実行することで避けることができます。

  1. 現在のフォルダーを書き込み可能な一時フォルダーに変更します。

  2. 一時フォルダーで、モデルを読み込み、パラメーターと入力ベクトルを設定して、モデルのシミュレーションを実行します。

  3. 最初の現在のフォルダーに戻ります。

  4. 一時フォルダーと一時パスを削除します。

このようにして個別の一時フォルダーにモデルを読み込んでシミュレートすることで、同時実行の問題を避けます。以下は、この方法で一般的な同時実行の問題を解決する例です。

Stateflow チャート、MATLAB Function ブロック、またはモデル参照を含むモデル

この例では、モデルがアクセラレータ モードでシミュレーションを実行するように構成されているか、1 つ以上の Stateflow チャート、MATLAB Function ブロック、またはモデル参照が含まれています。このようなモデルの例には、sf_bouncesldemo_autotrans などがあります。このような場合、ソフトウェアはシミュレーションの初期化フェーズ中にシミュレーション ターゲットを生成します。このようなモデルを parfor ループでシミュレーションすると、ワーカー セッションで初期化フェーズが実行されている間に、各シミュレーションで同じファイルを使用して同時にシミュレーション ターゲットが生成されます。次のコードに示されているように、parfor 本体の各反復を異なる一時フォルダーで実行することで、このようなデータの同時実行の問題を回避できます。

parfor i=1:4
   cwd = pwd;
   addpath(cwd)
   tmpdir = tempname;
   mkdir(tmpdir)
   cd(tmpdir)
   load_system(mdl)
   % set the block parameters, such as filename for To File block
   set_param(someBlkInMdl,blkParamName,blkParamValue{i})
   % set the model parameters by passing them to the sim function
   out{i} = sim(mdl,mdlParamName,mdlParamValue{i});
   close_system(mdl,0);
   cd(cwd)
   rmdir(tmpdir,'s')
   rmpath(cwd)
end

また、ファイルの I/O エラーによる他の同時実行の問題も、parfor 本体の各反復に一時フォルダーを使用すると回避できます。

Windows® プラットフォームでは、コマンド rmdir(tmpdir, 's') の前にコマンド evalin('base','clear mex'); を挿入することを検討してください。この順序では、まず MEX ファイルを閉じてから、rmdir を呼び出して一時ディレクトリ tmpdir を削除します。

evalin('base','clear mex');
rmdir(tmpdir,'s')

To File ブロックを含むモデル

parfor ループ内に 1 つ以上の To File ブロックを含むモデルをシミュレーションすると、ループの不連続性によってファイル I/O エラーが発生する可能性があります。並列シミュレーション中のこのようなエラーを回避するには、一時フォルダーの解決策を使用するか、To File ブロック パラメーターで指定されたファイル名に接尾辞を追加するオプションを使用して、シミュレーションをラピッド アクセラレータ モードで実行します。parfor 本体の反復ごとに一意の接尾辞を与えると、同時実行の問題を回避できます。

rtp = Simulink.BlockDiagram.buildRapidAcceleratorTarget(mdl); 
       parfor idx=1:4 
       sim(mdl,... 
           'ConcurrencyResolvingToFileSuffix',num2str(idx),... 
           'SimulationMode','rapid',... 
           'RapidAcceleratorUpToDateCheck','off'); 
        end

参考

トピック