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

Насколько быстрее подразумевается расширение по сравнению с bsxfun?

Как прокомментировал Стив Эддинс, неявное расширение (введенное в Matlab R2016b) быстрее, чем bsxfun для небольших размеров массива и имеет аналогичную скорость для больших массивов:

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

Кроме того, размер, по которому происходит расширение, может иметь влияние:

Когда происходит расширение в первом измерении, операторы могут быть не такими быстрыми, как bsxfun.

(Спасибо @Poelie и @rayryeng за то, что позволили мне знать об этом!)

Естественно возникают два вопроса:

  • Насколько быстрее неявное расширение по сравнению с bsxfun?
  • Для каких размеров или форм массива значительная разница?
4b9b3361

Ответ 1

Чтобы измерить разницу в скорости, некоторые тесты были выполнены. В тестах рассматривается две разные операции:

  • Добавление
  • мощность

и четыре различных формы для массивов, на которые нужно работать:

  • N×N массив с массивом N×1
  • N×N×N×N массив с массивом N×1×N
  • N×N массив с массивом 1×N
  • N×N×N×N массив с массивом 1×N×N

Для каждой из восьми комбинаций операций и форм массива та же операция выполняется с неявным расширением и с bsxfun. Используются несколько значений N, чтобы охватить диапазон от небольших до больших массивов. timeit используется для надежного времени.

В конце этого ответа приведен сравнительный код. Он был запущен на Matlab R2016b, Windows 10, с оперативной памятью 12 ГБ.

Результаты

Следующие графики показывают результаты. Горизонтальная ось - это количество элементов выходного массива, которое является лучшим показателем размера, чем N.

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

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

Выводы

Согласно графикам:

  • Результаты подтверждают, что неявное расширение выполняется быстрее для небольших массивов и имеет скорость, близкую к bsxfun для больших массивов.
  • Расширение вдоль первого или по другим измерениям, по-видимому, не оказывает большого влияния, по крайней мере, в рассматриваемых случаях.
  • Для небольших массивов разница может быть в десять раз и более. Обратите внимание, однако, что timeit не точен для небольших размеров, потому что код слишком быстрый (на самом деле он выдает предупреждение для таких небольших размеров).
  • Две скорости становятся равными, когда количество элементов выхода достигает около 1e5. Это значение может быть зависящим от системы.

Так как улучшение скорости имеет значение только тогда, когда массивы малы, это ситуация, когда любой подход очень быстрый, в любом случае использование неявного расширения или bsxfun, по-видимому, в основном зависит от вкуса, удобочитаемости или обратной совместимости.

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

clear

% NxN, Nx1, addition / power
N1 = 2.^(4:1:12);
t1_bsxfun_add = NaN(size(N1));
t1_implicit_add = NaN(size(N1));
t1_bsxfun_pow = NaN(size(N1));
t1_implicit_pow = NaN(size(N1));
for k = 1:numel(N1)
    N = N1(k);
    x = randn(N,N);
    y = randn(N,1);
    % y = randn(1,N); % use this line or the preceding one
    t1_bsxfun_add(k) = timeit(@() bsxfun(@plus, x, y));
    t1_implicit_add(k) = timeit(@() x+y);
    t1_bsxfun_pow(k) = timeit(@() bsxfun(@power, x, y));
    t1_implicit_pow(k) = timeit(@() x.^y);
end

% NxNxNxN, Nx1xN, addition / power
N2 = round(sqrt(N1));
t2_bsxfun_add = NaN(size(N2));
t2_implicit_add = NaN(size(N2));
t2_bsxfun_pow = NaN(size(N2));
t2_implicit_pow = NaN(size(N2));
for k = 1:numel(N1)
    N = N2(k);
    x = randn(N,N,N,N);
    y = randn(N,1,N);
    % y = randn(1,N,N); % use this line or the preceding one
    t2_bsxfun_add(k) = timeit(@() bsxfun(@plus, x, y));
    t2_implicit_add(k) = timeit(@() x+y);
    t2_bsxfun_pow(k) = timeit(@() bsxfun(@power, x, y));
    t2_implicit_pow(k) = timeit(@() x.^y);
end

% Plots
figure
colors = get(gca,'ColorOrder');

subplot(121)
title('N\times{}N,   N\times{}1')
% title('N\times{}N,   1\times{}N') % this or the preceding
set(gca,'XScale', 'log', 'YScale', 'log')
hold on
grid on
loglog(N1.^2, t1_bsxfun_add, 's-', 'color', colors(1,:))
loglog(N1.^2, t1_implicit_add, 's-', 'color', colors(2,:))
loglog(N1.^2, t1_bsxfun_pow, '^-', 'color', colors(1,:))
loglog(N1.^2, t1_implicit_pow, '^-', 'color', colors(2,:))
legend('Addition, bsxfun', 'Addition, implicit', 'Power, bsxfun', 'Power, implicit')

subplot(122)
title('N\times{}N\times{}N{}\times{}N,   N\times{}1\times{}N')
% title('N\times{}N\times{}N{}\times{}N,   1\times{}N\times{}N') % this or the preceding
set(gca,'XScale', 'log', 'YScale', 'log')
hold on
grid on
loglog(N2.^4, t2_bsxfun_add, 's-', 'color', colors(1,:))
loglog(N2.^4, t2_implicit_add, 's-', 'color', colors(2,:))
loglog(N2.^4, t2_bsxfun_pow, '^-', 'color', colors(1,:))
loglog(N2.^4, t2_implicit_pow, '^-', 'color', colors(2,:))
legend('Addition, bsxfun', 'Addition, implicit', 'Power, bsxfun', 'Power, implicit')