メインコンテンツ

行列乗算演算から MathWorks BLAS へのコード置換

浮動小数点の行列/行列乗算演算と行列/ベクトル乗算演算を MathWorks BLAS ライブラリで定義されている乗算関数 dgemm および dgemv に置き換えるためのコード置換ライブラリを開発できます。サードパーティの置換用 BLAS ライブラリを使用する場合は、そのライブラリを指すようにこの例のビルド要件を変更する必要があります。コード置換ライブラリを開発するには、対話的な方法またはプログラムによる方法のいずれかを使用します。詳細については、コード置換ライブラリの開発を参照してください。

対話的なコード置換ライブラリの開発

  1. MATLAB コマンド ラインから次のコマンドを使用して コード置換ツール (crtool) を開きます。

    >>crtool
  2. table を作成します。

    1. コード置換ツールのツールストリップから、[新規][テーブル] をクリックします。

    2. 右側のペインで、table に crl_table_blas と名前を付けます。[適用] をクリックします。

  3. エントリを作成します。[新規][エントリ][BLAS 演算エントリ] をクリックします。

  4. エントリ パラメーターを作成します。[関数] ドロップダウン リストで、Multiply を選択します。

  5. 概念表現を作成します。概念表現は、置換する関数のシグネチャを記述します。crtool の [概念関数] サブセクションで、double の [データ型] および行列の [引数タイプ] をもつ戻り引数 y1 と入力引数 u1 および u2 を指定します。

  6. 実装表現を作成します。実装表現は最適化関数のシグネチャを記述します。この例では、概念引数と同じ順序およびプロパティをもつ実装引数を指定するために、[概念引数と実装引数のタイプを同じにする] チェック ボックスをオンにします。

    置換関数の [名前]dgemm32 を指定します。

    [BLAS レベル]2(Vector) に設定します。

  7. ビルド情報を指定します。[ビルド情報] タブをクリックして、ビルド要件のペインを開きます。コード ジェネレーターがコード置換に必要なファイル (ソース、ヘッダー、オブジェクト) を指定します。この例では、ビルド情報を指定する必要はありません。

  8. table を確認して保存します。[マッピング情報] タブをクリックし、フィールドが以下に示すように入力されていることを確認します。[適用] をクリックし、[エントリの検証] をクリックします。ツールストリップで [保存] をクリックして table を保存します。

    Code replacement tool showing a function entry for the multiply operation.

  9. コード置換ライブラリを登録します。登録することにより、指定した table で構成されるライブラリが作成されます。[登録ファイルの作成] をクリックします。[登録ファイルの作成] ダイアログ ボックスで、以下のフィールドに入力します。

    • レジストリ名Matrix Multiplication Operation to Mathworks BLAS code

    • テーブル リストcrl_table_blas

    • 基本 CRLNone

    • ターゲット HW デバイス*

    • 説明Example code replacement library

    Generate registration file dialog box showing filled in fields.

    コード置換ライブラリを使用するには、現在の MATLAB セッションを次のコマンドで更新します。

    >>sl_refresh_customizations

  10. コード置換ライブラリを確認します。MATLAB コマンド ラインから、コード置換ビューアーを使用してライブラリを開き、table およびエントリが正しく指定されていることを確認します。詳細については、Verify Code Replacement Libraryを参照してください。コード置換ライブラリを使用するようにモデルを構成し、コードを生成して、その置換が予期したとおりに行われることを確認します。予期しない動作が行われた場合、ヒット ログとミス ログを調べて問題のトラブルシューティングを行います。

プログラムによるコード置換ライブラリの開発

  1. [新規][関数] を選択し、[MATLAB] メニューからプログラム インターフェイスを開きます。

  2. table を作成します。

    1. コード置換ライブラリ テーブルを呼び出す関数を作成します。この関数は引数をもつべきではなく、table オブジェクトを返します。

    2. RTW.TflTable を呼び出して table オブジェクトを作成します。

    function hTable = crl_table_blas
    % Create a function to call the code replacement library table 
    
    %% Create a table object
    hTable = RTW.TflTable;
    
  3. エントリを作成します。この例では関数を置き換えるため、エントリ関数 RTW.TflBlasEntryGenerator を呼び出して table にコード置換エントリを作成します。

    function hTable = crl_table_blas
    % Create a code replacement library table 
    
    %% Create a table object
    hTable = RTW.TflTable;
    
    %% Create an entry
    %%%%%%%%%%%% Define library path for Windows or UNIX block%%%%%%%%%%%%%%%%%%
    
    arch = computer('arch');
    if ~ispc
        LibPath = fullfile('$(MATLAB_ROOT)', 'bin', arch);
    else
        % Use Stateflow to get the compiler info
        compilerInfo = sf('Private','compilerman','get_compiler_info');
        compilerName = compilerInfo.compilerName;
        if strcmp(compilerName, 'msvc90') || ...
                strcmp(compilerName, 'msvc80') || ...
                strcmp(compilerName, 'msvc71') || ...
                strcmp(compilerName, 'msvc60'), ...
                compilerName = 'microsoft';
        end
        LibPath = fullfile('$(MATLAB_ROOT)', 'extern', 'lib', arch, compilerName);
    end
    
    
    if ispc
        libExt = 'lib';
    elseif ismac
        libExt = 'dylib';
    else
        libExt = 'so';
    end
    
    
    %%%%%%Beginning of entries for the first dgemm block%%%%%%%%%%%%%%%%%%
    
    
    % Create table entry for dgemm32
    hEntry = RTW.TflBlasEntryGenerator;
  4. エントリ パラメーターを作成します。この例では関数を置き換えるため、関数 setTflCFunctionEntryParameters を呼び出してエントリ パラメーターを作成します。

    function hTable = crl_table_blas
    % Create a code replacement library table 
    
    %% Create a table object
    hTable = RTW.TflTable;
    
    %% Create an entry
    %%%%%%%%%%%% Define library path for Windows or UNIX block%%%%%%%%%%%%%%%%%%
    
    arch = computer('arch');
    if ~ispc
        LibPath = fullfile('$(MATLAB_ROOT)', 'bin', arch);
    else
        % Use Stateflow to get the compiler info
        compilerInfo = sf('Private','compilerman','get_compiler_info');
        compilerName = compilerInfo.compilerName;
        if strcmp(compilerName, 'msvc90') || ...
                strcmp(compilerName, 'msvc80') || ...
                strcmp(compilerName, 'msvc71') || ...
                strcmp(compilerName, 'msvc60'), ...
                compilerName = 'microsoft';
        end
        LibPath = fullfile('$(MATLAB_ROOT)', 'extern', 'lib', arch, compilerName);
    end
    
    
    if ispc
        libExt = 'lib';
    elseif ismac
        libExt = 'dylib';
    else
        libExt = 'so';
    end
    
    
    %%%%%%Beginning of entries for the first dgemm block%%%%%%%%%%%%%%%%%%
    
    
    % Create table entry for dgemm32
    hEntry = RTW.TflBlasEntryGenerator;
    
    %% Create entry parameters
    setTflCOperationEntryParameters(hEntry, ...
        'Key',                      'RTW_OP_MUL', ...
        'Priority',                 100, ...
        'ImplementationName',       'dgemm32', ...
        'ImplementationHeaderFile', 'blascompat32_crl.h', ...
        'ImplementationHeaderPath', fullfile('$(MATLAB_ROOT)','extern','include'), ...
        'AdditionalLinkObjs',       {['libmwblascompat32.' libExt]}, ...
        'AdditionalLinkObjsPaths',  {LibPath}, ...
        'SideEffects',              true);
  5. 概念表現を作成します。概念表現は、置換する関数のシグネチャを記述します。引数プロパティを明示的に指定するには、関数 createAndAddConceptualArg を呼び出します。関数呼び出しで行列引数を指定するには、引数クラス RTW.TflArgMatrix を使用して、基本データ型と引数が有効な次元を指定します。このような table エントリでは、 [Dim1Min Dim2Min ... DimNMin; Dim1Max Dim2Max ... DimNMax] という形式で指定される次元の範囲がサポートされます。たとえば、[2 2; inf inf] は、サイズが 2 行 2 列以上の 2 次元行列を意味します。行列/行列の乗算の置換に対する dgemm32 エントリの概念出力引数では次元 [2 2; inf inf] を指定し、行列/ベクトルの乗算の置換に対する dgemv32 エントリの概念出力引数では次元 [2 1; inf 1] を指定します。

    function hTable = crl_table_blas
    % Create a code replacement library table 
    
    %% Create a table object
    hTable = RTW.TflTable;
    
    %% Create an entry
    %%%%%%%%%%%% Define library path for Windows or UNIX block%%%%%%%%%%%%%%%%%%
    
    arch = computer('arch');
    if ~ispc
        LibPath = fullfile('$(MATLAB_ROOT)', 'bin', arch);
    else
        % Use Stateflow to get the compiler info
        compilerInfo = sf('Private','compilerman','get_compiler_info');
        compilerName = compilerInfo.compilerName;
        if strcmp(compilerName, 'msvc90') || ...
                strcmp(compilerName, 'msvc80') || ...
                strcmp(compilerName, 'msvc71') || ...
                strcmp(compilerName, 'msvc60'), ...
                compilerName = 'microsoft';
        end
        LibPath = fullfile('$(MATLAB_ROOT)', 'extern', 'lib', arch, compilerName);
    end
    
    
    if ispc
        libExt = 'lib';
    elseif ismac
        libExt = 'dylib';
    else
        libExt = 'so';
    end
    
    
    %%%%%%Beginning of entries for the first dgemm block%%%%%%%%%%%%%%%%%%
    
    
    % Create table entry for dgemm32
    hEntry = RTW.TflBlasEntryGenerator;
    
    %% Create entry parameters
    setTflCOperationEntryParameters(hEntry, ...
        'Key',                      'RTW_OP_MUL', ...
        'Priority',                 100, ...
        'ImplementationName',       'dgemm32', ...
        'ImplementationHeaderFile', 'blascompat32_crl.h', ...
        'ImplementationHeaderPath', fullfile('$(MATLAB_ROOT)','extern','include'), ...
        'AdditionalLinkObjs',       {['libmwblascompat32.' libExt]}, ...
        'AdditionalLinkObjsPaths',  {LibPath}, ...
        'SideEffects',              true);
    
    %% Create the conceptual representation
    createAndAddConceptualArg(hEntry, 'RTW.TflArgMatrix', ...
                              'Name',         'y1', ...
                              'IOType',       'RTW_IO_OUTPUT', ...
                              'BaseType',     'double', ...
                              'DimRange',     [2 2; inf inf]);
    createAndAddConceptualArg(hEntry, 'RTW.TflArgMatrix', ...
                              'Name',         'u1', ...
                              'BaseType',     'double', ...
                              'DimRange',     [2 2; inf inf]);
    createAndAddConceptualArg(hEntry, 'RTW.TflArgMatrix', ...
                              'Name',         'u2', ...
                              'BaseType',     'double', ...
                              'DimRange',     [1 1; inf inf]);
  6. 実装表現を作成します。実装表現は最適化関数のシグネチャを記述します。実装引数が概念引数と同じ順序とプロパティをもつように指定するには、関数 getTflArgFromString を呼び出します。コード例では、関数 dgemm と関数 dgemv への置き換えに必要となる特別な実装引数を設定します。便利なメソッド setReturn および addArgument で、引数が戻り値か引数かを指定し、エントリの実装引数の配列に引数を追加します。関数 addEntry を呼び出して、table に完全なエントリを追加します。

    function hTable = crl_table_blas
    % Create a code replacement library table 
    
    %% Create a table object
    hTable = RTW.TflTable;
    
    %% Create an entry
    %%%%%%%%%%%% Define library path for Windows or UNIX block%%%%%%%%%%%%%%%%%%
    
    arch = computer('arch');
    if ~ispc
        LibPath = fullfile('$(MATLAB_ROOT)', 'bin', arch);
    else
        % Use Stateflow to get the compiler info
        compilerInfo = sf('Private','compilerman','get_compiler_info');
        compilerName = compilerInfo.compilerName;
        if strcmp(compilerName, 'msvc90') || ...
                strcmp(compilerName, 'msvc80') || ...
                strcmp(compilerName, 'msvc71') || ...
                strcmp(compilerName, 'msvc60'), ...
                compilerName = 'microsoft';
        end
        LibPath = fullfile('$(MATLAB_ROOT)', 'extern', 'lib', arch, compilerName);
    end
    
    
    if ispc
        libExt = 'lib';
    elseif ismac
        libExt = 'dylib';
    else
        libExt = 'so';
    end
    
    
    %%%%%%Beginning of entries for the first dgemm block%%%%%%%%%%%%%%%%%%
    
    
    % Create table entry for dgemm32
    hEntry = RTW.TflBlasEntryGenerator;
    
    %% Create entry parameters
    setTflCOperationEntryParameters(hEntry, ...
        'Key',                      'RTW_OP_MUL', ...
        'Priority',                 100, ...
        'ImplementationName',       'dgemm32', ...
        'ImplementationHeaderFile', 'blascompat32_crl.h', ...
        'ImplementationHeaderPath', fullfile('$(MATLAB_ROOT)','extern','include'), ...
        'AdditionalLinkObjs',       {['libmwblascompat32.' libExt]}, ...
        'AdditionalLinkObjsPaths',  {LibPath}, ...
        'SideEffects',              true);
    
    %% Create the conceptual representation
    createAndAddConceptualArg(hEntry, 'RTW.TflArgMatrix', ...
                              'Name',         'y1', ...
                              'IOType',       'RTW_IO_OUTPUT', ...
                              'BaseType',     'double', ...
                              'DimRange',     [2 2; inf inf]);
    createAndAddConceptualArg(hEntry, 'RTW.TflArgMatrix', ...
                              'Name',         'u1', ...
                              'BaseType',     'double', ...
                              'DimRange',     [2 2; inf inf]);
    createAndAddConceptualArg(hEntry, 'RTW.TflArgMatrix', ...
                              'Name',         'u2', ...
                              'BaseType',     'double', ...
                              'DimRange',     [1 1; inf inf]);
    
    %% Create the Implementation Representation
    % Using RTW.TflBlasEntryGenerator for xgemm requires the following
    % implementation signature:
    %
    % void f(char* TRANSA, char* TRANSB, int* M, int* N, int* K,
    %        type* ALPHA, type* u1, int* LDA, type* u2, int* LDB,
    %        type* BETA, type* y, int* LDC)
    %
    % When a match occurs, the code generator computes the
    % values for M, N, K, LDA, LDB, and LDC and inserts them into the
    % generated code. TRANSA and TRANSB are set to 'N'.
    
    % Specify replacement function signature
    
    arg = getTflArgFromString(hTable, 'y2', 'void');
    arg.IOType = 'RTW_IO_OUTPUT';
    hEntry.Implementation.setReturn(arg);
    
    arg = RTW.TflArgCharConstant('TRANSA');
    % Possible values for PassByType property are
    %  RTW_PASSBY_AUTO, RTW_PASSBY_POINTER,
    %  RTW_PASSBY_VOID_POINTER, RTW_PASSBY_BASE_POINTER
    arg.PassByType = 'RTW_PASSBY_POINTER';
    hEntry.Implementation.addArgument(arg);
    
    arg = RTW.TflArgCharConstant('TRANSB');
    arg.PassByType = 'RTW_PASSBY_POINTER';
    hEntry.Implementation.addArgument(arg);
    
    arg = getTflArgFromString(hTable, 'M', 'integer', 0);
    arg.PassByType = 'RTW_PASSBY_POINTER';
    arg.Type.ReadOnly = true;
    hEntry.Implementation.addArgument(arg);
    
    arg = getTflArgFromString(hTable, 'N', 'integer', 0);
    arg.PassByType = 'RTW_PASSBY_POINTER';
    arg.Type.ReadOnly = true;
    hEntry.Implementation.addArgument(arg);
    
    arg = getTflArgFromString(hTable, 'K', 'integer', 0);
    arg.PassByType = 'RTW_PASSBY_POINTER';
    arg.Type.ReadOnly = true;
    hEntry.Implementation.addArgument(arg);
    
    arg = getTflArgFromString(hTable, 'ALPHA', 'double', 0);
    arg.PassByType = 'RTW_PASSBY_POINTER';
    arg.Type.ReadOnly = true;
    hEntry.Implementation.addArgument(arg);
    
    arg = getTflArgFromString(hTable, 'u1', ['double' '*']);
    arg.PassByType = 'RTW_PASSBY_POINTER';
    arg.Type.ReadOnly = true;
    hEntry.Implementation.addArgument(arg);
    
    arg = getTflArgFromString(hTable, 'LDA', 'integer', 0);
    arg.PassByType = 'RTW_PASSBY_POINTER';
    arg.Type.ReadOnly = true;
    hEntry.Implementation.addArgument(arg);
    
    arg = getTflArgFromString(hTable, 'u2', ['double' '*']);
    arg.PassByType = 'RTW_PASSBY_POINTER';
    arg.Type.ReadOnly = true;
    hEntry.Implementation.addArgument(arg);
    
    arg = getTflArgFromString(hTable, 'LDB', 'integer', 0);
    arg.PassByType = 'RTW_PASSBY_POINTER';
    arg.Type.ReadOnly = true;
    hEntry.Implementation.addArgument(arg);
    
    arg = getTflArgFromString(hTable, 'BETA', 'double', 0);
    arg.PassByType = 'RTW_PASSBY_POINTER';
    arg.Type.ReadOnly = true;
    hEntry.Implementation.addArgument(arg);
    
    arg = getTflArgFromString(hTable, 'y1', ['double' '*']);
    arg.IOType = 'RTW_IO_OUTPUT';
    arg.PassByType = 'RTW_PASSBY_POINTER';
    hEntry.Implementation.addArgument(arg);
    
    arg = getTflArgFromString(hTable, 'LDC', 'integer', 0);
    arg.PassByType = 'RTW_PASSBY_POINTER';
    arg.Type.ReadOnly = true;
    hEntry.Implementation.addArgument(arg);
    
    
    %% Add the entry to the table
    addEntry(hTable, hEntry);
  7. ビルド情報を指定します。エントリ パラメーターで、コード ジェネレーターがコード置換に必要なファイル (ソース、ヘッダー、オブジェクト) を指定します。この例では、ビルド情報は必要ありません。

  8. カスタマイズ ファイルを確認して保存します。[MATLAB] メニューから、[ファイル][保存] を選択して、このカスタマイズ ファイルを保存します。コマンド ラインから、次のようにしてコード置換ライブラリ テーブルを呼び出し、確認します。

    >> hTable = crl_table_blas
  9. コード置換ライブラリを登録します。登録では、ライブラリ名、コード置換テーブル、その他の情報を定義することにより、コード置換ライブラリが作成されます。以下の仕様を使用して登録ファイルを作成します。

    function rtwTargetInfo(cm)
     
    cm.registerTargetInfo(@loc_register_crl);
    end
     
    function this = loc_register_crl 
     
    this(1) = RTW.TflRegistry; 
    this(1).Name = 'CRL for matrix multiplication for Mathworks Blas code';
    this(1).TableList = {'crl_table_blas.m'}; % table created in this example
    this(1).TargetHWDeviceType = {'*'};
    this(1).Description = 'Example code replacement library';
    
    end
    

    コード置換ライブラリを使用するには、現在の MATLAB セッションを次のコマンドで更新します。

    >>sl_refresh_customizations

  10. コード置換ライブラリを確認します。MATLAB コマンド ラインから、コード置換ビューアーを使用してライブラリを開き、table およびエントリが正しく指定されていることを確認します。詳細については、Verify Code Replacement Libraryを参照してください。コード置換ライブラリを使用するようにモデルを構成し、コードを生成して、その置換が予期したとおりに行われることを確認します。予期しない動作が行われた場合、ヒット ログとミス ログを調べて問題のトラブルシューティングを行います。

参考

トピック