如何在matlab中优化嵌套循环的运行速度

How to optimize the running speed of nested loops in matlab

提问人:Alex 提问时间:11/10/2023 最后编辑:Alex 更新时间:11/10/2023 访问量:65

问:

我需要在 matlab 中处理四维矩阵的元素。哪里是100左右。下面的程序计算起来非常耗时,不知道有没有合适的方法可以稍微简化一下?N

for i1 = 1 : N 
    for j1 = 1 : N 

        t1 = conj(PF(i1, j1));

        if abs(t1) ~= 0
            for i2 = 1 : N 
                for j2 = 1 : N 
                
                    t2 = PF(i2, j2);
                    mumual_intensity(i2, j2, i1, j1) = 
mumual_intensity(i2, j2, i1, j1) * t1 * t2;
                end
            end
        end
    end
end

我知道可以将索引网格修改为矢量计算,但我自己无法成功实现这一点。

MATLAB FOR 循环 嵌套 矢量化

评论

1赞 Martin Brown 11/10/2023
可能值得将 i2 控制循环移到外部,以便改进缓存局部性 - 或者交替交换 i 和 j,以便 j1、j2 循环是外部的,并且在内部循环中多次应用恒定小提琴系数超过 i1、i2。一个或另一个重新排列应该是一个明显的改进。在如此大的矩阵结构上,循环排序将很重要。
2赞 Cris Luengo 11/10/2023
是数组还是函数?如果是函数,请发布代码。乘法原则上是微不足道的矢量化。— 循环的排序在这里非常重要,使内部循环成为第一个索引,使外部循环成为最后一个索引。PF
0赞 Alex 11/10/2023
@CrisLuengo PF 是一个 N*N 二维矩阵。它的价值是直接给出的
1赞 Wolfie 11/10/2023
编辑您的问题,将您的代码变成一个最小的可重现示例,我们可以运行和基准测试,否则建议可能过于推测而无济于事
1赞 chtz 11/10/2023
假设是具有拟合尺寸的二维矩阵,应该可以将内部两个环替换为PFmumual_intensity(:, :, i1, j1) = mumual_intensity(:, :, i1, j1) .* (t1*PF);

答:

3赞 Cris Luengo 11/10/2023 #1

首先,您应该始终对循环进行排序,以便第一个数组维度变化最快(即内部循环)。这就是数据在内存中的存储方式,按内存顺序访问数据可以充分利用缓存。因此,按如下方式重新排序循环应该会大大加快代码速度:

for j1 = 1 : N  % last dimension is outer loop
    for i1 = 1 : N  % second to last dimension

        t1 = conj(PF(i1, j1));

        if abs(t1) ~= 0
            for j2 = 1 : N  % second dimension
                for i2 = 1 : N  % first dimension is inner loop
                
                    t2 = PF(i2, j2);
                    mumual_intensity(i2, j2, i1, j1) = mumual_intensity(i2, j2, i1, j1) * t1 * t2;
                end
            end
        end
    end
end

假设是一个数组,我们可以很容易地对乘法进行矢量化。 是元素乘法。 将自动扩展到与在末尾添加两个维度并沿这些维度复制数据相同的大小。也就是说,每个将乘以 。PF.*mumual_intensity .* PFt2mumual_intensitymumual_intensity(i2, j2, :, :)PF(i2, j2)

对于另一个乘法(除以 ),我们必须首先在开始时添加两个维度,我们可以用 来做。因此,循环可以替换为:t1shiftdim(PF,-2)

mumual_intensity = mumual_intensity .* conj(shiftdim(PF,-2)) .* PF;

在配备 MATLAB R2023b 的 M1 Mac 上,使用随机双精度数组时,我看到原始代码为 ~0.3 秒,重新排序循环为 ~0.2 秒,矢量化代码为 ~0.08 秒。差异不是很大,但对于足够大的增益是好的。N=100N

评论

0赞 Alex 11/11/2023
非常感谢您的回复。这对我非常有帮助和启发。
0赞 Alex 11/13/2023
我无意中发现矢量化的修改方法似乎会导致一些数据丢失。我通过设置 、 检查了图像,发现与原始代码相比,矢量化写入方法缺少边缘数据。我不确定这是为什么?i1=i2j1=j2
0赞 Alex 11/13/2023
这是我用来显示图像的代码。pupil_image_intensity = zeros(N, N); for i = 1 : N for j = 1 : N pupil_image_intensity(i, j) = real(mumual_intensity(i, j, i, j)); end end
0赞 Cris Luengo 11/13/2023
@Alex 我验证了我在这里建议的一行代码产生的结果与您在问题中发布的代码完全相同。边缘肯定不会有任何差异。那一定是你正在做的其他事情引起的。
0赞 Alex 11/14/2023
好的,谢谢你的回复。我想我需要仔细检查它。