Коли в 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 (весело, A, B) застосовує бінарну операцію" елемент за елементом ", визначену функцією обробки fun, до масивів A і B, з синглтон розширення ввімкнено. " Що означає словосполучення "з включеним розширенням однотонних"?


4
Зауважте, що швидкість читання, яку ви отримуєте, залежить від тесту, який ви виконуєте. Якщо після запуску Matlab запустити вищевказаний код і просто покласти tic...tocрядки, швидкість коду буде залежати від необхідності читання функцій у пам'яті.
Йонас

@ Jonas Так, я щойно дізнався про це, прочитавши про timeitфункцію у посиланні, яке ви / агін / Дан надаєте.
Colin T Bowers

Відповіді:


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

Дякую за відмінну відповідь +1. Я відзначив цю відповідь як найповнішу дискусію і отримав (на даний момент) найбільшу кількість голосів.
Colin T Bowers

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, у мене менше помилок і я програмую швидше .

Більше того, він коротший, що покращує швидкість друку та читабельність .


1
Дякую за відповідь Олі. +1, як мені здається, ця відповідь щось сприяла на додаток до відповідей прем'єра та Йонаса. Мені особливо сподобалося, як ви виклали кількість концептуальних проблем, які потрібно вирішити в заданому рядку коду.
Colin T Bowers

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 зазвичай вважається доставити на роботу ..

Редагувати Завдяки коментарю Дена, ось чудова стаття Лорен, яка обговорює саме це.


7
Ця стаття може бути відповідним: blogs.mathworks.com/loren/2008/08/04 / ...
Dan

@ Дякую за чудову рекомендацію.
angainor

Дякуємо за чудову відповідь. +1 за те, що першим чітко виклав головну перевагу, bsxfunнаводячи хороший приклад.
Colin T Bowers

13

Станом на R2016b, Matlab підтримує Implicit Expansion для широкого кола операторів, тому в більшості випадків використовувати його більше не потрібно bsxfun:

Раніше ця функція була доступна через bsxfunфункцію. Тепер рекомендується замінювати більшість застосувань bsxfunпрямими дзвінками до функцій та операторів, які підтримують неявне розширення . У порівнянні з використанням bsxfun, неявне розширення пропонує більш високу швидкість , краще використання пам’яті та покращену читабельність коду .

У блозі Лорен є детальна дискусія про неявне розширення та його ефективність. Для того, щоб цитувати Стів Eddins з MathWorks:

У R2016b неявне розширення працює так само швидко або швидше, ніж bsxfunу більшості випадків. Найкращі покращення продуктивності при неявному розширенні мають малі розміри матриць та масивів. Для великих розмірів матриці неявне розширення має тенденцію приблизно до тієї ж швидкості, що і bsxfun.


8

Речі не завжди узгоджуються з трьома загальними методами:, repmatрозширення за допомогою індексації та bsxfun. Це стає більш цікавим, коли ви ще більше збільшуєте розмір вектора. Дивіться сюжет:

порівняння

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.