主要内容

使用 MATLAB 探查器探查 MEX 函数

您可以使用 MATLAB® 探查器来探查 MATLAB Coder™ 生成的 MEX 函数的执行时间。所生成代码的探查文件显示对应 MATLAB 函数的调用次数和运行每行代码所花费的时间。使用探查器可识别产生耗时最长的生成代码的 MATLAB 代码行。这些信息可以帮助您在开发周期的早期确定和更正性能问题。有关 MATLAB 探查器的详细信息,请参阅 profile探查您的代码以改善性能

MATLAB Online™ 不支持探查器的图形界面。

生成 MEX 探查文件

您可以在命令行中以交互方式使用生成的 MEX 函数运行 MATLAB 探查器。或者,如果您有调用 MATLAB 函数的测试文件,您可以使用 codegen 命令或 MATLAB Coder 生成 MEX 函数并一步完成探查。

如果探查器正在运行,您必须在生成新探查之前将其关闭。要关闭探查器,请使用以下命令:

profile off;

以交互方式运行 MATLAB 探查器

如果您没有编写调用 MATLAB 函数的测试文件,则必须以交互方式运行探查器。要生成适合探查的 MEX 函数,请使用以下方法之一:

  • 在命令行上,创建一个 MEX 代码配置对象。将 EnableMexProfiling 属性设置为 true。使用 codegen 命令生成代码并指定配置对象。例如:

    cfg = coder.config("mex");
    cfg.EnableMexProfiling = true;
    codegen -config cfg MyFunction

  • 在命令行上,使用 codegen 命令与 -profile 选项生成代码。例如:

    codegen MyFunction -profile

  • 如果您使用 MATLAB Coder,请在工具条的 MATLAB Coder 选项卡中,点击输出类型 > MEX 以指示代码生成器生成 MEX 函数。在工具条上,点击设置以打开“代码生成设置”对话框。在调试窗格中,选中启用执行探查复选框。然后,在工具条上,点击生成代码并编译以生成 MEX 函数。

一旦您生成适合探查的 MEX 函数,请使用以下命令运行 MATLAB 探查器并查看探查摘要报告:

profile on;
MyFunction_mex;
profile viewer;
profile off;

确保您没有更改或移动原始 MATLAB 文件 MyFunction.m。否则,探查器无法为 MyFunction_mex 生成探查。

一步生成并探查 MEX 函数

如果您有调用 MATLAB 函数的测试文件,您可以在命令行上或使用 MATLAB Coder 一步生成 MEX 函数并对其进行探查。使用以下方法之一:

  • 在命令行中,使用带 -test-profile 选型的 codegen 命令生成 MEX 函数。例如:

    codegen MyFunction -test MyFunctionTest -profile

  • 在工具条的 MATLAB Coder 选项卡中,点击输出类型 > MEX 以指示代码生成器生成 MEX 函数。在工具条上,点击设置以打开“代码生成设置”对话框。在调试窗格中,选中启用执行探查复选框。在工具条上,点击运行生成的 MEX > 运行文件并选择 MyFunctionTest.m

MATLAB 在 MEX 生成后打开探查器窗口。

示例

您使用探查器来识别产生耗时最长的生成代码的函数或 MATLAB 代码行。下面是 MATLAB 函数的示例,该函数在一个代码行中将其输入矩阵 AB 的表示从行优先布局转换为列优先布局。对于大型矩阵,这种转换需要很长的执行时间。通过修改特定代码行来避免这种转换会使函数更加高效。

以 MATLAB 函数为例:

function [y] = MyFunction(A,B) %#codegen

% Generated code uses row-major representation of matrices A and B
coder.rowMajor; 
length = size(A,1);

% Summing absolute values of all elements of A and B by traversing over the
% matrices row by row
sum_abs = 0;  
for row = 1:length 
   for col = 1:length  
       sum_abs = sum_abs + abs(A(row,col)) + abs(B(row,col));
   end
end

% Calling external C function 'foo.c' that returns the sum of all elements
% of A and B
sum = 0;
sum = coder.ceval('foo',coder.ref(A),coder.ref(B),length);

% Returning the difference of sum_abs and sum
y = sum_abs - sum;
end

为该函数生成的代码使用方阵 AB 的行优先表示。该代码首先通过逐行遍历矩阵来计算 sum_absAB 的所有元素的绝对值之和)。该算法针对以行优先布局方式表示的矩阵进行了优化。然后,代码使用 coder.ceval 调用外部 C 函数 foo.c

#include <stdio.h>
#include <stdlib.h>
#include "foo.h"

double foo(double *A, double *B, double length)
{
 int i,j,s;
 double sum = 0;
 s = (int)length;
 
 /*Summing all the elements of A and B*/
 for(i=0;i<s*s;i++)
 {
         sum += A[i] + B[i];
 }
 return(sum);
}

对应的 C 头文件 foo.h 是:

#include "rtwtypes.h"

double foo(double *A, double *B, double length);

foo.c 返回变量 sum,它是 AB 的所有元素的总和。函数 foo.c 的性能与矩阵 AB 以行优先布局还是列优先布局表示无关。MyFunction 返回 sum_abssum 的差值。

您可以针对大型输入矩阵 AB 测量 MyFunction 的性能,然后进一步对该函数进行优化:

  1. MyFunction 启用 MEX 探查并生成 MEX 代码。对两个大型随机矩阵 AB 运行 MyFunction_mex。查看探查摘要报告。

    A = rand(20000);
    B = rand(20000);
    
    codegen MyFunction -args {A,B} foo.c foo.h -profile
    
    profile on; 
    MyFunction_mex(A,B);
    profile viewer;

    将打开单独的窗口,显示探查摘要报告。

    Profile summary exhibiting a table with field Function Name Calls, Total Time in seconds, Self Time in seconds and total time plot. A flame graph is present, representing the table in a bar graph.

    探查摘要报告显示 MEX 文件及其子文件(为原始 MATLAB 函数生成的代码)的总时间和自用时间。

  2. 在函数名称下,点击第一个链接查看为 MyFunction 生成的代码的探查详细信息报告。您可以查看耗时最多的代码行:

    Table with fields Line Number, Code, Cells, Total time in seconds, Percentage of time and time plot with relevant data entries from example code. Important to point out that the total time for coder.ceval is relatively high.

  3. 调用 coder.ceval 的代码行需要很长时间(16.914 秒)。此代码行的执行时间很长,因为 coder.ceval 将矩阵 AB 的表示从行优先布局转换为列优先布局,然后将其传递给外部 C 函数。您可以通过在 coder.ceval 中使用附加参量 -layout:rowMajor 来避免此转换:

    sum = coder.ceval('-layout:rowMajor','foo',coder.ref(A),coder.ref(B),length);
  4. 使用修改后的 MyFunction 再次生成 MEX 函数和探查。

    A = rand(20000);
    B = rand(20000);
    
    codegen MyFunction -args {A,B} foo.c foo.h -profile
    
    profile on; 
    MyFunction_mex(A,B);
    profile viewer;
    MyFunction 的探查详细信息报告显示,调用 coder.ceval 的代码行现在只需 0.653 秒:

    Same image as mentioned above, here coder.ceval has a reduced total time of 0.653s.

折叠表达式对 MEX 代码覆盖率的影响

当您使用 coder.const 将表达式折叠成常量时,会导致 MATLAB 函数和 MEX 函数的代码覆盖率不同。以如下函数为例:

function y = MyFoldFunction %#codegen
a = 1;
b = 2; 
c = a + b;
y = 5 + coder.const(c);
end

探查 MATLAB 函数 MyFoldFunction 会在探查详细信息报告中显示以下代码覆盖率:

Profile Detail Report, showing code coverage

但是,探查 MEX 函数 MyFoldFunction_mex 会显示不同的代码覆盖率:

Profile Detail Report, showing code coverage

生成的代码中不会执行第 2、3 和 4 行,因为您已将表达式 c = a + b 折叠成常量以进行代码生成。

此示例使用用户定义的表达式折叠。代码生成器有时会自动折叠某些表达式,以优化生成代码的性能。这种优化还导致 MEX 函数的覆盖率不同于 MATLAB 函数。

另请参阅

| | | | |

主题