Производительность Matlab: сравнение медленнее, чем арифметика

А назад я дал ответ на этот вопрос.

Цель: подсчитать количество значений в этой матрице, находящихся в диапазоне [3 6]:

A = [2 3 4 5 6 7;
     7 6 5 4 3 2]

Я придумал 12 различных способов сделать это:

count = numel(A( A(:)>3 & A(:)<6 ))      %# (1)
count = length(A( A(:)>3 & A(:)<6 ))     %# (2)
count = nnz( A(:)>3 & A(:)<6 )           %# (3)
count = sum( A(:)>3 & A(:)<6 )           %# (4)

Ac = A(:);
count = numel(A( Ac>3 & Ac<6 ))          %# (5,6,7,8)
%# prevents double expansion
%# similar for length(), nnz(), sum(),
%# in the same order as (1)-(4)

count = numel(A( abs(A-(6+3)/2)<3/2 ))   %# (9,10,11,12)
%# prevents double comparison and & 
%# similar for length(), nnz(), sum()
%# in the same order as (1)-(4)

Итак, я решил узнать, что быстрее. Тестовый код:

A = randi(10, 50);
tic
for ii = 1:1e5

    %# method is inserted here

end
toc

результаты (лучшие из 5 прогонов, все в секундах):

%# ( 1): 2.981446
%# ( 2): 3.006602
%# ( 3): 3.077083
%# ( 4): 2.619057
%# ( 5): 3.011029
%# ( 6): 2.868021
%# ( 7): 3.149641
%# ( 8): 2.457988
%# ( 9): 1.675575
%# (10): 1.675384
%# (11): 2.442607
%# (12): 1.222510

Итак, кажется, что count = sum(( abs(A(:)-(6+3)/2) < (3/2) )); - самый быстрый способ перейти сюда...

Я торгую одним < с двумя делениями, добавлением и abs, а время выполнения меньше половины! У кого-нибудь есть объяснение, почему это так?

Компилятор JIT, вероятно, заменяет деления/добавления на одно значение в памяти, но все же существует abs... неверное предсказание ветки? Кажется глупым для чего-то столь же простого, как это...

Ответ 1

Выражение A(:)>3 & A(:)<6 должно оценивать два условия, тогда как abs(A(:)-(6+3)/2) < 3/2) оценивает только один.

Для очень сложных циклов интенсивного вычисления это имеет большое значение. Даже без ложных предсказаний ветвления, ветвление само по себе является относительно дорогостоящим. Вот почему, например, разворот цикла работает как метод оптимизации.