当我无法对计算进行矢量化时,如何加速Matlab嵌套for循环?

时间:2015-05-04 09:55:04

标签: matlab

我有三个大小相同的3D阵列[41 * 141 * 12403],在alpha,beta和ni下面的Matlab代码中命名。从它们我需要计算另一个具有相同大小的3D数组,它是通过计算结合无限和和定积分计算从原始矩阵元素获得的,使用每个元素的值。因此,必须使用几个嵌套循环来进行此计算似乎是不可避免的。代码已经运行了几个小时(!),它仍然在外循环的第一次迭代中(需要执行41次!!根据我的计算,这样程序将运行超过两年!!!)。我不知道如何优化代码。请帮我 !!

我使用的代码:

    z_len=size(KELDYSH_PARAM_r_z_t,1);   % 41 rows
    r_len=size(KELDYSH_PARAM_r_z_t,2);   % 141 columns   
    t_len=size(KELDYSH_PARAM_r_z_t,3);   % 12403 slices

    sumRes=zeros(z_len,r_len,t_len);

    for z_ind=1:z_len
        z_ind     % in order to track the advancement of the calculation
        for r_ind=1:r_len
            for t_ind=1:t_len
                sumCurrent=0;
                sumPrevious=inf;
                s=0;

                while abs(sumPrevious-sumCurrent)>1e-6
                    kapa=kapa_0+s;    %some scalar
                    x_of_w=(beta(z_ind,r_ind,t_ind).*(kapa-ni...
                       (z_ind,r_ind,t_ind))).^0.5;               
                    sumPrevious=sumCurrent;
                    sumCurrent=sumCurrent+exp(-alpha(z_ind,r_ind,t_ind).* ...
                        (kapa-ni(z_ind,r_ind,t_ind))).*(x_of_w.^(2*abs(m)+1)/2).* ...
                            w_m_integral(x_of_w,m);
                    s=s+1;
                end

                sumRes(z_ind,r_ind,t_ind)=sumCurrent;
            end
        end
    end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function  res=w_m_integral(x_of_w,m)

    res=quad(@integrandFun,0,1,1e-6);

    function y=integrandFun(t)
            y=exp(-x_of_w^2*t).*t.^(abs(m))./((1-t).^0.5);
    end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

1 个答案:

答案 0 :(得分:2)

选项1 - 更多矢量化

这是一个非常复杂的模型,你并没有解释所有术语,但有些部分仍然可以进一步矢量化。您的alphabetani矩阵可能是静态的并且是预先计算的?您的s值是标量,kapa也可以是,因此您可以一次性预先计算x_of_w矩阵。这会让你自己获得一个非常小的加速,虽然你会花费内存来获得它 - 这些天可以使用7100万分,但是需要大量的硬件。对41行中的每一行进行一次操作可以减轻负担。

留下积分本身。 quad函数不接受向量输入 - 这不是一场噩梦吗? - integral也没有,Mathworks建议您使用它。但是,如果你的集成限制在每种情况下是相同的,那么为什么不以老式的方式进行积分?计算被积函数的矩阵为1,计算被积函数的另一个矩阵为0,然后取差值。

然后你可以编写一个循环来计算整个输入空间的积分,然后测试所有矩阵元素的收敛。制作一个掩码,记下未收敛的掩码,并重新计算增加s的掩码。重复直到所有已经收敛(或者你达到迭代的阈值)。

选项2 - 并行化

过去的情况是matlab的矢量化操作要比循环快得多。我现在无法找到它的来源,但我想我已经读到它最近使用for循环变得更快,所以根据您可用的资源而定通过并行化您当前的代码可能会获得更好的结果。这也需要一些重构 - 大问题是将数据复制到工作人员(可以通过将输入切换成块并只是输入相关的数据来修复)和{{ 1}}循环不允许你使用某些变量,通常是覆盖整个空间的变量。再次砍掉它们会有所帮助。

但是如果你有2年的运行时间,你需要一个至少100我猜测的因子,这意味着一个集群!如果您在大学或某个地方,您可以在500核集群上获得几天,那就去吧......

如果您可以以封闭形式编写积分,那么它可能适合GPU计算。这些东西可以非常快速地进行某些类别的计算,但是你必须能够并行化工作并将实际计算减少到主要由加法和乘法组成的基本计算。 CUDA图书馆做了大量的工作,matlab has an interface to them所以请读一读。

选项3 - 缩小范围

最后,如果上述两个都没有产生足够的加速,那么您可能必须缩小计算范围。尽可能多地修剪输入空间,并且可能接受较低的收敛阈值。如果您知道在最里面的parfor循环(其中包含while计数器的循环)中需要多少次迭代,那么可能会发现减少收敛标准会减少您需要的迭代次数,这可以加快它。分析器可以帮助您了解自己的时间。

但最重要的是,7100万点需要一些时间来计算。你可以只对目前的计算进行优化,对于这个大小的问题你可能需要在它上面抛出硬件。