Если утверждение vs Continue в цикле for

У меня есть цикл for в 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; 

Или, не имеет ли значения ни один из способов, то есть оптимизирован ли он на тот же результат при выполнении?

Поскольку его единственная микро-оптимизация - это не очень важно - но мне любопытно узнать!

РЕДАКТИРОВАТЬ: Интересно, что после профилирования кода, как рекомендовано, последний, по-видимому, будет намного быстрее - если кто-нибудь захочет объяснить, что это было бы здорово! (В конце концов, в лучшем случае это та же логика, но с дополнительными инструкциями, которые нужно выполнить)

Ответы

Ответ 1

В теории не должно быть разницы в производительности между двумя предложенными вами методами, потому что оператор 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').

script Я использовал для всех версий 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

Как вы можете видеть здесь, continue и оператор if имеют очень схожую производительность без включения ускорения JIT. Однако, когда вы включаете ускорение (по умолчанию MATLAB), только ускоряется инструкция if. Скорость подхода continue остается относительно неизменной. В результате оператор if будет выполняться быстрее.

введите описание изображения здесь

Вероятно, это связано с тем, что компилятор JIT ускоряет блоки команд, которые выполняются много раз подряд. При вставке логики ветвления с помощью continue там вы меняете поток программы в зависимости от состояния, и это изменяет инструкции, которые выполняются каждый раз через цикл. Это, по-видимому, предотвращает компиляцию JIT.

R2014b

Аналогично R2014a.

введите описание изображения здесь

R2015a

Аналогично R2014a.

введите описание изображения здесь

R2015b (новый механизм выполнения)

К сожалению, в R2015b не кажется, что вы можете отключить JIT таким же образом (если кто-нибудь знает, как, дайте мне знать, и я буду обновлять), чтобы оба этих графика активировали ускорение, но кажется, что новый механизм выполнения устраняет различия во времени выполнения, которые ранее создавал компилятор JIT. Это связано с тем, что новый механизм выполнения может JIT компилировать весь код (включая, очевидно, continue)

Из документов MATLAB:

Компиляция всего кода MATLAB вовремя вовремя

Переработанный механизм выполнения MATLAB использует компиляцию JIT всего кода MATLAB, тогда как механизм выполнения ранее использовал компиляцию JIT в некоторых случаях. Компиляция JIT генерирует собственный код уровня машины, который оптимизирован для исполняемого кода MATLAB и для конкретной аппаратной платформы.

Преимущество производительности компиляции JIT является наибольшим, когда код MATLAB выполняется дополнительно и может повторно использовать скомпилированный код. Это происходит в обычных случаях, таких как for-loops или когда приложения запускаются дополнительно в сеансе MATLAB, по крайней мере, в некоторых приложениях файлы MATLAB остаются неизмененными между последующими запусками.

введите описание изображения здесь

Резюме

В более ранних версиях MATLAB (R2015a и ранее) оператор continue предотвратил ускорение JIT, в результате чего версия if выполнялась быстрее, когда JIT был включен (по умолчанию). С введением нового механизма выполнения в R2015b весь код ускорен JIT и, как таковой, разница фактически исчезла.

Как вы уже отметили, оператор if работает только быстрее. Вероятно, это связано с накладными расходами, вызвавшими фактически вызов continue. Эта разница незначительна в великой схеме вещей. Кроме того, если общая производительность вашего цикла for действительно зависит от этой разницы, это означает, что скорость содержимого цикла очень быстрая, а ваше узкое место - это цикл for, и вы должны рассмотреть возможность векторизации кода (т.е. Если Я положил вызов magic(3) в мой цикл for вместо простого умножения, показанного здесь, разница полностью исчезает).