Matlab parfor切片正确

时间:2016-08-02 12:31:58

标签: arrays matlab optimization slice parfor

我有两个嵌套循环,我想并行化。

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);

不要一次转换整个矢量,而是让它只用一个标量来尝试。还使用x的最简单元素,它取决于i和j。我最后还删除了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

上面的代码是示例函数,我想并行化。

对于外循环r(i)的一次迭代,内部循环总是需要访问相同的向量i。此访问权限是写入操作(*=),但订单对此操作无关紧要。

由于Matlab中不允许嵌套parfor循环,因此我尝试将所有内容打包在一个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

由于计算了indies,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,更少的内存,......)?

更新

按照尝试简化任务并将其减少到问题的最小工作示例的顺序,我省略了i~=j的检查以使其更容易,尽管导致了所有NaN结果。此外,添加此检查时,代码的性质会导致所有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

1 个答案:

答案 0 :(得分:1)

此问题不需要任何并行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更快但它也是内存昂贵的,因此在内存问题的情况下,更喜欢循环。此外,在并行化的情况下不需要全局变量。如果您需要这个,您可能需要查看您的设计(但是如果代码很短,则没有一些关键,那么您就不必费心了。)

相关问题