Ответ 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
вместо простого умножения, показанного здесь, разница полностью исчезает).