For 循环中的 If vs Continue 语句

Posted

技术标签:

【中文标题】For 循环中的 If vs Continue 语句【英文标题】:If vs Continue statement in a for loop 【发布时间】:2016-08-09 04:53:33 【问题描述】:

我在 Matlab 中有一个 for 循环,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; 

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

由于它只是一个微优化,它并不是非常重要 - 但我很想知道!

编辑:有趣的是,在按照建议分析代码后,后者似乎部分更快 - 如果有人愿意解释,那就太好了! (毕竟,充其量是相同的逻辑,但需要执行额外的指令)

【问题讨论】:

据我所知,应该没有区别。 Profile it and find out Profiler 是你的朋友。无论如何,很难想象这种检查会成为性能瓶颈。就我个人而言,我喜欢继续,因为它会将缩进级别降低 1,尽管它纯粹是基于意见的。 这是一个很好的问题。我在下面的回答中添加了更彻底的分析。 【参考方案1】:

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

对于这些测试中的每一个,我都会创建一个大小不一的数组p,其中包含相同数量的 1 和 0,并随机排列 0 和 1 的顺序。

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

第一次测试,我disabled the JIT compilerfeature('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 语句将执行得更快。

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

R2014b

类似于 R2014a。

R2015a

类似于 R2014a。

R2015b(new execution engine 介绍)

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

来自 MATLAB 文档:

所有 MATLAB 代码的即时编译

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

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

总结

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

正如您所指出的,if 语句只是几乎快了。这可能是由于实际上调用continue 的开销。这种差异在宏伟的计划中可以忽略不计。此外,如果你的 for 循环的整体性能真的 取决于这种差异,这意味着循环的 contents 的速度非常快,而你的瓶颈是 @ 987654349@ 循环本身,你应该考虑对你的代码进行矢量化(即,如果我在我的for 循环中放置一个magic(3) 调用而不是这里显示的简单乘法,那么差异就完全消失了)。

【讨论】:

以上是关于For 循环中的 If vs Continue 语句的主要内容,如果未能解决你的问题,请参考以下文章

for循环和if条件中的continue语句[关闭]

for循环中的continue和break

JavaScript continue语句有啥用?

循环语句,beark语句和continue语句,请举几个例子

如何用Pythoncontinue语句及单个for循环输出10~20之间的所有奇数及21~30间的?

Python程序开发——第二章 条件语句