关于 matlab-write-performance-tests
面向 MATLAB 开发者的性能测试技能,使用 `matlab.perftest.TestCase` 编写可重复测试。内容涵盖统计测量、参数化设计和回归对比。
主要功能
- 01包含预热与采样的统计计时
- 02测量边界设置指南
- 03参数化性能测试
- 04基线对比以检查回归
适用场景
- 01跨输入规模基准测试 MATLAB 函数
- 02检查性能随时间的变化
- 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.yamlreferences/FeaturePerformanceTest.msimulink-template.mdtExampleWorkflow.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.