【问题标题】:Matlab performances with/without loops带/不带循环的 Matlab 性能
【发布时间】:2014-08-26 05:05:49
【问题描述】:

我在THIS 上读到了关于 SO 的评论,Matlab 在 for 循环中不再慢(c.f. link)。

我在学习期间经常使用 Matlab,我记得通过始终找到不涉及过多循环的解决方案(使用 reshaperepmatarrayfun)节省了多少时间。

所以上面这篇文章引起了我的注意,我很快写了这个:

clear all; T = linspace(0,1,1e6);
tic
i = 0;
for t = T
    i = i + 1; y(i) = sin(t);
end
toc

clear all; T = linspace(0,1,1e6);
tic
i = 0;
y = zeros(numel(T), 1);
for t = T
    i = i + 1; y(i) = sin(t);
end
toc

clear all; T = linspace(0,1,1e6);
tic
y = sin(T);
toc

输出这个:

Elapsed time is 1.741640 seconds.
Elapsed time is 1.400412 seconds.
Elapsed time is 0.004076 seconds.

我还尝试切换accel 功能...

>feature accel on

但每次,即使对于更复杂的矩阵操作,使用原生 Matlab 函数的矢量化版本总是更快。

也许我遗漏了一些重要的观点,或者我的观点仍然正确:使用 Matlab,我们应该始终尽可能避免循环。

现在,我正在寻找一个反例。

【问题讨论】:

    标签: performance matlab loops


    【解决方案1】:

    问题在于不同的人认为“慢”。

    当 MATLAB for 循环从“难以置信的慢”变为“比矢量化版本慢 8 倍”时,将会出现

    1. 有些人惊呼:“哇,MATLAB 循环不再慢了!”
    2. 其他人说“MATLAB 在使用循环时变得更好。仍然不好,但可以忍受。”
    3. 有些人发现“嗯,8 倍的速度仍然很慢。一路矢量化”
    4. 最后一些人得出结论:“C 仍然更快,即使在矢量化代码上也是如此。MATLAB 只是没有削减它。”

    在我看来,MATLAB 的循环速度仍然很慢(我猜我是第三组),您应该尽可能进行矢量化(除非可读性受到影响)。 仅仅因为它过去更慢,并不能使当前的性能更好。

    另外,MATLAB 还有一些其他的弱点:https://stackoverflow.com/a/17933146/1974021

    【讨论】:

    • 我也是第三组 :)
    【解决方案2】:

    可以建议几个例子来研究 for-loopvectorization 的性能。

    示例 #1

    这只是计算多个元素的正弦的一个非常基本的计算。这个元素的数量是变化的,以评估手头的问题。灵感来自this screenshot link

    基准代码

    num_runs = 1000;
    N_arr = [ 1000 10000 100000 1000000];
    
    %// Warm up tic/toc.
    for k = 1:100
        tic(); elapsed = toc();
    end
    
    for k = 1:numel(N_arr)
        N = N_arr(k);
        tic
        for runs=1:num_runs
            out_f1 = zeros(1,N);
            for t = 1:N
                out_f1(t) = sin(t);
            end
        end
        t_forloop = toc/num_runs;
        
        tic
        for runs=1:num_runs
            out_v1 = sin(1:N);
        end
        t_vect = toc/num_runs;
    end
    

    结果

    ----------- Datsize(N) = 1000 -------------
    Elapsed time with for-loops -       7.1826e-05
    Elapsed time with vectorized code - 8.3601e-05
    ----------- Datsize(N) = 10000 -------------
    Elapsed time with for-loops -       0.00068531
    Elapsed time with vectorized code - 0.00045043
    ----------- Datsize(N) = 100000 -------------
    Elapsed time with for-loops -       0.0074613
    Elapsed time with vectorized code - 0.0053368
    ----------- Datsize(N) = 1000000 -------------
    Elapsed time with for-loops -       0.077707
    Elapsed time with vectorized code - 0.053255
    

    请注意,这些结果与timeit 结果一致(此处未显示代码和结果)。

    结论

    • 结果表明,您可以像忘记 for-loops 一样快地忘记 10000 元素案例。

    示例 #2

    让我们考虑在 for 循环的每次迭代中使用元素数组的情况。让它在每次迭代中将sinecosinetansec存储到一列中,即[sin(t) ; cos(t) ; tan(t) ; sec(t)]

    For-loop 代码将是 -

    out_f1 = zeros(4,N);
    for t = 1:N
        out_f1(:,t) = [sin(t) ; cos(t) ; tan(t) ; sec(t)];
    end
    

    矢量化代码 -

    out_v1 = [sin(1:N); cos(1:N) ; tan(1:N); sec(1:N)];
    

    结果

    ----------- Datsize(N) = 100 -------------
    Elapsed time with for-loops - 0.00011861
    Elapsed time with vectorized code - 6.0569e-05
    ----------- Datsize(N) = 1000 -------------
    Elapsed time with for-loops - 0.0011867
    Elapsed time with vectorized code - 0.00036786
    ----------- Datsize(N) = 10000 -------------
    Elapsed time with for-loops - 0.011819
    Elapsed time with vectorized code - 0.0025536
    ----------- Datsize(N) = 1000000 -------------
    Elapsed time with for-loops - 1.2329
    Elapsed time with vectorized code - 0.33383
    

    修改后的情况

    人们很容易得出这样的结论,即 for-loop 在这里没有机会。但是等一下,我们如何再次进行元素分配,例如 for-loop 的示例 #1,像这样 -

    out_f1 = zeros(4,N);
    for t = 1:N
        out_f1(1,t) = sin(t);
        out_f1(2,t) = cos(t);
        out_f1(3,t) = tan(t);
        out_f1(4,t) = sec(t);
    end
    

    现在,这使用空间局部性,因此使用相同的竞争向量化代码将是 -

    out_v1 = [sin(1:N) cos(1:N) tan(1:N) sec(1:N)]';
    

    这个测试用例的这些修改代码的基准测试结果是 -

    ----------- Datsize(N) = 100 -------------
    Elapsed time with for-loops - 3.1987e-05
    Elapsed time with vectorized code - 6.9778e-05
    ----------- Datsize(N) = 1000 -------------
    Elapsed time with for-loops - 0.00027976
    Elapsed time with vectorized code - 0.00036804
    ----------- Datsize(N) = 10000 -------------
    Elapsed time with for-loops - 0.0029712
    Elapsed time with vectorized code - 0.0024423
    ----------- Datsize(N) = 100000 -------------
    Elapsed time with for-loops - 0.031113
    Elapsed time with vectorized code - 0.028549
    ----------- Datsize(N) = 1000000 -------------
    Elapsed time with for-loops - 0.32636
    Elapsed time with vectorized code - 0.28063
    

    结论

    后一个基准测试结果似乎再次证明,对于高达 10000 的元素,for-loop 获胜,之后矢量化解决方案将是首选。但必须注意的是,这是以编写元素分配为代价的。


    最终结论

    1. 关于决定哪一侧(for循环或矢量化)更好的论点,似乎与黑白图片相差甚远。

    【讨论】:

    • 如果运行时间低于 1 秒,您应该使用另一个循环来平均另一个 1e6 执行的运行时间。如果没有这些措施,您将无法产生 1e-5s 量级的可靠运行时间。
    • @pyStarter 这很有用!现在运行时与 timeit 匹配。感谢您指出这个问题。
    【解决方案3】:

    使用真正的循环索引,jit 编译器会理解你的循环:

    clear all; T = linspace(0,1,1e6);
    tic
    y = zeros(numel(T), 1);
    for idx=1:numel(T)
        y(idx) = sin(T(idx));
    end
    toc
    

    这样的代码要快得多。优化基于代码分析,编写清晰的代码并让 matlab 有机会成功分析它;)

    【讨论】:

    • 非常有趣 :) 但仍然比使用原生函数慢
    猜你喜欢
    • 1970-01-01
    • 2014-07-13
    • 1970-01-01
    • 2017-10-28
    • 2015-05-24
    • 2018-04-10
    • 1970-01-01
    • 2018-05-23
    • 1970-01-01
    相关资源
    最近更新 更多