Подтвердить что ты не робот

Действительно ли A == 0 лучше, чем ~ A?

Введение в настройку проблемы

Я делал некоторые тесты, связанные с ~A и A==0 для double array with no NaNs, оба из которых конвертируют A в логический массив, где все zeros преобразуются в значения true, а остальные устанавливаются как false.

Для бенчмаркинга я использовал три набора входных данных -

  • Очень маленькие и мелкие данные - 15:5:100
  • Данные малого и среднего размера - 50:40:1000
  • Средние и большие данные - 200:400:3800

Вход создается с помощью A = round(rand(N)*20), где N - параметр, взятый из массива размера. Таким образом, N будет меняться от 15 to 100 with stepsize of 5 для первого набора и аналогично для второго и третьего множеств. Пожалуйста, обратите внимание, что я определяю данные как N, поэтому количество элементов будет составлять данные ^ 2 или N ^ 2.

Код бенчмаркинга

N_arr = 15:5:100; %// for very small to small sized input array
N_arr = 50:40:1000; %// for small to medium sized input array
N_arr = 200:400:3800; %// for medium to large sized input array
timeall = zeros(2,numel(N_arr));
for k1 = 1:numel(N_arr)
    A = round(rand(N_arr(k1))*20);

    f = @() ~A;
    timeall(1,k1) = timeit(f);
    clear f

    f = @() A==0;
    timeall(2,k1) = timeit(f);
    clear f
end

Результаты

enter image description here

enter image description here

enter image description here

Наконец, вопросы

Можно видеть, как A==0 работает лучше, чем ~A для всех типов данных. Итак, вот некоторые замечания и связанные с ними вопросы вместе с ними -

  • A==0 имеет один реляционный оператор и один операнд, тогда как ~A имеет только один реляционный оператор. Оба производят логические массивы и оба принимают двойные массивы. Фактически, A==0 будет работать с NaNs тоже, если ~A не будет. Итак, почему все еще ~A по крайней мере не так хорошо, как A==0, поскольку он выглядит так: A==0 делает больше работы или я что-то пропустил здесь?

  • Это своеобразное падение прошедшего времени с A==0 и, таким образом, увеличение производительности при N = 320, т.е. в 102400 для элементов A. Я наблюдал это во многих прогонах с этим размером на двух разных системах, У меня есть доступ. Так что происходит там?

4b9b3361

Ответ 1

Это не строго ответ, а мой вклад в обсуждение

Я использовал profiler для исследования слегка модифицированной версии вашего кода:

N_arr = 200:400:3800; %// for medium to large sized input array

for k1 = 1:numel(N_arr)

    A = randi(1,N_arr(k1));
    [~]=eq(A,0);
    clear A

    A = randi(1,N_arr(k1));
    [~]=not(A);
    clear A   

end

Я использовал следующие фреймы профайлера (согласно UndocumentedMatlab ряд сообщений о profiler):

profile('-memory','on');
profile('on','-detail','builtin');

И вот выдержка из результатов профилировщика (ссылка на увеличенное изображение): Profiler output

Кажется, что вариант == выделяет крошечный бит дополнительной памяти, который позволяет ему работать с магией....

Что касается вашего вопроса 2: Прежде чем удалять сохранение timeall, я попытался построить те же диаграммы, которые вы сделали в Excel. Я не заметил поведения, которое вы упомянули для N = 320. Я подозреваю, что это может иметь какое-то отношение к дополнительным оболочкам (т.е. Дескрипторам функций), которые вы используете в своем коде.


Я подумал, что для быстрой справки я буду прилагать доступную документацию для обсуждаемых функций.

Документация для ~ (\ MATLAB\R20???\toolbox\matlab\ops\not.m):

%~   Logical NOT.
%   ~A performs a logical NOT of input array A, and returns an array
%   containing elements set to either logical 1 (TRUE) or logical 0 (FALSE).
%   An element of the output array is set to 1 if A contains a zero value
%   element at that same array location.  Otherwise, that element is set to
%   0.
%
%   B = NOT(A) is called for the syntax '~A' when A is an object.
%
%   ~ can also be used to ignore input arguments in a function definition,
%   and output arguments in a function call.  See "help punct"

%   Copyright 1984-2005 The MathWorks, Inc.

Документация для == (\ MATLAB\R20???\toolbox\matlab\ops\eq.m):

%==  Equal.
%   A == B does element by element comparisons between A and B
%   and returns a matrix of the same size with elements set to logical 1
%   where the relation is true and elements set to logical 0 where it is
%   not.  A and B must have the same dimensions unless one is a
%   scalar. A scalar can be compared with any size array.
%
%   C = EQ(A,B) is called for the syntax 'A == B' when A or B is an
%   object.

%   Copyright 1984-2005 The MathWorks, Inc.

Ответ 2

Также не строго ответ, но я хочу добавить к обсуждению. Возможно, это сводится к вашей функции timeit.

Я пробовал функцию Dev-iL. Я профилировал и получил те же результаты: EQ кажется быстрее, чем NOT, а EQ выделяет немного больше памяти, чем NOT. Казалось логичным, что если оператор EQ выделяет больше памяти, то, увеличив размер массива, это распределение памяти также увеличится. Подозрительно, это не так!

Я продолжал и удалял все ненужное и повторял цикл для N=1000 итераций. Профилировщик по-прежнему соглашался с тем, что EQ быстрее, чем NOT. Но я не был уверен.

Далее я должен был удалить странно выглядящие [~] = ~A и [~] = A == 0 для чего-то более гуманного, выглядящего как tmp1 = ~A и tmp2 = A == 0 и voilà! Время выполнения почти равно.

profiler results

Поэтому я предполагаю, что вы делаете что-то подобное внутри своей функции timeid. Стоит отметить, что назначение [~] замедляет обе функции, но NOT кажется более подверженным влиянию, чем EQ.

Теперь большой вопрос: почему оператор [~] замедляет функции? Я не знаю. Возможно, только Mathworks может ответить на это. Вы можете открыть билет на веб-странице Mathworks.

Частичный ответ: у них почти одинаковое время выполнения, даже для больших массивов (самый большой массив, который я пытался - 10K).

неотвеченная часть: почему [~] назначение замедляет код. Почему NOT больше влияет на EQ.

Мой код:

clear all
clear classes

array_sizes = [1000:1000:10000];
repetitions = 10000;

for i = 1:length(array_sizes)
    A1 = randi([0, 1], array_sizes(i), 1);
    for j = 1:repetitions
        tmp1 = eq(A1, 0);
    end
end

for i = 1:length(array_sizes)
    A2 = randi([0, 1], array_sizes(i), 1);
    for j = 1:repetitions
        tmp2 = not(A2);
    end
end