Main Content

本页采用了机器翻译。点击此处可查看英文原文。

排除 parfor-循环中的变量故障

确保 parfor-循环变量是连续增加的整数

parfor 循环中的循环变量必须是连续增加的整数。因此,以下示例返回错误:

parfor i = 0:0.2:1      % not integers
parfor j = 1:2:11       % not consecutive
parfor k = 12:-1:1      % not increasing
您可以通过将循环变量转换为有效范围来修复这些错误。例如,您可以按如下方式修复非整数示例:
iValues = 0:0.2:1;
parfor idx = 1:numel(iValues)
    i = iValues(idx);
    ...
end

避免 parfor 循环中的溢出

如果 MATLAB® 检测到 parfor-loop 变量可以溢出,则会报告错误。

溢出条件示例解决方案

parfor-循环范围的长度超出了循环变量类型的最大值。

这里,MATLAB 报错,因为 length(-128:127)>maxint('int8')

parfor idx=int8(-128:127)
    idx;
end

parfor-loop 变量使用更大的数据类型。如果您想在计算中保留原始数据类型,请在 parfor 循环内转换 parfor-loop 变量。

parfor idx=-128:127
    int8(idx);
end

parfor-循环范围的初始值等于循环变量类型的最小值。

这里,MATLAB 报错,因为 0=intmin('uint32')

parfor idx=uint32(0:1)
    idx;
end

  • 像前面的解决方案一样,使用较大的数据类型和较低的最小值。

  • 增加值的范围。例如:

    parfor idx=uint32(0:1)+1
        idx-1;
    end

解决 parfor 循环中的变量分类问题

当 MATLAB 将 parfor 循环中的名称识别为变量时,该变量将被归类为下表所示的几种类别之一。确保您的变量被唯一分类并满足类别要求。违反要求的 parfor-循环将返回错误。

分类描述
循环变量循环索引
分段变量数组的各段由循环的不同迭代进行操作
广播变量循环前定义的变量,其值在循环内部是必需的,但从未在循环内部赋值
归约变量无论迭代顺序如何,在循环迭代过程中都会累积一个值的变量
临时变量在循环内创建的变量,并且不能在循环外访问

要找出您拥有哪些变量,请检查代码片段。表中的所有变量分类均用以下代码表示:

Code fragment containing a parfor-loop in which each variable is labelled according to its classification.

如果您遇到变量分类问题,请在采用更困难的方法将 parfor 循环体转换为函数之前考虑这些方法。

  • 如果使用嵌套的 for 循环来索引切片数组,则不能在 parfor 循环的其他地方使用该数组。左边的代码不起作用,因为 A 在嵌套的 for 循环内被切片和索引。右边的代码能够工作是因为在嵌套循环外 v 被赋值给了 A。您可以计算整行,然后对切片输出执行单个分配。

    无效有效
    A = zeros(4, 10);
    parfor i = 1:4
        for j = 1:10
            A(i, j) = i + j;
        end
        disp(A(i, 1))
    end
    A = zeros(4, 10);
    parfor i = 1:4
        v = zeros(1, 10);
        for j = 1:10
            v(j) = i + j;
        end
        disp(v(1))
        A(i, :) = v;
    end

  • 左边的代码不起作用,因为 parfor 中的变量 x 无法进行分类。该变量无法分类,因为对 x 的不同部分有多个分配。因此 parfor 无法确定循环迭代之间是否存在依赖关系。右边的代码之所以有效,是因为您完全覆盖了 x 的值。parfor 现在可以明确地确定 x 是一个临时变量。

    无效有效
    parfor idx = 1:10
        x(1) = 7;
        x(2) = 8;
        out(idx) = sum(x);
    end
    parfor idx = 1:10
        x = [7, 8];
        out(idx) = sum(x);
    end

  • 此示例显示如何对结构化数组的字段进行切分。请参阅 struct 了解详细信息。左边的代码不起作用,因为 parfor 中的变量 a 无法进行分类。该变量无法分类,因为索引形式对于分段变量无效。第一级索引不是切片索引操作,即使 a 的字段 x 看起来被正确切片。右边的代码之所以有效,是因为您将 struct 的字段提取到单独的变量 tmpx 中。parfor 现在可以正确确定该变量已被切片。一般来说,不能将 struct 的字段或对象的属性用作 parfor 中的切片输入或输出变量。

    无效有效
    a.x = [];
    parfor idx = 1:10
        a.x(idx) = 7;
    end
    tmpx = [];
    parfor idx = 1:10
        tmpx(idx) = 7;
    end
    a.x = tmpx;

parfor 循环中的结构数组

创建临时结构

您不能使用点符号赋值在 parfor 循环中创建结构。在左侧的代码中,循环内的两行都会产生分类错误。在右侧的代码中,作为一种解决方法,您可以使用 struct 函数在循环中或第一个字段中创建结构。

无效有效
parfor i = 1:4
    temp.myfield1 = rand();
    temp.myfield2 = i;
end
parfor i = 1:4
    temp = struct();
    temp.myfield1 = rand();
    temp.myfield2 = i;
end
parfor i = 1:4
    temp = struct('myfield1',rand(),'myfield2',i);
end

切片结构字段

您不能在 parfor 循环中将结构字段用作切片输入或输出数组。换句话说,您不能使用循环变量来索引结构字段的元素。在左边的代码中,循环中的两行都会因为索引而产生分类错误。在右侧的代码中,作为切片输出的解决方法,您可以在循环中使用单独的切片数组。然后在循环完成后分配结构字段。

无效有效
parfor i = 1:4
    outputData.outArray1(i) = 1/i;
    outputData.outArray2(i) = i^2;
end
parfor i = 1:4
    outArray1(i) = 1/i;
    outArray2(i) = i^2;
end
outputData = struct('outArray1',outArray1,'outArray2',outArray2);

切片输入的解决方法是在循环之前将结构字段分配给单独的数组。您可以将该新数组用于切片输入。

inArray1 = inputData.inArray1;
inArray2 = inputData.inArray2;
parfor i = 1:4
    temp1 = inArray1(i);
    temp2 = inArray2(i);
end

parfor 循环体转换为函数

如果其他方法都失败了,您通常可以通过将 parfor 循环体转换为函数来解决 parfor 循环中的变量分类问题。在左侧的代码中,代码分析器标记变量 y 存在问题,但无法解决它。在右侧的代码中,您可以通过将 parfor 循环的主体转换为函数来解决此问题。

无效有效
function parfor_loop_body_bad
    data = rand(5,5);
    means = zeros(1,5);
    parfor i = 1:5
        % Code Analyzer flags problem 
        % with variable y below  
        y.mean = mean(data(:,i));
        means(i) = y.mean;
    end
    disp(means);
end
function parfor_loop_body_good
    data = rand(5,5);
    means = zeros(1,5);
    parfor i = 1:5
        % Call a function instead
        means(i) = computeMeans(data(:,i));
    end
    disp(means);
end

% This function now contains the body
% of the parfor-loop
function means = computeMeans(data)
    y.mean = mean(data);
    means = y.mean;
end
Starting parallel pool (parpool) using the 'Processes' profile ... connected to 4 workers.
    0.6786    0.5691    0.6742    0.6462    0.6307

明确的变量名

如果您使用的名称是 MATLAB 无法明确区分为 parfor 循环内的变量,则在解析时 MATLAB 会假定您正在引用一个函数。然后在运行时,如果找不到该函数,MATLAB 就会生成错误。请参阅变量名称。例如,在下面的代码中,f(5) 可以引用名为 f 的数组的第五个元素,或者引用名为 f 且带有 5 参量的函数。如果代码中没有明确将 f 定义为变量,则代码运行时 MATLAB 会在路径上寻找函数 f

parfor i = 1:n
    ...
    a = f(5);
    ...
end

透明的 parfor 循环

parfor 循环的主体必须是透明的:所有对变量的引用在代码文本中都必须是“可见的”。有关透明度的更多详细信息,请参阅 确保 parfor 循环或 spmd 语句的透明度

全局变量和持久变量

parfor 循环的主体不能包含 globalpersistent 变量声明。

相关主题