分段变量
分段变量是指其值可以被分割为多个段或分段,然后由不同的工作单元分别对其进行操作。循环的每次迭代都作用于数组的不同切片。使用分段变量可以减少客户端和工作单元之间的通信。
在这个示例中,工作单元将 f 分别应用于 A 的元素。
parfor i = 1:length(A) B(i) = f(A(i)); end
分段变量的特征
如果 parfor 循环中的变量具有以下所有特征,则该变量被分段:
一级索引的类型 - 一级索引是圆括号
()或大括号{}。固定索引列表 - 在第一级括号或大括号内,索引列表对于给定变量的所有出现都是相同的。
索引形式 - 在变量的索引列表中,只有一个索引涉及循环变量。
数组的形状 - 数组保持恒定的形状。在分配给分段变量时,赋值的右侧不能是
[]或'',因为这些运算符试图删除元素。
一级索引类型
对于分段变量,第一级索引用括号 () 或大括号 {} 括起来。
以下是切片和未切片数组的第一级索引的形式。
| 未切片 | 切片 |
|---|---|
A.x | A(...) |
A.(...) | A{...} |
在第一级之后,您可以在第二级和后续级别中使用任何类型的有效 MATLAB® 索引。
这里左侧显示的变量 A 未被分段;而右侧显示的变量已被分段。
A.q{i,12} A{i,12}.q
固定索引列表
在分段变量的第一级索引中,索引列表对于给定变量的所有出现都是相同的。
左边的变量 A 没有被分段,因为 A 被 i 和 i+1 在不同的地方索引。在右侧的代码中,变量 A 被正确分段。
| 未切片 | 切片 |
|---|---|
parfor i = 1:k B(:) = h(A(i), A(i+1)); end | parfor i = 1:k B(i) = f(A(i)); C(i) = g(A{i}); end |
右边的示例显示在同一个循环中使用括号和大括号进行一级索引,这是可以接受的。
下面左边的示例没有对 A 进行切片,因为 A 的索引在所有地方并不相同。右边的示例对 A 和 B 进行了切片。A 的索引与 B 的索引不同。但是,A 和 B 的索引单独来看都是一致的。
| 未切片 | 切片 |
|---|---|
parfor i=1:10 b = A(1,i) + A(2,i) end | A = [ 1 2 3 4 5 6 7 8 9 10;
10 20 30 40 50 60 70 80 90 100];
B = zeros(1,10);
parfor i=1:10
for n=1:2
B(i) = B(i)+A(n,i)
end
end |
索引形式
在分段变量的第一级索引中,只有一个索引表达式的形式为 i、i+k、i-k 或 k+i。索引 i 是循环变量,而 k 是标量整数常量或简单(非索引)广播变量。每个其他索引表达式都是一个正整数常量、一个简单(非索引)广播变量、一个嵌套的 for 循环索引变量、colon、一个涉及简单广播变量或标量整数常量的冒号表达式或 end。
以 i 作为循环变量,左侧显示的 A 变量未被分段,而右侧显示的 A 变量被分段。
| 未切片 | 切片 |
|---|---|
A(i+f(k),j,:,3) % f(k) invalid for slicing A(i,:,s.field1) % s.field1 not simple broadcast var A(i,[20,21,30],end) % array literal not supported 在 R2024b 之前的版本中: A(i,20:30,end) |
A(i+k,j,:,3)
A(i,:,end)
A(i,:,k)
A(i,20:30,end) (自 R2024b 起) |
您可以通过将不受支持的索引表达式声明为广播变量来解决它们。在这个示例中,左边的代码不起作用,因为它直接使用不受支持的索引表达式来索引分段变量。右侧的代码通过在循环体之前将表达式声明为广播变量提供了一种解决方法。
| 未切片 | 切片 |
|---|---|
parfor i = 1:n A(i, [false,true,true]) = 7; % not supported B(i, [20,21,30]) = 7; % also not supported end | b1 = [false,true,true]; b2 = [20,21,30]; parfor i = 1:n A(i,b1) = 7; % works B(i,b2) = 7; % also works end |
当您使用其他变量与循环变量一起来索引数组时,您不能在循环内设置这些变量。实际上,这些变量在整个 parfor 语句的执行过程中保持不变。不能将循环变量与其自身组合来形成索引表达式。
数组形状
分段变量必须保持恒定的形状。此处显示的变量 A 未被分段:
A(i,:) = [];
A 没有被切片,因为改变切片数组的形状会违反控制客户端和工作单元之间通信的假设。
分段输入和输出变量
分段变量可以是输入变量、输出变量或两者兼有。MATLAB 将分段输入变量从客户端传输到工作单元,并将分段输出变量从工作单元传输回客户端。如果一个变量既是输入又是输出,那么它会在两个方向上传输。
在这个 parfor 循环中,A 是一个分段输入变量,而 B 是一个分段输出变量。
A = rand(1,10); parfor ii = 1:10 B(ii) = A(ii); end
但是,如果 MATLAB 确定在每次迭代中,分段变量元素在任何使用之前都已设置,则 MATLAB 不会将变量传输给工作单元。在这个示例中,A 的所有元素在任何使用之前都被设置。
parfor ii = 1:n if someCondition A(ii) = 32; else A(ii) = 17; end % loop code that uses A(ii) end
分段输出变量可以通过索引分配动态增长,并在中间索引处插入默认值。在此示例中,您可以看到在 A 中的几处地方插入了默认值 0。
A = []; parfor idx = 1:10 if rand < 0.5 A(idx) = idx; end end disp(A);
0 2 0 4 5 0 0 8 9 10
即使分段变量没有显式引用为输入,隐式使用也可以使其成为输入。在下面的示例中,A 的所有元素不一定都在 parfor 循环内设置。因此,数组的原始值被接收、保存,然后从循环中返回。
A = 1:10; parfor ii = 1:10 if rand < 0.5 A(ii) = 0; end end
在某些情况下,parfor 循环必须假设一个工作单元可能需要分段变量的所有段。在这个示例中,无法确定在执行之前将读取分段变量的哪些元素,因此 parfor 发送所有可能的段。
A = 1:10; parfor ii=1:11 if ii <= randi([10 11]) A(ii) = A(ii) + 1; end end
带有分段变量的嵌套 for 循环
使用嵌套的 for 循环变量索引分段变量时,请记住以下要求:
分段变量必须包含在相应的
for循环内。在这个示例中,左边的代码不起作用,因为它在定义
A的嵌套for循环之外对分段变量j进行索引。未切片 切片 A = zeros(10); parfor i=1:10 for j=1:10 end A(i,j)=1; end
A = zeros(10); parfor i=1:10 for j=1:10 A(i,j) = 1; end end
for循环变量的范围必须是正的常数或变量的行向量。在这个示例中,左边的代码不起作用,因为它用函数调用定义了嵌套的
for循环的上限。右侧的代码通过在parfor循环之外的常量变量中定义上限来提供一种解决方法。未切片 切片 A = zeros(10); parfor i=1:10 for j=1:size(A,2) A(i,j)=1; end end
A = zeros(10); L = size(A,2); parfor i=1:10 for j=1:L A(i,j)=1; end end
for循环变量不得通过其for语句以外的方式赋值。在这个示例中,左边的代码不起作用,因为它在
for循环里面重新分配了for循环变量。右侧的代码通过将i分配给临时变量t来提供一种解决方法。未切片 切片 A = zeros(10); parfor i=1:10 for j=1:10 if i == j j = i; A(i,j) = j; end end end
A = zeros(10); parfor i=1:10 for j=1:10 if i == j t = i; A(i,j) = t; end end end
数据类型限制
某些 MATLAB 数据类型不支持用作
parfor循环的分段输入或输出变量。要将变量用作分段变量,parfor实现必须能够使用索引来扩展变量。以下数据类型不支持作为分段变量:
dictionarytable
当您使用
handle对象的切片数组时,MATLAB 会构建默认元素。有关详细信息,请参阅创建和初始化对象数组。