在 Octave 中优化 for 循环中的矩阵计算

Optimizing matrix calculations in for loops in Octave

我将代码从 Matlab 导入到 Octave,某些功能的速度似乎下降了。 我研究了矢量化,但以我有限的知识无法提出解决方案。 我想问一下,有什么办法可以加快速度吗?

    n = 181;
    N = 250;
    for i=1:n    
        for j=1:n
            par=0;
            for k=1:N;
               par=par+log2(1+(10.^(matrix1(j,i,matrix2(j,i))./10)./(matrix3(j,i).*double1+double2)));
            end
            resultingMatrix(j,i)=2.^((1/N).*par)-1;
        end
    end

尺寸为:

    matrix1 = 181x181x2,  
    matrix2 = 181x181 -->  containing values either 1 or 2 only,  
    matrix3 = 181x181,  
    double1, double2 = just doubles

这是我的测试代码,我已经通过制作一些随机矩阵完成了你的代码:

n = 181;
N = 250;
matrix1 = rand(n,n,2);
matrix2 = randi(2,n,n);
matrix3 = rand(n,n);
double1 = 1;
double2 = 1;
tic
for i=1:n
   for j=1:n
      par=0;
      for k=1:N
         par=par+log2(1+(10.^(matrix1(j,i,matrix2(j,i))./10)./(matrix3(j,i).*double1+double2)));
      end
      resultingMatrix(j,i)=2.^((1/N).*par)-1;
   end
end
toc

请注意 k 循环中的代码不使用 k。这使得循环变得多余。我们可以很容易地删除它。循环执行相同的计算 250 次,将结果相加,然后除以 250,得到重复计算之一的值。

另一件重要的事情是preallocate resultingMatrix,以避免它随着每次循环迭代而增长。

这是结果代码:

tic
resultingMatrix2 = zeros(n,n);
for i=1:n
   for j=1:n
      par=log2(1+(10.^(matrix1(j,i,matrix2(j,i))./10)./(matrix3(j,i).*double1+double2)));
      resultingMatrix2(j,i)=2.^par-1;
   end
end
toc
max(abs((resultingMatrix(:)-resultingMatrix2(:))./resultingMatrix(:)))

最后一行计算最大相对差。在我的 Octave 版本中是 9.9424e-15。它会根据版本、系统等而有所不同。这个误差就是浮点数舍入误差。请注意,原始代码将相同的值相加 250 次,然后除以 250,将产生比修改后的代码更大的舍入误差。例如,

x = pi;
t = 0;
for i = 1:N
   t = t + x;
end;
t = t / N;
t-x

给出 -8.4377e-15,与我们在上面看到的类似的舍入误差。

原代码耗时81.5秒,修改后的代码仅耗时0.4秒。这不是矢量化的好处,它只是预分配的好处,而不是一遍又一遍地重复相同的计算。

接下来,我们可以通过向量化运算来移除另外两个循环。这里的难点是 matrix1(j,i,matrix2(j,i))。我们可以用 (1:n*n).' + (matrix2(:)-1)*(n*n) 生成每个 n*n 线性索引。这不是微不足道的,我建议你考虑一下这个计算是如何工作的。您需要知道线性索引计数,从左上角数组元素的 1 开始,首先向下,然后向右,然后沿着第 3 个维度。所以 1:n*n 只是二维数组中每个元素的线性索引,按顺序排列。如果我们需要访问第 3 个维度上的第 2 个元素,我们向其中的每一个添加 n*n

我们现在有了代码

tic
index = reshape((1:n*n).' + (matrix2(:)-1)*(n*n), n, n);
par = log2(1+(10.^(matrix1(index)./10)./(matrix3.*double1+double2)));
resultingMatrix3 = 2.^par-1;
toc
max(abs((resultingMatrix(:)-resultingMatrix3(:))./resultingMatrix(:)))

此代码产生的结果与我以前的版本完全相同,运行时间仅为 0.013 秒,比非矢量化代码快 30 倍,比非矢量化代码快 6000 倍原代码。