[已答复] parfor并行循环中出现有效索引受限的情况

[复制链接]
sweet_moon 发表于 2022-5-13 14:29:58
新手使用parfor循环,出现数组(矩阵)有效索引受限
一个简单的测试程序

A=1:10;
B=zeros(1,40);
%对B进行赋值,满足B(4*i-3:4*i)=A(i)

parfor i=1:10
    B(4*i-3:4*i)=A(i);
end


每一次赋值都是独立的,不同循环之间的数组也不会相互干扰,为什么会出错啊?

7 条回复


谢中华 发表于 2022-5-13 15:34:24
parfor中支持的变量有5类:
循环变量(Loop Variables):循环索引
切片变量数组(Sliced Variables):各部分由循环的不同迭代得到
广播变量(Broadcast Variables):在循环之前定义的变量,其值在循环内是必需的,但从未在循环内赋值
归约变量(Reduction Variables):在循环的迭代过程中累积值的变量,与迭代顺序无关
临时变量(Temporary Variables):在循环内部创建,在循环外部无法访问的变量
各类型变量如下图所示: parfor支持的变量类型.PNG
你的代码中的B变量的不同部分在parfor中有多个赋值,因此不属于以上介绍的5类变量,所以代码不能运行。可以按照如下方式修改。
第一种方法:
  1. A = 1:10;
  2. B = zeros(1,40);
  3. %对B进行赋值,满足B(4*i-3:4*i)=A(i)
  4. parfor i = 1:40
  5.     B(i) = A(ceil(i/4));
  6. end
  7. B
复制代码
第二种方法:
  1. A = 1:10;
  2. B = zeros(4,10);
  3. %对B进行赋值,满足B(4*i-3:4*i)=A(i)
  4. parfor i = 1:10
  5.     C = repmat(A(i),[4,1]);
  6.     B(:,i) = C;
  7. end
  8. B = B(:)'
复制代码



sweet_moon 发表于 2022-5-13 16:37:59
谢中华 发表于 2022-5-13 15:34
parfor中支持的变量有5类:
循环变量(Loop Variables):循环索引
切片变量数组(Sliced Variables):各 ...

那是不是说parfor不能对数组的某一区间段直接赋值,只能对数组确定的一个索引或者矩阵的行、列整体赋值?

谢中华 发表于 2022-5-15 08:32:20
sweet_moon 发表于 2022-5-13 16:37
那是不是说parfor不能对数组的某一区间段直接赋值,只能对数组确定的一个索引或者矩阵的行、列整体赋值? ...

不符合parfor中支持的5种类型就会出错。例如下面的代码就不能正确运行
  1. A = 1:10;
  2. B = zeros(1,40);
  3. %对B进行赋值,满足B(4*i-3:4*i)=A(i)
  4. parfor i = 1:40
  5.     B(1) = A(ceil(i/4));
  6. end
  7. B
复制代码

1119093102 发表于 2022-5-18 15:40:26
谢中华 发表于 2022-5-13 15:34
parfor中支持的变量有5类:
循环变量(Loop Variables):循环索引
切片变量数组(Sliced Variables):各 ...

老师询问一下问题:
1、使用parpool建立进程池,使用parfor运行函数,函数内部使用了global,是不是此时建立的global只会在同一个进程中进行传递。
2、对于你所说的第二种方法,同样是赋值了多个索引的数值,原文为索引多个位置,你的回答是索引了一列,是不是赋值是需要一对一个位置的赋值呢,一定需要临时变量做中转吗?

谢中华 发表于 2022-5-18 22:00:08
1119093102 发表于 2022-5-18 15:40
老师询问一下问题:
1、使用parpool建立进程池,使用parfor运行函数,函数内部使用了global,是不是此时建 ...

在函数中对全局变量初始化就可以了,另外并行循环中不必用临时变量做中转。你看看parfor中支持的5类变量,就知道为什么可以对列或行进行赋值了。下面做一个全局变量的示例:
  1. global A
  2. B = zeros(1,5);
  3. parfor i = 1:5
  4.     B(i) = MyTestFun(i);
  5. end
  6. B
复制代码

其中MyTestFun函数代码如下:
  1. function y = MyTestFun(x)
  2. global A
  3. if isempty(A)
  4.     A = 5;
  5. end
  6. y = x+A;
  7. end
复制代码

我的一点建议:尽量少用全局变量。

1119093102 发表于 2022-5-19 10:01:47
本帖最后由 1119093102 于 2022-5-19 10:10 编辑
谢中华 发表于 2022-5-18 22:00
在函数中对全局变量初始化就可以了,另外并行循环中不必用临时变量做中转。你看看parfor中支持的5类变量 ...

在parfor中少用吗?是会增加通信开销还是会拖慢运行速度,又或者是会有内存错误的风险?我现在的问题是没有你代码中的情况,比如我开了parpool(4),
  1. parfor i = 1: 4
  2. func(i);
  3. end

  4. function func(i)
  5. global a;
  6. a = i;
  7. end
复制代码
这种形式,我在外部并没有定义global a,而func是一个很复杂的函数集合,我通过func将变量输入到func中,将变量赋值给函数内部建立全局变量a,次函数调用a进行运算,此时有多个进程都在运行func,此时多个进程的全局变量a是不是都是独立的?

谢中华 发表于 2022-5-19 16:57:52
1119093102 发表于 2022-5-19 10:01
在parfor中少用吗?是会增加通信开销还是会拖慢运行速度,又或者是会有内存错误的风险?我现在的问题是没 ...

再做一个测试:
先看看普通的for循环
  1. >> global A
  2. >> A = 9;
  3. >> B = zeros(1,5);
  4. >> for i = 1:5
  5.     B(i) = MyTestFun(i);
  6. end
  7. >> B
  8. B =
  9.      2     3     4     5     6
  10. >> A
  11. A =
  12.      5
复制代码

全局变量A的值从9变成了5

再看看parfor循环
  1. >> global A
  2. >> A = 9;
  3. >> B = zeros(1,5);
  4. >> parfor i = 1:5
  5.     B(i) = MyTestFun(i);
  6. end
  7. >> B
  8. B =
  9.      2     3     4     5     6
  10. >> A
  11. A =
  12.      9
复制代码

全局变量A的值仍是9,这说明多个进程的全局变量a是独立的。

其中MyTestFun函数代码如下:
  1. function y = MyTestFun(x)
  2. global A
  3. A = x;
  4. y = subfun(A);
  5. end

  6. function y = subfun(x)
  7. y = x+1;
  8. end
复制代码
您需要登录后才可以回帖 登录 | 注册

本版积分规则

相关帖子
相关文章
热门教程
站长推荐
快速回复 返回顶部 返回列表