mcpField
M
matlab-diagnose-parfor

matlab-diagnose-parfor

github.com/matlab开发1.2k58 次安装1 天前更新
代码仓库

关于 matlab-diagnose-parfor

用于诊断和修复 MATLAB `parfor` 变量分类问题的编码技能。它以 MATLAB 代码分析器为准,不凭推测判断规则,并提供分类修复方案及复查步骤。

主要功能

  1. 01首先运行代码分析器
  2. 02根据诊断定位分类原因
  3. 03提供 MATLAB 代码重构模式
  4. 04使用分析器复查修复结果

适用场景

  1. 01排查 `parfor` 分类错误
  2. 02将 `for` 循环改写为 `parfor`
  3. 03验证修正后的 `parfor` 代码

MCP 相关

工作流使用 `mcp__matlab__check_matlab_code` MCP 工具分析并验证含有 `parfor` 的 MATLAB 代码。

安装

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

$npx skills add https://github.com/matlab/matlab-agentic-toolkit --skill matlab-diagnose-parfor

安装后在 Claude Code 中输入/matlab-diagnose-parfor确认已加载。

附带文件manifest.yamlreferences/parfor-analyser-messages.md

详细介绍

诊断并修复 parfor 变量分类问题

切勿凭记忆推断 parfor 的分类规则。 LLM 训练数据中对此类规则的解释并不完整,且经常有误。 MATLAB 的代码分析器(checkcode / codeIssues / mcp__matlab__check_matlab_code) 是唯一可靠的权威来源——它实现了与运行时完全相同的分类器。务必先运行分析器;切勿猜测。

适用场景

  • 涉及 parfor 的任何代码,且用户报告了问题或寻求帮助
  • 用户遇到 parfor 分类错误(运行时或编辑器中)
  • 用户想将 for 循环转换为 parfor
  • 用户询问 parfor 为何报错、无法运行,或因分类问题而运行缓慢
  • 准备建议修改 parfor 代码时——先运行分析器

不适用场景

  • parfor 可以运行,但速度很慢——这是性能/进程池选择问题,而非分类错误
  • 工作进程需要设置/状态(路径、环境、工具箱)——这不是 parfor 变量问题
  • 代码只是提到了 parfor,但用户没有提及相关问题

工作流程

务必按此顺序操作。切勿猜测分类规则。

  1. 使用 mcp__matlab__check_matlab_code MCP 工具,对包含 parfor 循环的文件运行代码分析器。切勿通过 Bash 调用 MATLAB;务必使用 MCP 工具。仔细阅读确切的错误消息。

  2. 根据消息判断错误类型(见下表)。

  3. 应用对应的修复模式(见“模式”部分)。

  4. 验证:对修复后的代码重新运行 mcp__matlab__check_matlab_code,确认不存在 parfor 分类错误。

错误消息及根本原因

如需查看全部 48 条 parfor 分析器消息(含 ID),请参阅 references/parfor-analyser-messages.md。下表列出了最常见的分类错误。

checkcode 消息根本原因修复模式
"Unable to classify variable 'X'"兜底错误:可能的原因很多。通常是切片无效(下标不一致、索引未涉及循环变量),或访问方式组合无效(例如,带索引的归约)。结构体字段访问是触发此错误的一种特定方式。确定具体的访问模式,然后重构为符合某一类别的形式,或拆分相互冲突的用途
"multiple sliced accesses...do not all have the same list of subscripts"同一变量使用了不同的下标模式进行切片(例如,A{i,1}、A{i,2})使用单次赋值:A(i,:) = {...}
"Variable 'X' is indexed...but it is not a valid sliced output variable"某个下标是不确定的(随机值、函数调用),或循环变量出现在多个维度中,或下标模式以其他方式违反了切片规则预先计算有问题的下标;重构代码,使循环变量只出现在一个维度中,且其余下标固定
"accessed with an invalid combination of sliced indexing expressions and non-indexed reads"同一迭代中,变量既通过切片索引写入,又整体读取——用途冲突拆分为两个变量:一个用于切片写入,另一个用于整体读取的快照或广播副本
"The temporary variable 'X' might be used after the PARFOR loop" / "Temporary variable 'X' must be set inside the parfor loop before it is used"变量是在条件分支中赋值的(仅在 if 分支中赋值),因此 MATLAB 会将其视为临时变量——但它在循环后仍会被使用,或某些路径上尚未赋值就被读取重构为切片输出(存储每次迭代的值)并在循环后进行归约
"When indexing a sliced variable with a nested for loop variable, the range...must be a row vector of positive constant numbers or variables"内层循环为 for j = 1:(expr),其中 expr 不是字面常量或广播变量将内层循环范围设为常量或广播变量,或先累加到局部临时变量中,再在每次外层迭代中对切片变量赋值一次
"The entire array or structure 'X' is a broadcast variable"变量被整体使用(未通过循环变量索引);只读这不是错误,而是性能警告。如果变量较大,请使用 parallel.pool.Constant(X)

变量分类规则(参考)

parfor 会将每个变量准确归入以下一个类别:

类别规则示例
循环变量循环索引变量parfor i = 1:N
切片变量在任意一个维度(不只限于第一个维度)中由循环变量索引;每次访问都使用相同的下标模式。循环变量可以偏移字面常量或广播变量(例如,A(i+1,:)、A(:,i-k),其中 k 是广播变量)。A(i,:) = ...、A(:,i) = ...、A(i+1) = ...
广播变量整体使用(未通过循环变量索引);只读total = sum(bigArray)
归约变量形式为 X = X op expr 或 X = expr op X,其中 X 是整个变量,op 是具有结合性的运算符 +、.*、&、| 等。total = total + val
临时变量在迭代中的首次使用是赋值;循环后不再使用temp = rand(10)

关键限制: 一个变量必须且只能属于一个类别。混合使用(例如,将结构体字段用作归约变量,或同一变量使用不同下标进行切片)会导致分类失败。

模式

修复 1:将结构体字段作为切片输出 → 使用普通数组

错误:“无法对变量 'out' 进行分类”

结构体导致“无法分类”时,最常见的原因是这个。用户会写 out.field(i) = someFcn(),期望对结构体字段进行切片,但 out 的第一级索引是点号(.field),而不是圆括号。切片 变量要求第一级索引使用带循环变量的 () 或 {} — 点号索引永远不能用于切片。

% BROKEN: first-level subscript on out is dot, not paren
parfor i = 1:N
    out.x(i) = computeX(i);
    out.y(i) = computeY(i);
end

**修复方法:**使用单独的数组作为切片输出,并在循环后打包到结构体中:

xVals = zeros(1, N);
yVals = zeros(1, N);
parfor i = 1:N
    xVals(i) = computeX(i);
    yVals(i) = computeY(i);
end
out.x = xVals;
out.y = yVals;

一种相关情况:将结构体字段用作归约(results.sum = results.sum + val)。 同样的规则适用——将每个字段提取到各自的标量变量中,分别进行归约, 然后在循环后打包结构体。

修复 2:多个切片下标 → 单次赋值

错误:“多个切片访问……下标列表不完全相同”

% BROKEN: {i,1}, {i,2}, {i,3} are three different subscript patterns
output = cell(N, 3);
parfor i = 1:N
    [a, b, c] = computeStuff(i);
    output{i,1} = a;
    output{i,2} = b;
    output{i,3} = c;
end

**修复方法:**使用下标一致的单个圆括号索引赋值:

output = cell(N, 3);
parfor i = 1:N
    [a, b, c] = computeStuff(i);
    output(i,:) = {a, b, c};
end

注意:赋值多个单元格时,请使用 () 圆括号索引(赋值单元格),而不是 {} 花括号索引。

修复 3:无效的切片下标模式

错误:“变量 'X' 已被索引……但它不是有效的切片输出变量”

切片变量必须恰好在一个维度上由循环变量索引,其余下标必须固定(字面值、广播变量 或冒号)。以下是三种常见违规情况:

parfor i = 1:N
    out(i, randi(5)) = val;      % (a) non-deterministic subscript
    out2(i, 1:4, i) = row;      % (b) loop variable in two dimensions
    for j = 1:(N-1)
        out3(i, j) = fcn(i,j);  % (c) nested-for range is not constant
    end
end

修复方法:

(a) 在 parfor 前预先计算非确定性索引,或先累积到局部临时变量中,再一次性赋值整行:

parfor i = 1:N
    localRow = zeros(1, 5);
    localRow(randi(5)) = val;
    out(i,:) = localRow;
end

(b) 移除重复的循环变量维度——调整结构,使 i 只索引一个维度:

parfor i = 1:N
    out2(i,:) = computeRow(i);  % i in dim 1 only
end

(c) 将嵌套 for 的范围设为常量或广播变量,或者先收集到局部变量中,再一次性赋值切片输出:

innerN = N - 1;  % broadcast (constant before parfor)
parfor i = 1:N
    row = zeros(1, innerN);
    for j = 1:innerN
        row(j) = fcn(i, j);
    end
    out3(i, 1:innerN) = row;
end
修复 4:切片写入与完整读取结合

错误:“无法对变量 'data' 进行分类”

切片读取与广播(整体)读取结合使用是合法的——此时变量只会“退化”为广播变量。无法分类的 组合是:同一变量在同一次迭代中既进行切片写入,又进行完整读取。通常,整体读取只是试图 从切片输出变量中提取大小或类型信息。

% BROKEN: data is sliced-write (data(i,j) = ...) AND read whole (size(data,2))
data = buildMatrix(); % 
parfor i = 1:size(data,1)
    for j = 1:size(data,2) % This counts as a "full read" of data
        data(i,j) = myFunc(i,j);
    end
end

**修复方法:**在循环开始前提取所需的常量信息。

data = buildMatrix();
[N,M] = size(data);
parfor i = 1:N
    for j = 1:M
        data(i,j) = myFunc(i,j);
    end
end

请注意,如果切片输出变量的“整体读取”取决于变量中的实际值,那么此修复方法不适用,而且这可能意味着迭代并非相互独立。

修复 5:归约形式不符合标准形式

错误:“无法对变量 'X' 进行分类”

归约必须严格符合以下形式:X = X op expr、X = expr op X 或 X = fcn(X, expr),其中 X 是 整个变量(不使用索引),op 是关联运算符,例如 +、.* 等;fcn 可以是任何 关联函数,例如 union、max 等。

% BROKEN: indexed reduction
counts = zeros(1, 5);
parfor i = 1:100
    bin = randi(5);
    counts(bin) = counts(bin) + 1;  % indexed reduction - not supported
end

**修复方法:**逐次迭代累积,然后执行归约:

counts = zeros(1, 5);
parfor i = 1:100
    bin = randi(5);
    localCounts = zeros(1, 5);
    localCounts(bin) = 1;
    counts = counts + localCounts;  % whole-variable reduction
end
修复 6:临时变量被错误标记

如果每次迭代中变量都在使用前赋值,但 checkcode 仍发出警告,请确保迭代中的第一次 引用是完整赋值(不带索引):

% BROKEN: temp is not fully-assigned as first reference
parfor i = 1:10
    temp.a = rand();
    temp.b = 2*rand();
    result(i) = temp.a * temp.b;
end

**修复方法:**通过单次赋值创建 temp

parfor i = 1:N
    temp = struct('a', rand(), 'b', 2*rand()); % Ok - single non-indexed assignment
    result(i) = sum(temp);
end
修复 7:条件更新的结果需要在循环后保留

错误:“临时变量 'X' 可能在 PARFOR 循环后使用”

任何临时变量在循环后都不可用——这是 parfor 的基本规则,并非条件赋值特有的问题。“当前最佳值”模式会失败,因为条件语句中的 bestscore = score 不符合归约形式(X = X op expr),因此分类器无法将其视为归约。它会被视为临时变量,而临时变量会在循环结束时被丢弃:

% BROKEN: bestscore doesn't match reduction form — classified as temporary
bestscore = -Inf;
parfor i = 1:N
    score = expensiveCompute(i);
    if score > bestscore
        bestscore = score;
        X = candidates(i);
    end
end
% bestscore and X are undefined here

**修复方法:**将所有候选结果计算为切片输出,然后在循环后执行归约:

scores = zeros(1, N);
allX = cell(1, N);
parfor i = 1:N
    scores(i) = expensiveCompute(i);
    allX{i} = candidates(i);
end
[bestscore, idx] = max(scores);
X = allX{idx};

约定

  • 务必先运行代码分析器,再进行诊断。 使用 mcp__matlab__check_matlab_code(MCP 工具)、codeIssues(R2022b+)或 checkcode。分析器采用与运行时完全相同的分类器,是判断依据。切勿仅凭记忆进行诊断。
  • 务必使用分析器验证修复效果——在修复后的文件上重新运行分析器,直到不再有 parfor 错误,再宣布成功。修复一个问题可能会暴露其他问题。
  • 不要运行代码——并行池可能很慢或不可用。代码分析器可以静态捕获所有分类错误。
  • 保留语义——修复后的代码必须与原始 for 循环产生相同结果。
  • 尽量少改动——只修复分类问题。除非用户要求,否则不要重构整个算法。

版权所有 2026 The MathWorks, Inc.


评论

支持 Markdown