排除 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 变量可以溢出,则会报告错误。
溢出条件 | 示例 | 解决方案 |
---|---|---|
| 这里,MATLAB 报错,因为 parfor idx=int8(-128:127) idx; end | 对 parfor idx=-128:127 int8(idx); end |
| 这里,MATLAB 报错,因为 parfor idx=uint32(0:1) idx; end |
|
解决 parfor
循环中的变量分类问题
当 MATLAB 将 parfor
循环中的名称识别为变量时,该变量将被归类为下表所示的几种类别之一。确保您的变量被唯一分类并满足类别要求。违反要求的 parfor
-循环将返回错误。
分类 | 描述 |
---|---|
循环变量 | 循环索引 |
分段变量 | 数组的各段由循环的不同迭代进行操作 |
广播变量 | 循环前定义的变量,其值在循环内部是必需的,但从未在循环内部赋值 |
归约变量 | 无论迭代顺序如何,在循环迭代过程中都会累积一个值的变量 |
临时变量 | 在循环内创建的变量,并且不能在循环外访问 |
要找出您拥有哪些变量,请检查代码片段。表中的所有变量分类均用以下代码表示:
如果您遇到变量分类问题,请在采用更困难的方法将 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
循环的主体不能包含 global
或 persistent
变量声明。