在for循环中使用If语句和Continue语句的区别

16

我在Matlab中有一个for循环,循环内的所有代码都被包含在一个if语句中。例如:

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语句进行相等性测试并在结果为真时执行内部代码,是否比测试不相等然后使用continue语句更快?例如:

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; 

或者说,无论哪种方式都没有区别,即在执行时它会得到相同的优化结果吗?

由于这只是微小的优化,所以并不是非常重要,但我很想知道!

编辑:有趣的是,按照建议对代码进行了分析后,后者似乎略微更快 - 如果有人能解释一下那就太好了!(毕竟,在最好的情况下,它是相同的逻辑,但需要执行额外的指令)


据我所知,这应该没有什么区别。 - letmutx
Profiler是你的好朋友。无论如何,很难想象这个检查会成为性能瓶颈。就个人而言,我喜欢使用continue,因为它可以将缩进级别减少一级,尽管这纯粹是基于个人意见的。 - Łukasz Rogalski
1
这是一个很好的问题。我在下面的答案中添加了更详细的分析。 - Suever
1个回答

19

理论上来说,您提出的两种方法在性能上不应该有差异,因为无论如何每次循环都必须评估if语句,但让我们通过一些分析(timeit)来更仔细地查看。我下面列出了一些在版本R2014a到R2015b上的测试结果。

对于这些测试中的每一个,我创建了一个数组p,它包含相同数量的1和0,并随机改变了0和1的顺序。

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

在第一次测试中,我禁用了JIT编译器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 (新执行引擎引入)

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

来自 MATLAB 文档:

对所有 MATLAB 代码进行即时编译

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

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

enter image description here

概述

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

不过,正如您所指出的,if语句仍然略微更快。这可能是由于实际调用continue的开销。在整个大局中,这种差异非常小。此外,如果您的for循环的整体性能确实取决于这种差异,那么这意味着循环中的内容非常快,并且瓶颈是for循环本身,您应该考虑向量化您的代码(即,如果我在我的for循环中放一个magic(3)调用而不是这里显示的简单乘法,差异将完全消失)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接