mcpField
M
matlab-write-performance-tests

matlab-write-performance-tests

github.com/matlab测试与质量1.2k78 次安装1 天前更新
代码仓库

关于 matlab-write-performance-tests

面向 MATLAB 开发者的性能测试技能,使用 `matlab.perftest.TestCase` 编写可重复测试。内容涵盖统计测量、参数化设计和回归对比。

主要功能

  1. 01包含预热与采样的统计计时
  2. 02测量边界设置指南
  3. 03参数化性能测试
  4. 04基线对比以检查回归

适用场景

  1. 01跨输入规模基准测试 MATLAB 函数
  2. 02检查性能随时间的变化
  3. 03设置持续性能监控

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/matlab/matlab-agentic-toolkit --skill matlab-write-performance-tests

安装后在 Claude Code 中输入/matlab-write-performance-tests确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

manifest.yaml
references/
FeaturePerformanceTest.m
simulink-template.md
tExampleWorkflow.m

详细介绍

编写 MATLAB 性能测试

使用 matlab.perftest.TestCase 框架为 MATLAB 代码编写性能测试。该框架通过自动预热、多次采样和异常值处理,提供统计上严谨的计时结果。

适用场景

  • 用户希望为自己的 MATLAB 代码编写可重复的性能测试
  • 用户需要以统计置信度为函数进行基准测试
  • 用户希望随着时间推移检测性能回归
  • 用户正在设置持续性能监控
  • 用户询问如何使用 runperf 或 matlab.perftest.TestCase

不适用场景

  • 用户只想快速计时一次(请改用 timeit,参见 matlab-optimize-performance)
  • 用户想优化现有代码(请使用 matlab-optimize-performance)
  • 用户想测量内存使用量(请使用 matlab-optimize-memory)
  • 用户想分析代码以查找瓶颈(请使用 matlab-optimize-performance 的步骤 2)

框架:matlab.perftest.TestCase

所有性能测试都继承 matlab.perftest.TestCase,并使用测量边界来控制计时范围。

基本模板
classdef MyFeaturePerformanceTest < matlab.perftest.TestCase

    properties (MethodSetupParameter)
        DataSize = struct('Small', 100, 'Medium', 1000, 'Large', 10000)
    end

    properties
        inputData
    end

    methods (TestMethodSetup)
        function setupData(testCase, DataSize)
            % ALL setup outside the measurement boundary
            testCase.inputData = randn(DataSize, 1);
        end
    end

    methods (Test)
        function testMyFunction(testCase)
            data = testCase.inputData;
            while testCase.keepMeasuring
                result = myFunction(data);
            end
            testCase.verifyNotEmpty(result);
        end
    end
end
运行性能测试
% Run with statistical rigor (automatic sample size)
results = runperf('MyFeaturePerformanceTest');

% View results
disp(results)

% Fixed sample count (faster, less statistical power)
import matlab.perftest.TimeExperiment;
suite = testsuite('MyFeaturePerformanceTest');
experiment = TimeExperiment.withFixedSampleSize(4);
results = run(experiment, suite);

测量边界

该框架提供三种控制测量内容的方式:

1. keepMeasuring — 快速代码(<10ms)必需

自动循环执行代码,直到收集到足够的样本。对于 10ms 以下的操作,为确保统计严谨性必须使用;任何速度的代码都可以使用,但对于较慢的代码会增加开销,此时优先使用 startMeasuring/stopMeasuring:

function testFastFunction(testCase)
    data = testCase.inputData;
    while testCase.keepMeasuring
        result = fastFunction(data);
    end
    testCase.verifyNotEmpty(result);
end
2. startMeasuring/stopMeasuring — 用于精确控制

当需要在迭代之间进行设置,或想排除特定代码时使用:

function testWithBoundaries(testCase)
    data = testCase.inputData;
    % Pre-computation (NOT measured)
    preparedData = preprocess(data);

    testCase.startMeasuring();
    result = functionUnderTest(preparedData);
    testCase.stopMeasuring();

    % Verification (NOT measured)
    testCase.verifyEqual(size(result), [100 1]);
end
3. 无边界 — 测量整个方法

整个 Test 方法体都会计时。仅当整个方法本身就是工作负载时使用:

function testSlowFunction(testCase, DataSize) %#ok<INUSD>
    data = testCase.inputData;
    result = slowFunction(data);
    testCase.verifyNotEmpty(result);
end

参数化

对测试进行参数化,以测量不同输入大小或配置下的性能。

MethodSetupParameter — 设置时需要使用参数
properties (MethodSetupParameter)
    DataSize = struct('Small', 100, 'Medium', 1000, 'Large', 10000)
end

methods (TestMethodSetup)
    function setupData(testCase, DataSize)
        testCase.inputData = randn(DataSize, 1);
    end
end
TestParameter — 仅测试方法需要使用参数
properties (TestParameter)
    Algorithm = {'chol', 'lu', 'qr'}
end

methods (Test)
    function testSolve(testCase, Algorithm)
        ...
    end
end

关键注意事项: 不要对 TestMethodSetup 使用的属性采用 TestParameter。MATLAB 会报错:"Define 'X' as a MethodSetupParameter." 如果设置方法需要该参数,就必须使用 MethodSetupParameter。

组合使用 — 在设置中指定大小,在测试中指定算法
properties (MethodSetupParameter)
    DataSize = struct('Small', 100, 'Medium', 1000, 'Large', 10000)
end

properties (TestParameter)
    Algorithm = {'chol', 'lu', 'qr'}
end

methods (TestMethodSetup)
    function setupData(testCase, DataSize)
        testCase.inputData = randn(DataSize);
    end
end

methods (Test)
    function testSolve(testCase, Algorithm)
        A = testCase.inputData' * testCase.inputData; % SPD matrix
        data = A;
        while testCase.keepMeasuring
            result = decomposition(data, Algorithm);
        end
        testCase.verifyNotEmpty(result);
    end
end

这会生成 9 个测试点(3 种大小 × 3 种算法)。

精简设置 — 关键规则

所有设置都必须放在测量边界之外:

设置任务放置位置
数据生成TestMethodSetup
加载文件TestClassSetup
创建对象TestMethodSetup 或 TestClassSetup
路径操作TestClassSetup
设置 RNG 种子TestMethodSetup

切勿将设置或拆除操作放在测量区域内。这样会拉长计时结果并引入噪声。

测量前将属性复制到局部变量。 不要在测量边界内访问 testCase.PropertyName,因为这会把 matlab.perftest.TestCase 的索引开销也计入测量。请在边界外将其赋值给局部变量:

% Correct: local variable assigned before measurement
data = testCase.inputData;
while testCase.keepMeasuring
    result = myFunction(data);
end
methods (TestMethodSetup)
    function setupData(testCase, DataSize)
        rng(42, 'twister');  % Deterministic data
        testCase.inputData = randn(DataSize, 1);
    end
end

噪声与时长

目标时长
  • 每个测试点的执行时间应 >10ms
  • 如果速度太快,请增大数据规模或使用 keepMeasuring
  • 亚毫秒级测试会产生不可靠的结果
降低噪声
  • 对快速操作使用 keepMeasuring
  • 在设置阶段预分配所有数据
  • 禁止所有输出('Display', 'off')
  • 避免在测量区域内进行文件 I/O
  • 避免使用 drawnow、pause 或 GUI 操作
  • 在设置阶段设置 RNG 种子,以生成确定性数据
解读结果
results = runperf('MyPerformanceTest');

% Access timing statistics
for i = 1:numel(results)
    samples = results(i).Samples.MeasuredTime;
    fprintf('%s: median=%.4fs, std=%.4fs, CV=%.1f%%\n', ...
        results(i).Name, median(samples), std(samples), ...
        100*std(samples)/mean(samples));
end

变异系数(CV)超过 10% 表明结果存在较大噪声,请重新检查测试设置。

反模式

反模式为什么不合适修正方法
在测量期间进行设置会拉长计时并增加噪声移至 TestMethodSetup
测试时间低于 1 ms 且未使用 keepMeasuring噪声影响占主导使用 keepMeasuring 或增大数据量
使用 tic/toc 而非框架缺乏统计严谨性,且无法处理预热使用 runperf/keepMeasuring
使用 TestParameter 传递设置参数框架会在运行时出错使用 MethodSetupParameter
单个测试涵盖多个 API无法隔离回归问题拆分为专注的测试
使用随机数据但未设置 RNG 种子结果不确定,更难调试在设置中使用 rng(42, 'twister')
在测量代码中打印或绘图控制台/图形 I/O 会增加噪声禁止所有输出

比较不同时间的结果

保存并比较结果,以检测回归:

% Save baseline
baselineResults = runperf('MyPerformanceTest');
save('perfBaseline.mat', 'baselineResults');

% Later: compare against baseline
currentResults = runperf('MyPerformanceTest');
load('perfBaseline.mat');

for i = 1:numel(currentResults)
    baseMed = median(baselineResults(i).Samples.MeasuredTime);
    currMed = median(currentResults(i).Samples.MeasuredTime);
    ratio = currMed / baseMed;
    status = "OK";
    if ratio > 1.2
        status = "REGRESSION";
    elseif ratio < 0.8
        status = "IMPROVEMENT";
    end
    fprintf('%s: %.4fs -> %.4fs (%.2fx) %s\n', ...
        currentResults(i).Name, baseMed, currMed, ratio, status);
end

测试粒度

当需要编写性能测试时,请考虑适合的级别:

级别范围参数化?目标时长
单元单个操作(例如,svd、mldivide)是 — 遍历不同大小每个测试点 >10 ms
系统单个函数的端到端执行是 — 遍历不同大小每个测试点 >10 ms
工作流(ALB)完整的多步骤客户工作流否 — 使用一个有代表性的大小总计 0.5–5 s

适用场景:

  • 单元:函数包含多个开销较大的操作,且你需要隔离出发生回归的操作
  • 系统:默认选择 — 测量用户调用函数时的性能
  • 工作流:验证单元级优化是否能转化为实际加速;发现跨函数瓶颈

如果不确定,先生成系统级测试,然后询问是否需要进行单元级拆分或工作流级基准测试。

工作流基准测试(应用级)

端到端工作流基准测试的结构与单元/系统测试不同:

  • 不进行参数化 — 使用一个有代表性的问题规模(这是单元测试的用途)
  • 使用真实数据 — 输入应达到生产规模,而非合成数据
  • 单个测试方法 — 整个工作流作为一个测试点
  • 使用 startMeasuring/stopMeasuring — 始终使用(切勿使用 keepMeasuring)
  • 在 TestClassSetup 中进行设置 — 只加载一次数据,而非每个方法都加载
  • 目标为 0.5–5 s — 低于 0.5 s 时噪声影响占主导;超过 30 s 会拖慢迭代

完整模板请参见 references/tExampleWorkflow.m。

质量检查清单

生成每个测试类后,请进行以下检查:

  • 设置位于测量边界之外(TestMethodSetup)
  • 测量前将属性复制到局部变量
  • 每个测试点的时长超过 10 ms(如果更快,则使用 keepMeasuring)
  • 为 RNG 设置种子以生成确定性数据
  • 测量区域内没有控制台输出、绘图或文件 I/O
  • 按数据大小进行参数化
  • 每个测试方法只包含一个 API/操作(不捆绑多个操作)
  • 当设置过程需要使用参数时,使用 MethodSetupParameter(而非 TestParameter)

参考文件

  • references/FeaturePerformanceTest.m — 包含所有测量模式的性能测试类模板
  • references/tExampleWorkflow.m — 工作流级基准测试模板(应用级)
  • references/simulink-template.md — Simulink 模型基准测试的性能测试模板

版权所有 2026 The MathWorks, Inc.

评论

支持 Markdown