Ответ 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')