如果在for循环中vs continue语句

时间:2016-04-17 13:35:25

标签: performance matlab if-statement for-loop

我在Matlab中有一个if循环,for循环中的所有代码都包含在for p = 1:length(array) if array(p) == 1 %// Test positive for condition %// Generic code here that %// Only executes if p == 1 end; end; 语句中。例如:

if

使用for p = 1:length(array) if array(p) ~= 1 %// Test negative for condition continue; %// Skip if negative end; %// Generic code here that %// Only executes if p == 1 end; 语句测试相等性是否更快,如果为true则执行内部代码,或者,测试不等式,然后使用continue语句,例如:

Class Library

或者,两种方式都没有区别,即它在执行时优化到相同的结果?

因为它唯一的微观优化并不是非常重要 - 但我很想知道!

编辑:有趣的是,在按照推荐的方式对代码进行分析后,后者似乎分数更快 - 如果有人愿意解释这将是伟大的! (毕竟,最好是相同的逻辑,但需要执行额外的指令)

1 个答案:

答案 0 :(得分:19)

理论上,你提出的两种方法之间不应该存在性能差异,因为无论如何都必须在每次循环中评估if语句,但让我们仔细看看进行一些分析(timeit)。我在R2014a到R2015b版本下面进行了一些测试。

对于这些测试中的每一个,我创建了一个不同大小的相同数量的1&0和0的数组p,并按0和#1的顺序随机化。 39; S

%// Creates random zeros and ones of size n
p = mod(randperm(n),2);

对于第一个测试,我disabled the JIT compiler feature('JIT', 'off')和第二个测试我启用了JIT编译器feature('JIT', 'on')

我用于所有MATLAB版本的脚本是:

function tests()
    V = ver;

    sz = round(linspace(100, 10000,100));

    hfig = figure('Position', [0 0 900 400]);

    %// Disable JIT
    feature('JIT', 'off')
    runtests(sz, 1, ['JIT Disabled ', V(1).Release]);

    %// Enable JIT
    feature('JIT', 'on')
    runtests(sz, 2, ['JIT Enabled ', V(1).Release]);

    %// Match up ylims on all plots
    ax = findall(hfig, 'type', 'axes');
    ylims = get(ax, 'ylim');
    ylims = cat(1, ylims{:});
    set(ax, 'ylim', [0, max(ylims(:,2))])
end

function out = runtests(sz, n, label)

    times1 = zeros(numel(sz), 1);
    times2 = zeros(numel(sz), 1);

    for k = 1:numel(sz)
        p = mod(randperm(sz(k)),2);
        times1(k) = timeit(@()continueit(p));
        p = mod(randperm(sz(k)),2);
        times2(k) = timeit(@()ifit(p));
    end

    subplot(1,2,n)
    plots(sz, cat(2, times1, times2))
    title(label)
end

function plots(sz, times)
    plot(sz, times * 1000)
    legend({'Continue', 'If'})
    xlabel('Size of Array')
    ylabel('Execution Time (ms)')
end

function continueit(p)
    c = 1;
    for k = 1:numel(p)
        if p(k) ~= 1
            continue;
        end

        c = c * k;
    end
end

function ifit(p)
    c = 1;
    for k = 1:numel(p)
        if p(k) == 1
            c = c * k;
        end
    end
end

R2014a

正如您在此处所看到的,continueif语句的非常性能相似但没有启用JIT加速。但是,当您打开加速度(MATLAB的默认值)时, if语句似乎会加速。 continue方法的速度保持相对不变。因此,if语句执行得更快。

enter image description here

这可能是由于JIT编译器加速连续多次执行的指令块。通过在其中粘贴分支逻辑continue,您将根据条件更改程序的流程,这会更改每次循环运行的指令。这显然是在阻止JIT编译。

R2014b

与R2014a类似。

enter image description here

R2015a

与R2014a类似。

enter image description here

R2015b(new execution engine介绍)

不幸的是,在R2015b中,您似乎无法以相同的方式禁用JIT(如果有人知道如何,让我知道并且我会更新),所以这两个图都启用了加速,但是似乎新的执行引擎消除了JIT编译器之前创建的执行时间的差异。这是因为新的执行引擎能够JIT编译所有代码(包括显然是continue

来自MATLAB文档:

  

即时编译所有MATLAB代码

     

重新设计的MATLAB执行引擎使用所有MATLAB代码的JIT编译,而执行引擎以前在某些情况下使用JIT编译。 JIT编译生成本机机器级代码,该代码针对正在执行的MATLAB代码和特定硬件平台进行了优化。

     

当MATLAB代码执行额外的次数并且可以重复使用已编译的代码时,JIT编译的性能优势最大。这种情况发生在常见的情况下,例如for循环,或者在MATLAB会话中运行应用程序时,其中至少某些应用程序的MATLAB文件在后续运行之间保持不变。

enter image description here

摘要

在早期版本的MATLAB(R2015a及更早版本)中,continue语句阻止了JIT加速,导致if版本在启用JIT时执行得更快(默认情况下)。随着R2015b中新执行引擎的引入,所有代码都是JIT加速的,因此,差异实际上已经消失了。

正如您所指出的那样,if语句只是勉强更快。这可能是由于实际调用continue的开销。在宏观方案中,这种差异可以忽略不计。此外,如果for循环的整体性能确实取决于这种差异,则意味着循环的内容的速度非常快,并且您的瓶颈是{ {1}}循环本身,您应该考虑对代码进行矢量化(即如果我在for循环中调用magic(3)调用而不是此处显示的简单乘法,则差异将完全消失。)