问题
I have a for
loop in Matlab, and all the code inside the for loop is enclosed in an if
statement. For example :
for p = 1:length(array)
if array(p) == 1 %// Test positive for condition
%// Generic code here that
%// Only executes if p == 1
end;
end;
Is it faster to have test for equality using an if
statement, and execute the interior code if true, or, to test for inequality and then use a continue statement, such as :
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;
Or, does it make no difference either way i.e. its optimized to the same result on execution?
As its only a micro-optimization its not terribly important - but I'm curious to know!
EDIT: Interestingly, after profiling the code as recommended, the latter seems to be fractionally faster - if anybody would care to explain that would be great! (After all, at best its the same logic but with extra instructions to be executed)
回答1:
In theory, there should not be a performance difference between the two methods that you have proposed because the if
statement has to be evaluated every time through the loop regardless, but let's take a closer look with some profiling (timeit
). I have some tests below on versions R2014a through R2015b.
For each of these tests, I create an array p
of varying sizes of an equal number of 1's and 0's and randomized the order of 0's and 1's.
%// Creates random zeros and ones of size n
p = mod(randperm(n),2);
For the first test, I disabled the JIT compiler feature('JIT', 'off')
and the second test I enabled the JIT compiler feature('JIT', 'on')
.
The script I used for all versions of MATLAB was:
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
As you can see here, continue
and the if
statement have very similar performance with no JIT acceleration turned on. However, when you turn on the acceleration (MATLAB's default), only the if
statement seems to be accelerated. The speed of the continue
approach remains relatively unchanged. As a result, the if
statement would execute faster.
This is likely due to the fact that the JIT compiler accelerates blocks of instructions that are executed many times in a row. By sticking the branching logic with a continue
in there, you are changing the flow of the program depending upon a condition and this changes the instructions that are run each time through the loop. This is apparently preventing JIT compilation.
R2014b
Similar to R2014a.
R2015a
Similar to R2014a.
R2015b (new execution engine introduced)
Unfortunately, in R2015b it doesn't seem that you can disable JIT in the same way (if anyone knows how, let me know and I'll update) so both of these plots have acceleration enabled, but it appears that the new execution engine removes the differences in execution time that the JIT compiler had previously created. This is because the new execution engine is able to JIT compile all code (including, obviously the continue
)
From the MATLAB docs:
Just-in-Time Compilation of All MATLAB Code
The redesigned MATLAB execution engine uses JIT compilation of all MATLAB code, whereas the execution engine previously used JIT compilation in some cases. The JIT compilation generates native machine level code that is optimized for the MATLAB code being executed and for the specific hardware platform.
The performance benefit of JIT compilation is greatest when MATLAB code is executed additional times and can re-use the compiled code. This happens in common cases such as for-loops or when applications are run additional times in a MATLAB session with at least some of the application’s MATLAB files remaining unmodified between subsequent runs.
Summary
In older versions of MATLAB (R2015a and earlier), the continue
statement prevented JIT acceleration causing the if
version to execute faster when JIT was enabled (by default). With the introduction of the new execution engine in R2015b, all code is JIT accelerated and as such, the difference has effectively vanished.
As you noted though, the if
statement is just barely faster still. This is likely due to the overhead of actually calling continue
. This difference is negligible in the grand scheme of things. Furthermore, if the overall performance of your for loop really depends on this difference, it means that the speed of the contents of the loop is very fast and your bottleneck is the for
loop itself and you should consider vectorizing your code (i.e. if I put a magic(3)
call within my for
loop instead of the simple multiplication shown here, the difference completely disappears).
来源:https://stackoverflow.com/questions/36677256/if-vs-continue-statement-in-a-for-loop