Matlab parfor 切片正确
Matlab parfor slice correctly
我有两个要并行化的嵌套循环。
n=100;
x=rand(1,n);
m=5;
xx=rand(1,m);
r = zeros(1,m);
for i=1:n
q = ones(1,m);
for j=1:n
q = q .* (xx-x(j))/(x(i)-x(j));
end
r = r + q;
end
为了准备这个函数进行腭化,我将局部变量更改为全局变量。
n=100;
x=rand(1,n);
m=5;
xx=rand(1,m);
r = ones(n,m);
for i=1:n
for j=1:n
r(i,:) = r(i,:) .* (xx-x(j))/x(i)-x(j))
end
end
r = sum(r,1);
与其一次变换整个向量,不如尝试只用一个标量。还使用依赖于 i 和 j 的 x 的最简单元素。我最后也删除了 sum
。我们可以稍后再添加回来。
n=100;
x=rand(1,n);
r = ones(n,1);
for i=1:n
for j=1:n
y = x(i)+x(j);
r(i) = r(i) * y;
end
end
上面的代码是示例函数,我想并行化。
对于外循环 i
的一次迭代,内循环总是需要访问相同的向量 r(i)
。此访问是一个 write 操作 (*=
),但 order 对于此操作无关紧要。
因为嵌套 parfor
循环在 Matlab 中是不允许的,我试图将所有东西打包在一个 parfor
循环中。
n=100;
x=rand(1,n);
r = ones(n,1);
parfor k=1:(n*n)
%i = floor((k-1)/n)+1; % outer loop
%j = mod(k-1,n)+1; % inner loop
[j,i] = ind2sub([n,n],k);
y = x(i)+x(j);
r(i) = r(i) * y; % ERROR here
end
由于独立计算,Matlab 仍然不知道如何对其进行切片。
因此,我决定将乘法运算移到外面并使用线性索引。
n=100;
x=rand(1,n);
r = ones(n,n);
parfor k=1:(n*n)
[j,i] = ind2sub([n,n],k);
y = x(i)+x(j);
r(k) = y;
end
r = prod(r,1);
r = squeeze(r); % remove singleton dimensions
虽然这对内部循环中的标量值有效,但对内部循环中的向量无效,因为必须重新计算索引。
n=100;
x=rand(1,n);
m=5;
r = ones(n,n,m);
parfor k=1:(n*n)
[j,i] = ind2sub([n,n],k);
y = x(i)+x(j);
r((k-1)*m+1:k*m) = y.*(1:m); % ERROR here
end
r = prod(r,1);
r = squeeze(r); % remove singleton dimensions
虽然它确实有效,但当我重塑数组时。
n=100;
x=rand(1,n);
m=5;
r = ones(n*n,m);
parfor k=1:(n*n)
[j,i] = ind2sub([n,n],k);
y = x(i)+x(j);
r(k,:) = y.*(1:m); % ERROR here
end
r = reshape(r,n,n,m);
r = prod(r,2);
r = squeeze(r); % remove singleton dimensions
这样,我可以将向量 xx
转换为另一个向量 r
。
n=100;
x=rand(1,n);
m=5;
xx=rand(1,m);
r = ones(n*n,m);
parfor k=1:(n*n)
[j,i] = ind2sub([n,n],k);
y = x(i)+x(j);
r(k,:) = y.*xx; % ERROR here
end
r = reshape(r,n,n,m);
r = prod(r,2);
r = sum(r,1);
r = reshape(r,size(xx)); % reshape output vector to input vector
对于我的并行解决方案,我需要一个 n*n*m
数组而不是 n*m
数组,这看起来效率很低。
有没有更好的方法来做我想做的事?
其他方式的优点是什么(更漂亮的代码,更少 CPU,更少的 RAM,...)?
更新
为了尝试简化任务并将其减少到问题的最小工作示例,我省略了 i~=j
的检查以使其更容易,尽管导致所有 NaN
结果。此外,代码的性质在添加此检查时会导致 all 1
结果。为了使代码有意义,这些因素只是另一个向量 z
.
的权重
更详细的问题如下:
n=100;
x=rand(1,n);
z=rand(1,n);
m=5;
xx=rand(1,m);
r = zeros(1,m);
for i=1:n
q = ones(1,m);
for j=1:n
if i~=j
q = q .* (xx-x(j))/(x(i)-x(j));
end
end
r = r + z(i) .* q;
end
这道题不需要任何并行for循环来执行。一个问题是 x(i)-x(j)
被重复计算了很多次。这是低效的。建议的方法只计算每个数字一次,并将 xx
中每个元素的操作向量化。由于 xx
是迄今为止最短的向量,因此它几乎完全向量化了。如果您还想对最后一个循环进行矢量化,这可能就像一个隐藏的 for 循环一样,它将占用更多内存并且代码会更复杂(例如 3D 矩阵等)。为了测试,我自由地将分母中的减号切换为加号。减号将为所有数字生成 NaN。最后一种方法稍微快一些。 n=10000 时大约 10 次。我建议您尝试更详细的基准测试。
function test()
% Initiate variables
n=100;
x=rand(1,n);
m=5;
xx=rand(1,m);
tic;
% Alternative 1
r = zeros(1,m);
for i=1:n
q = ones(1,m);
for j=1:n
q = q .* (xx-x(j))/(x(i)+x(j));
end
r = r + q;
end
toc;
tic;
% Alternative 2
xden = bsxfun(@plus, x, x.'); % Calculate denominator
xnom = repmat(x,n,1); % Calculate nominator
xfull = (xnom./xden).'; % calculate right term on rhs.
for (k = 1:m)
tmp= prod(xx(k)./xden - xfull); % Split in 2 calculations
r2(k) = sum(tmp); % "r = r + xx(k)"
end
toc;
disp(r);
disp(r2);
最后只是一个注释。备选方案 2 更快,但它的内存也很昂贵,因此在内存问题的情况下最好使用循环。此外,在并行化的情况下不需要全局变量。如果你需要这个,你可能需要检查你的设计(但如果代码很短,没有一些关键的东西,那么你就不需要那么麻烦了)。
我有两个要并行化的嵌套循环。
n=100;
x=rand(1,n);
m=5;
xx=rand(1,m);
r = zeros(1,m);
for i=1:n
q = ones(1,m);
for j=1:n
q = q .* (xx-x(j))/(x(i)-x(j));
end
r = r + q;
end
为了准备这个函数进行腭化,我将局部变量更改为全局变量。
n=100;
x=rand(1,n);
m=5;
xx=rand(1,m);
r = ones(n,m);
for i=1:n
for j=1:n
r(i,:) = r(i,:) .* (xx-x(j))/x(i)-x(j))
end
end
r = sum(r,1);
与其一次变换整个向量,不如尝试只用一个标量。还使用依赖于 i 和 j 的 x 的最简单元素。我最后也删除了 sum
。我们可以稍后再添加回来。
n=100;
x=rand(1,n);
r = ones(n,1);
for i=1:n
for j=1:n
y = x(i)+x(j);
r(i) = r(i) * y;
end
end
上面的代码是示例函数,我想并行化。
对于外循环 i
的一次迭代,内循环总是需要访问相同的向量 r(i)
。此访问是一个 write 操作 (*=
),但 order 对于此操作无关紧要。
因为嵌套 parfor
循环在 Matlab 中是不允许的,我试图将所有东西打包在一个 parfor
循环中。
n=100;
x=rand(1,n);
r = ones(n,1);
parfor k=1:(n*n)
%i = floor((k-1)/n)+1; % outer loop
%j = mod(k-1,n)+1; % inner loop
[j,i] = ind2sub([n,n],k);
y = x(i)+x(j);
r(i) = r(i) * y; % ERROR here
end
由于独立计算,Matlab 仍然不知道如何对其进行切片。 因此,我决定将乘法运算移到外面并使用线性索引。
n=100;
x=rand(1,n);
r = ones(n,n);
parfor k=1:(n*n)
[j,i] = ind2sub([n,n],k);
y = x(i)+x(j);
r(k) = y;
end
r = prod(r,1);
r = squeeze(r); % remove singleton dimensions
虽然这对内部循环中的标量值有效,但对内部循环中的向量无效,因为必须重新计算索引。
n=100;
x=rand(1,n);
m=5;
r = ones(n,n,m);
parfor k=1:(n*n)
[j,i] = ind2sub([n,n],k);
y = x(i)+x(j);
r((k-1)*m+1:k*m) = y.*(1:m); % ERROR here
end
r = prod(r,1);
r = squeeze(r); % remove singleton dimensions
虽然它确实有效,但当我重塑数组时。
n=100;
x=rand(1,n);
m=5;
r = ones(n*n,m);
parfor k=1:(n*n)
[j,i] = ind2sub([n,n],k);
y = x(i)+x(j);
r(k,:) = y.*(1:m); % ERROR here
end
r = reshape(r,n,n,m);
r = prod(r,2);
r = squeeze(r); % remove singleton dimensions
这样,我可以将向量 xx
转换为另一个向量 r
。
n=100;
x=rand(1,n);
m=5;
xx=rand(1,m);
r = ones(n*n,m);
parfor k=1:(n*n)
[j,i] = ind2sub([n,n],k);
y = x(i)+x(j);
r(k,:) = y.*xx; % ERROR here
end
r = reshape(r,n,n,m);
r = prod(r,2);
r = sum(r,1);
r = reshape(r,size(xx)); % reshape output vector to input vector
对于我的并行解决方案,我需要一个 n*n*m
数组而不是 n*m
数组,这看起来效率很低。
有没有更好的方法来做我想做的事?
其他方式的优点是什么(更漂亮的代码,更少 CPU,更少的 RAM,...)?
更新
为了尝试简化任务并将其减少到问题的最小工作示例,我省略了 i~=j
的检查以使其更容易,尽管导致所有 NaN
结果。此外,代码的性质在添加此检查时会导致 all 1
结果。为了使代码有意义,这些因素只是另一个向量 z
.
更详细的问题如下:
n=100;
x=rand(1,n);
z=rand(1,n);
m=5;
xx=rand(1,m);
r = zeros(1,m);
for i=1:n
q = ones(1,m);
for j=1:n
if i~=j
q = q .* (xx-x(j))/(x(i)-x(j));
end
end
r = r + z(i) .* q;
end
这道题不需要任何并行for循环来执行。一个问题是 x(i)-x(j)
被重复计算了很多次。这是低效的。建议的方法只计算每个数字一次,并将 xx
中每个元素的操作向量化。由于 xx
是迄今为止最短的向量,因此它几乎完全向量化了。如果您还想对最后一个循环进行矢量化,这可能就像一个隐藏的 for 循环一样,它将占用更多内存并且代码会更复杂(例如 3D 矩阵等)。为了测试,我自由地将分母中的减号切换为加号。减号将为所有数字生成 NaN。最后一种方法稍微快一些。 n=10000 时大约 10 次。我建议您尝试更详细的基准测试。
function test()
% Initiate variables
n=100;
x=rand(1,n);
m=5;
xx=rand(1,m);
tic;
% Alternative 1
r = zeros(1,m);
for i=1:n
q = ones(1,m);
for j=1:n
q = q .* (xx-x(j))/(x(i)+x(j));
end
r = r + q;
end
toc;
tic;
% Alternative 2
xden = bsxfun(@plus, x, x.'); % Calculate denominator
xnom = repmat(x,n,1); % Calculate nominator
xfull = (xnom./xden).'; % calculate right term on rhs.
for (k = 1:m)
tmp= prod(xx(k)./xden - xfull); % Split in 2 calculations
r2(k) = sum(tmp); % "r = r + xx(k)"
end
toc;
disp(r);
disp(r2);
最后只是一个注释。备选方案 2 更快,但它的内存也很昂贵,因此在内存问题的情况下最好使用循环。此外,在并行化的情况下不需要全局变量。如果你需要这个,你可能需要检查你的设计(但如果代码很短,没有一些关键的东西,那么你就不需要那么麻烦了)。