由于许多嵌套循环,我的代码的这一部分(如下所示)需要很长时间才能运行。有没有办法可以避免这些嵌套循环使其运行得更快?
for k = 1:numel(UpscaledZLen.pq)
for j = 1:numel(UpscaledRowLen.pq)
for i = 1:numel(UpscaledColLen.pq)
iZ = 1;
while iZ <= UpscaledZLen.pq(k)
for ZLag = 1:iZ
ihRow = 1;
while ihRow <= UpscaledRowLen.pq(j)
for hRowLag = 1:ihRow
ihCol = 1;
while ihCol <= UpscaledColLen.pq(i)
for hColLag = 1:ihCol
temp1(hColLag) = trapz(AnalGamma(hRowLag,...
1:hColLag,...
ZLag));
end
temp2(ihCol) = trapz(temp1);
ihCol = ihCol + 1;
end
temp3(hRowLag) = trapz(temp2);
end
temp4(ihRow) = trapz(temp3);
ihRow = ihRow + 1;
end
temp5(ZLag) = trapz(temp4);
end
temp6(iZ) = trapz(temp5);
iZ = iZ + 1;
end
NormVariance_AnalCorrAvg(i, j, k) = (2/((UpscaledRowLen.pq(j)*...
UpscaledColLen.pq(i)*UpscaledZLen.pq(k))^2))*trapz(temp6);
end
end
end
这段代码试图实现以下整数表达式:
编辑(SSCCE) :对于一个简短的示例,我采用了以下变量大小,然后使用上面的代码来查看为这些特定变量大小运行所需的时间:
nRow = 4;
nCol = 4;
nZ = 4;
RowLenScale.pq = 1:nRow;
ColLenScale.pq = 1:nCol;
UpscaledRowLen.pq = RowLenScale.pq(rem(RowLenScale.pq(end), RowLenScale.pq) == 0);
UpscaledColLen.pq = RowLenScale.pq(rem(ColLenScale.pq(end), ColLenScale.pq) == 0);
ZLenScale.pq = 1:nZ;
UpscaledZLen.pq = ZLenScale.pq(rem(ZLenScale.pq(end), ZLenScale.pq) == 0);
AnalGamma = rand(nRow, nCol, nZ);
对于此示例,它只需 0.321976秒,而对于nRow = 100;
,nCol = 100;
和nZ = 20;
的原始情况,它花了超过24小时仍在k = 4
处运行最外层循环索引。
答案 0 :(得分:7)
首先,您的代码有问题,因为临时变量未初始化,并且上次迭代中剩余的值仍然存在于下一个迭代中。在更正此问题并将while
替换为for
循环后,代码如下所示:
NormVariance_AnalCorrAvg = nan(numel(UpscaledColLen.pq), numel(UpscaledRowLen.pq), numel(UpscaledZLen.pq));
for k = 1 : numel(UpscaledZLen.pq)
for j = 1 : numel(UpscaledRowLen.pq)
for i = 1 : numel(UpscaledColLen.pq)
temp6 = nan(1, UpscaledZLen.pq(k));
for iZ = 1 : UpscaledZLen.pq(k)
temp5 = nan(1, iZ);
for ZLag = 1 : iZ
temp4 = nan(1, UpscaledRowLen.pq(j));
for ihRow = 1 : UpscaledRowLen.pq(j)
temp3 = nan(1, ihRow);
for hRowLag = 1 : ihRow
temp2 = nan(1, UpscaledColLen.pq(i));
for ihCol = 1 : UpscaledColLen.pq(i)
temp1 = nan(1, ihCol);
for hColLag = 1:ihCol
temp1(hColLag) = trapz(AnalGamma(hRowLag,...
1:hColLag,...
ZLag));
end
temp2(ihCol) = trapz(temp1);
end
temp3(hRowLag) = trapz(temp2);
end
temp4(ihRow) = trapz(temp3);
end
temp5(ZLag) = trapz(temp4);
end
temp6(iZ) = trapz(temp5);
end
NormVariance_AnalCorrAvg(i, j, k) = (2/((UpscaledRowLen.pq(j)*...
UpscaledColLen.pq(i)*UpscaledZLen.pq(k))^2))*trapz(temp6);
end
end
end
这里的初始化也会导致数组的预分配,这是加速循环的基本建议之一。但是,在这种情况下,它没有效果。
为了对代码进行基准测试,我使用rand
使用您的代码段,但使用
nRow = 20;
nCol = 20;
nZ = 2;
在我的计算机上,清理后的代码需要25.5秒才能完成。
如何让它更快?好旧的矢量化:
hColLag
上的最内层循环可以用cumtrapz
替换,将运行时间减少到8.2秒。
temp1 = cumtrapz(AnalGamma(hRowLag, 1 : ihCol, ZLag), 2);
同样适用于ihCol
上的循环,将运行时间减少到1.85秒。
temp2 = cumtrapz(cumtrapz(AnalGamma(hRowLag, 1 : UpscaledColLen.pq(i), ZLag), 2), 2);
hRowLag
上的下一个外环仅用于计算几个类似的trapz
。这是没有必要的,因为trapz
是完全矢量化的;循环可以被一次调用替换,这会将运行时间减少到0.34秒。
temp3 = trapz(cumtrapz(cumtrapz(AnalGamma(1 : ihRow, 1 : UpscaledColLen.pq(i), ZLag), 2), 2), 2);
ihRow
上的循环计算累积积分;使用cumtrapz
使运行时间缩短到大约0.07秒。
temp4 = cumtrapz(trapz(cumtrapz(cumtrapz(AnalGamma(1 : UpscaledRowLen.pq(j), 1 : UpscaledColLen.pq(i), ZLag), 2), 2), 2), 1);
对iZ
和ZLag
上的循环应用相同的逻辑,我们得到以下代码
NormVariance_AnalCorrAvg = nan(numel(UpscaledColLen.pq), numel(UpscaledRowLen.pq), numel(UpscaledZLen.pq));
for k = 1 : numel(UpscaledZLen.pq)
for j = 1 : numel(UpscaledRowLen.pq)
for i = 1 : numel(UpscaledColLen.pq)
temp6 = cumtrapz(trapz(cumtrapz(trapz(cumtrapz(cumtrapz(...
AnalGamma(1 : UpscaledRowLen.pq(j), 1 : UpscaledColLen.pq(i), 1 : UpscaledZLen.pq(k)), ...
2), 2), 2), 1), 1), 3);
NormVariance_AnalCorrAvg(i, j, k) = (2/((UpscaledRowLen.pq(j)*...
UpscaledColLen.pq(i)*UpscaledZLen.pq(k))^2))*trapz(temp6);
end
end
end
运行时间为0.043秒,相当于600倍。
编写所有这些嵌套的trapz
和cumtrapz
当然非常容易出错。我使用了一个校验和应用于保存到文件的结果,以确保我的代码更改不会更改它。不过,你应该仔细检查才能完全确定。
可能更进一步。前三个for循环再次执行累积-y,因此可能需要更多cumtrapz
。 trapz
实现效率不高,因为它有很多错误检查代码。由于它的作用基本上是平均值的总和(参见trapezoidal rule),因此在整个网格上预先计算多维平均值将允许将这些调用转换为对sum
的调用。比梯形规则更简单的是rectangle method,您可以计算函数的值,而不是在边缘处,而是在网格单元格的中心。当然,您可以尝试分析地部分计算积分;你可以降低网格的分辨率。
希望这有帮助!
答案 1 :(得分:0)
您可以采取一些措施来改善效果。首先,您可以使用for循环替换while循环。其次,你可以用sum替换trapz,一个集成。如果用sum替换,而不是计算每个循环中的和,而不是在最后计算总和,那么你将获得一些改进。最后,您可以提取最终的乘法计算。
伽玛函数的输入是真的(hRowLag,1:hColLag,ZLag)还是它(hRowLag,hColLag,ZLag)。如果可以对gamma函数进行矢量化,则可以构建一个网格来提供函数,而不是循环。
temp = zeros(max(UpscaledZLen.pq),max(UpscaledZLen.pq),max(UpscaledRowLen.pq),max(UpscaledRowLen.pq),max(UpscaledColLen.pq),max(UpscaledColLen.pq));
my_integral = zeros(numel(UpscaledZLen.pq), numel(UpscaledRowLen.pq), numel(UpscaledColLen.pq));
for k = 1:numel(UpscaledZLen.pq)
for j = 1:numel(UpscaledRowLen.pq)
for i = 1:numel(UpscaledColLen.pq)
for iZ = 1:UpscaledZLen.pq(k)
for ZLag = 1:iZ
for ihRow = 1:UpscaledRowLen.pq(j)
for RLag = 1:ihRow
for ihCol = 1:UpscaledColLen.pq(i)
for CLag = 1:ihCol
temp(iZ,ZLag,ihRow,RLag,ihCol,CLag,:) = AnalGamma(RLag,1:CLag,ZLag);
end
end
end
end
end
end
my_integral(i,j,k) = sum(temp(:));
end
end
end
my_z = repmat(permute(UpscaledZLen.pq(:),[3,2,1]),[numel(UpscaledRowLen.pq),numel(UpscaledColLen.pq),1]);
my_r = repmat(permute(UpscaledRowLen.pq(:),[2,1]),[numel(UpscaledRowLen.pq),1,numel(UpscaledZLen.pq)]);
my_c = repmat(UpscaledColLen.pq(:),[1,numel(UpscaledColLen.pq),numel(UpscaledZLen.pq)]);
NormVariance_AnalCorrAvg = my_integral .* 2 ./ (my_z .* my_r .* my_c).^2;