Операция умножения матриц к MathWorks BLAS Code Replacement

Этот пример показывает, как заменить операции матрицы/матрицы и матрицы/векторного умножения с плавающей точкой на функции умножения dgemm и dgemv, заданный в библиотеке MathWorks BLAS. Если вы будете пользоваться сторонней библиотекой BLAS для замены, необходимо будет изменить требования сборки в этом примере, чтобы указать на библиотеку. Этот пример задает функциональные отображения программно. Также можно использовать Code Replacement Tool, чтобы задать те же отображения.

Заменяющие библиотеки кода только поддерживают ограниченные случаи от библиотек BLAS. Таблица поддержки библиотек BLAS / умножение матриц в форме  C = (op (A) * op (B)) + до н.э. Где выражение op (X) представляет или перемещение или Эрмитово перемещение X. Однако заменяющие библиотеки кода только поддерживают ограниченный случай  C = op (A) * op (B) ( = 1.0, b = 0.0). Кроме того, таблица поддержки библиотек BLAS / векторное умножение в форме  y = (op (A) * x) + , в то время как заменяющие библиотеки кода только поддерживают ограниченный случай  y = op (A) * x ( = 1.0, b = 0.0).

  1. Создайте табличный файл определения, который содержит функциональное определение. Например:

    function hTable = crl_table_tmwblas_mmult_double
  2. В теле функции составьте таблицу путем вызова функционального RTW.TflTable.

    hTable = RTW.TflTable;
    
  3. Задайте путь для библиотеки функции BLAS. Если ваши заменяющие функции находятся на пути поиска файлов MATLAB® или находятся в вашей рабочей папке, можно пропустить этот шаг.

    % Define library path for Windows or UNIX
    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
    
  4. Создайте запись для первого отображения с вызовом функции RTW.TflBlasEntryGenerator.

    % Create table entry for dgemm32
    op_entry = RTW.TflBlasEntryGenerator;
    
  5. Вызовите setTflCFunctionEntryParameters, чтобы установить параметры записи оператора. Для нескалярного сложения с плавающей точкой и вычитания, генератор кода игнорирует насыщение и округление режимов. Для нескалярного сложения и операций вычитания, которые не включают данные фиксированной точки, задайте SaturationMode как 'RTW_SATURATE_UNSPECIFIED' и RoundingModes как {'RTW_ROUND_UNSPECIFIED'}.

    if ispc
        libExt = 'lib';
    elseif ismac
        libExt = 'dylib';
    else
        libExt = 'so';
    end
    setTflCOperationEntryParameters(op_entry, ...
        '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);
    
  6. Создайте концептуальные аргументы y1, u1 и u2. Существует несколько способов настроить концептуальные аргументы. Этот пример использует вызовы функции createAndAddConceptualArg, чтобы создать и добавить аргумент с одним вызовом функции. Чтобы задать матричный аргумент в вызове функции, используйте класс аргумента RTW.TflArgMatrix и задайте базовый тип и размерности, для которых аргумент допустим. Этот тип записи таблицы поддерживает область значений размерностей, заданных в формате [Dim1Min Dim2Min ... DimNMin; Dim1Max Dim2Max ... DimNMax]. Например, [2 2; inf inf] означает двумерную матрицу размера 2x2 или больше. Концептуальный выходной аргумент в пользу записи dgemm32 для замены матрицы/умножения матриц задает размерности [2 2; inf inf], в то время как концептуальный выходной аргумент в пользу записи dgemv32 для замены матрицы/векторного умножения задает размерности [2 1; inf 1].

    % Specify operands and result
    createAndAddConceptualArg(op_entry, 'RTW.TflArgMatrix', ...
                              'Name',         'y1', ...
                              'IOType',       'RTW_IO_OUTPUT', ...
                              'BaseType',     'double', ...
                              'DimRange',     [2 2; inf inf]);
    createAndAddConceptualArg(op_entry, 'RTW.TflArgMatrix', ...
                              'Name',         'u1', ...
                              'BaseType',     'double', ...
                              'DimRange',     [2 2; inf inf]);
    createAndAddConceptualArg(op_entry, 'RTW.TflArgMatrix', ...
                              'Name',         'u2', ...
                              'BaseType',     'double', ...
                              'DimRange',     [1 1; inf inf]);
    
  7. Создайте аргументы реализации. Существует несколько способов настроить аргументы реализации. Этот пример использует вызовы getTflArgFromString и функций RTW.TflArgCharConstant, чтобы создать аргументы. Пример кода конфигурирует специальные аргументы реализации, которые требуются для dgemm и замен функции dgemv. setReturn удобных методов и addArgument задают, является ли аргумент возвращаемым значением или аргументом и добавляет аргумент в массив записи аргументов реализации.

    % 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';
    op_entry.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';
    op_entry.Implementation.addArgument(arg);
    
    arg = RTW.TflArgCharConstant('TRANSB');
    arg.PassByType = 'RTW_PASSBY_POINTER';
    op_entry.Implementation.addArgument(arg);
    
    arg = getTflArgFromString(hTable, 'M', 'integer', 0);
    arg.PassByType = 'RTW_PASSBY_POINTER';
    arg.Type.ReadOnly = true;
    op_entry.Implementation.addArgument(arg);
    
    arg = getTflArgFromString(hTable, 'N', 'integer', 0);
    arg.PassByType = 'RTW_PASSBY_POINTER';
    arg.Type.ReadOnly = true;
    op_entry.Implementation.addArgument(arg);
    
    arg = getTflArgFromString(hTable, 'K', 'integer', 0);
    arg.PassByType = 'RTW_PASSBY_POINTER';
    arg.Type.ReadOnly = true;
    op_entry.Implementation.addArgument(arg);
    
    arg = getTflArgFromString(hTable, 'ALPHA', 'double', 0);
    arg.PassByType = 'RTW_PASSBY_POINTER';
    arg.Type.ReadOnly = true;
    op_entry.Implementation.addArgument(arg);
    
    arg = getTflArgFromString(hTable, 'u1', ['double' '*']);
    arg.PassByType = 'RTW_PASSBY_POINTER';
    arg.Type.ReadOnly = true;
    op_entry.Implementation.addArgument(arg);
    
    arg = getTflArgFromString(hTable, 'LDA', 'integer', 0);
    arg.PassByType = 'RTW_PASSBY_POINTER';
    arg.Type.ReadOnly = true;
    op_entry.Implementation.addArgument(arg);
    
    arg = getTflArgFromString(hTable, 'u2', ['double' '*']);
    arg.PassByType = 'RTW_PASSBY_POINTER';
    arg.Type.ReadOnly = true;
    op_entry.Implementation.addArgument(arg);
    
    arg = getTflArgFromString(hTable, 'LDB', 'integer', 0);
    arg.PassByType = 'RTW_PASSBY_POINTER';
    arg.Type.ReadOnly = true;
    op_entry.Implementation.addArgument(arg);
    
    arg = getTflArgFromString(hTable, 'BETA', 'double', 0);
    arg.PassByType = 'RTW_PASSBY_POINTER';
    arg.Type.ReadOnly = true;
    op_entry.Implementation.addArgument(arg);
    
    arg = getTflArgFromString(hTable, 'y1', ['double' '*']);
    arg.IOType = 'RTW_IO_OUTPUT';
    arg.PassByType = 'RTW_PASSBY_POINTER';
    op_entry.Implementation.addArgument(arg);
    
    arg = getTflArgFromString(hTable, 'LDC', 'integer', 0);
    arg.PassByType = 'RTW_PASSBY_POINTER';
    arg.Type.ReadOnly = true;
    op_entry.Implementation.addArgument(arg);
    
  8. Добавьте запись в заменяющую таблицу кода с вызовом функции addEntry.

    addEntry(hTable, op_entry);
  9. Создайте запись для второго отображения.

    % Create table entry for dgemv32
    op_entry = RTW.TflBlasEntryGenerator;
    if ispc
        libExt = 'lib';
    elseif ismac
        libExt = 'dylib';
    else
        libExt = 'so';
    end
    setTflCOperationEntryParameters(op_entry, ...
        'Key',                      'RTW_OP_MUL', ...
        'Priority',                 100, ...
        'ImplementationName',       'dgemv32', ...
        'ImplementationHeaderFile', 'blascompat32_crl.h', ...
        'ImplementationHeaderPath', fullfile('$(MATLAB_ROOT)','extern','include'), ...
        'AdditionalLinkObjs',       {['libmwblascompat32.' libExt]}, ...
        'AdditionalLinkObjsPaths',  {LibPath},...
        'SideEffects',              true);
    
    % Specify operands and result
    createAndAddConceptualArg(op_entry, 'RTW.TflArgMatrix', ...
                              'Name',         'y1', ...
                              'IOType',       'RTW_IO_OUTPUT', ...
                              'BaseType',     'double', ...
                              'DimRange',     [2 1; inf 1]);
    createAndAddConceptualArg(op_entry, 'RTW.TflArgMatrix', ...
                              'Name',         'u1', ...
                              'BaseType',     'double', ...
                              'DimRange',     [2 2; inf inf]);
    createAndAddConceptualArg(op_entry, 'RTW.TflArgMatrix',...
                              'Name',         'u2', ...
                              'BaseType',     'double', ...
                              'DimRange',     [1 1; inf 1]);
    
    % Using RTW.TflBlasEntryGenerator for xgemv requires the following
    % implementation signature:
    %
    % void f(char* TRANS, int* M, int* N,
    %        type* ALPHA, type* u1, int* LDA, type* u2, int* INCX,
    %        type* BETA, type* y, int* INCY)
    %
    % Upon a match, the CRL entry will compute the
    % values for M, N, LDA, INCX, and INCY, and insert them into the
    % generated code. TRANS will be set to 'N'.
    
    % Specify replacement function signature
    
    arg = getTflArgFromString(hTable, 'y2', 'void');
    arg.IOType = 'RTW_IO_OUTPUT';
    op_entry.Implementation.setReturn(arg);
    
    arg = RTW.TflArgCharConstant('TRANS');
    arg.PassByType = 'RTW_PASSBY_POINTER';
    op_entry.Implementation.addArgument(arg);
    
    arg = getTflArgFromString(hTable, 'M', 'integer', 0);
    arg.PassByType = 'RTW_PASSBY_POINTER';
    arg.Type.ReadOnly = true;
    op_entry.Implementation.addArgument(arg);
    
    arg = getTflArgFromString(hTable, 'N', 'integer', 0);
    arg.PassByType = 'RTW_PASSBY_POINTER';
    arg.Type.ReadOnly = true;
    op_entry.Implementation.addArgument(arg);
    
    arg = getTflArgFromString(hTable, 'ALPHA', 'double', 0);
    arg.PassByType = 'RTW_PASSBY_POINTER';
    arg.Type.ReadOnly = true;
    op_entry.Implementation.addArgument(arg);
    
    arg = getTflArgFromString(hTable, 'u1', ['double' '*']);
    arg.PassByType = 'RTW_PASSBY_POINTER';
    arg.Type.ReadOnly = true;
    op_entry.Implementation.addArgument(arg);
    
    arg = getTflArgFromString(hTable, 'LDA', 'integer', 0);
    arg.PassByType = 'RTW_PASSBY_POINTER';
    arg.Type.ReadOnly = true;
    op_entry.Implementation.addArgument(arg);
    
    arg = getTflArgFromString(hTable, 'u2', ['double' '*']);
    arg.PassByType = 'RTW_PASSBY_POINTER';
    arg.Type.ReadOnly = true;
    op_entry.Implementation.addArgument(arg);
    
    arg = getTflArgFromString(hTable, 'INCX','integer', 0);
    arg.PassByType = 'RTW_PASSBY_POINTER';
    arg.Type.ReadOnly = true;
    op_entry.Implementation.addArgument(arg);
    
    arg = getTflArgFromString(hTable, 'BETA', 'double', 0);
    arg.PassByType = 'RTW_PASSBY_POINTER';
    arg.Type.ReadOnly = true;
    op_entry.Implementation.addArgument(arg);
    
    arg = getTflArgFromString(hTable, 'y1', ['double' '*']);
    arg.IOType = 'RTW_IO_OUTPUT';
    arg.PassByType = 'RTW_PASSBY_POINTER';
    op_entry.Implementation.addArgument(arg);
    
    arg = getTflArgFromString(hTable, 'INCY', 'integer', 0);
    arg.PassByType = 'RTW_PASSBY_POINTER';
    arg.Type.ReadOnly = true;
    op_entry.Implementation.addArgument(arg);
    
    addEntry(hTable, op_entry);
  10. Сохраните табличный файл определения. Используйте имя табличной функции определения, чтобы назвать файл.

Протестировать этот пример:

  1. Укажите заменяющее отображение кода.

  2. Создайте модель, которая включает два блока продукта.

  3. Для каждого блока продукта, набор параметры блоков Multiplication к значению Matrix(*).

  4. Сконфигурируйте модель со следующими настройками:

    • На панели Solver выберите фиксированный шаг, дискретный решатель с размером фиксированного шага, таким как 0.1.

    • На панели Code Generation выберите ERT-based system target file.

    • На Code Generation> панель Interface, выберите заменяющую библиотеку кода, которая содержит вашу запись операции сложения.

  5. В Model Explorer сконфигурируйте Signal Attributes для In1, In2 и исходных блоков In3. Для In1 и In2, набора Port dimensions к [3 3] и набору Data type к double. Для In3, набор Port dimensions к [3 1] и набору Data type к double.

  6. Сгенерируйте код и отчет генерации кода.

  7. Рассмотрите замены кода.

Похожие темы