В Matlab, когда оптимально использовать bsxfun?

135

Мой вопрос: я заметил, что многие хорошие ответы на вопросы Matlab по SO часто используют эту функцию bsxfun. Зачем?

Мотивация: в документации Matlab для bsxfunприведен следующий пример:

A = magic(5);
A = bsxfun(@minus, A, mean(A))

Конечно, мы могли бы сделать ту же операцию, используя:

A = A - (ones(size(A, 1), 1) * mean(A));

И на самом деле простой тест скорости показывает, что второй метод работает примерно на 20% быстрее. Так зачем использовать первый метод? Я предполагаю, что есть некоторые обстоятельства, когда использование bsxfunбудет намного быстрее, чем «ручной» подход. Мне было бы очень интересно увидеть пример такой ситуации и объяснить, почему это происходит быстрее.

Кроме того, еще один последний элемент этого вопроса, опять же из документации Matlab для bsxfun: «C = bsxfun (fun, A, B) применяет двоичную операцию поэлементно, указанную функцией handle функции, к массивам A и B с помощью singleton расширение включено. Что означает фраза «с включенным расширением синглтона»?

Колин Т Боуэрс
источник
4
Обратите внимание, что скорость чтения зависит от теста, который вы выполняете. Если вы запустите приведенный выше код после перезапуска Matlab и просто поместите tic...tocего в строки, скорость кода будет зависеть от необходимости читать функции в памяти.
Джонас
@Jonas Да, я только что узнал об этом, прочитав о timeitфункции в ссылке, которую вы / angainor / Dan предоставили.
Колин Т Боуэрс

Ответы:

152

Я использую три причины bsxfun( документация , ссылка на блог )

  1. bsxfunбыстрее чем repmat(см. ниже)
  2. bsxfun требует меньше печатать
  3. Использование bsxfun, как использование accumarray, заставляет меня чувствовать себя хорошо о моем понимании Matlab.

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

Вполне возможно, что для очень небольших задач этот repmatподход будет более быстрым, но при таком размере массива обе операции выполняются настолько быстро, что, скорее всего, это не повлияет на общую производительность. Есть две важные причины bsxfun, которые ускоряют: (1) вычисление происходит в скомпилированном коде, что означает, что фактическая репликация массива никогда не происходит, и (2) bsxfunявляется одной из многопоточных функций Matlab.

На моем прилично быстром ноутбуке я проверил сравнение скорости repmatи bsxfunс R2012b.

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

Для меня bsxfunэто примерно в 3 раза быстрее, чем repmat. Разница становится более заметной, если массивы становятся больше

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

Скачок во время выполнения repmatпроисходит вокруг размера массива 1 Мб, что может иметь какое-то отношение к размеру моего кеша процессора - bsxfunне так уж плохо, потому что ему нужно только выделить выходной массив.

Ниже вы найдете код, который я использовал для синхронизации:

n = 300;
k=1; %# k=100 for the second graph
a = ones(10,1);
rr = zeros(n,1);
bb=zeros(n,1);
ntt=100;
tt=zeros(ntt,1);
for i=1:n;
   r = rand(1,i*k);
   for it=1:ntt;
      tic,
      x=bsxfun(@plus,a,r);
      tt(it)=toc;
   end;
   bb(i)=median(tt);
   for it=1:ntt;
      tic,
      y=repmat(a,1,i*k)+repmat(r,10,1);
      tt(it)=toc;
   end;
   rr(i)=median(tt);
end
Jonas
источник
Спасибо за отличный ответ +1. Я пометил этот ответ, так как он является наиболее полным обсуждением и также (на данный момент) получил наибольшее количество голосов.
Колин Т Боуэрс
40

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

Для того, чтобы написать свой пример:

A = A - (ones(size(A, 1), 1) * mean(A));

Я должен решить несколько проблем:

1) size(A,1)илиsize(A,2)

2) ones(sizes(A,1),1)илиones(1,sizes(A,1))

3) ones(size(A, 1), 1) * mean(A)илиmean(A)*ones(size(A, 1), 1)

4) mean(A)илиmean(A,2)

Когда я использую bsxfun, я просто должен решить последний:

а) mean(A)илиmean(A,2)

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

Более того, он короче, что улучшает скорость набора текста и удобочитаемость .

Oli
источник
1
Спасибо за ответ Оли. +1, так как я думаю, что этот ответ внес что-то в дополнение к ответам Ангайнора и Йонаса. Мне особенно понравилось, как вы изложили ряд концептуальных проблем, которые должны быть решены в данной строке кода.
Колин Т Боуэрс
16

Очень интересный вопрос! Я недавно наткнулся на именно такую ​​ситуацию, отвечая на этот вопрос. Рассмотрим следующий код, который вычисляет индексы скользящего окна размером 3 через вектор a:

a = rand(1e7,1);

tic;
idx = bsxfun(@plus, [0:2]', 1:numel(a)-2);
toc

% equivalent code from im2col function in MATLAB
tic;
idx0 = repmat([0:2]', 1, numel(a)-2);
idx1 = repmat(1:numel(a)-2, 3, 1);
idx2 = idx0+idx1;
toc;

isequal(idx, idx2)

Elapsed time is 0.297987 seconds.
Elapsed time is 0.501047 seconds.

ans =

 1

В этом случае bsxfunпочти в два раза быстрее! Это полезно и быстро, потому что позволяет избежать явного выделения памяти для матриц idx0и idx1сохранения их в памяти, а затем чтения их снова, чтобы просто добавить их. Поскольку пропускная способность памяти является ценным активом и часто является узким местом в современных архитектурах, вы хотите использовать ее с умом и снизить требования к памяти для своего кода, чтобы повысить производительность.

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

v1=[0:2]';
v2 = 1:numel(a)-2;
tic;
vout = v1*v2;
toc
Elapsed time is 0.309763 seconds.

Вы умножаете два вектора, чтобы получить матрицу. Просто внешний продукт выполняет только умножение и bsxfunможет применять произвольные операторы. Как примечание, очень интересно видеть, что bsxfunэто так же быстро, как внешний продукт BLAS. И BLAS обычно считается доставить на работу ..

Edit Благодаря комментарию Дэна, вот отличная статья Лорена, обсуждающая именно это.

angainor
источник
7
Эта статья может быть актуальна: blogs.mathworks.com/loren/2008/08/04/…
Дан
@Dan Спасибо за отличный отзыв.
Angainor
Спасибо за отличный ответ angainor. +1 за то, что был первым, кто ясно указал главное преимущество bsxfunс хорошим примером.
Колин Т Боуэрс
13

Начиная с R2016b, Matlab поддерживает неявное расширение для широкого спектра операторов, поэтому в большинстве случаев больше нет необходимости использовать bsxfun:

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

Там есть подробное обсуждение о неявном расширении и его производительность на блоге Лорен. Чтобы процитировать Стива Эддинса из MathWorks:

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

нирвана-MSU
источник
8

Вещи не всегда согласуются с 3 распространенными методами: repmatзатраты на индексацию и bsxfun. Это становится намного интереснее, когда вы увеличиваете размер вектора еще больше. Смотрите сюжет:

сравнение

bsxfunна самом деле в какой-то момент становится немного медленнее, чем два других, но меня удивляет то, что если вы увеличите размер вектора еще больше (> 13E6 выходных элементов), bsxfun вдруг снова станет быстрее примерно в 3 раза. Их скорости, кажется, скачкообразно скачут, и порядок не всегда соответствует. Я предполагаю, что это также может зависеть от размера процессора / памяти, но в целом я думаю, что буду придерживаться, bsxfunкогда это возможно.

Джастин вонг
источник