Di Matlab, kapan optimal untuk menggunakan bsxfun?

136

Pertanyaan saya: Saya telah memperhatikan bahwa banyak jawaban bagus untuk pertanyaan Matlab di SO sering menggunakan fungsi tersebut bsxfun. Mengapa?

Motivasi: Dalam dokumentasi Matlab untuk bsxfun, contoh berikut diberikan:

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

Tentu saja kami dapat melakukan operasi yang sama dengan menggunakan:

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

Dan faktanya, uji kecepatan sederhana menunjukkan bahwa metode kedua lebih cepat 20%. Jadi mengapa menggunakan metode pertama? Saya menduga ada beberapa keadaan di mana penggunaan bsxfunakan jauh lebih cepat daripada pendekatan "manual". Saya akan sangat tertarik untuk melihat contoh dari situasi seperti itu dan penjelasan mengapa lebih cepat.

Juga, satu elemen terakhir untuk pertanyaan ini, lagi-lagi dari dokumentasi Matlab untuk bsxfun: "C = bsxfun (fun, A, B) menerapkan operasi biner elemen demi elemen yang ditentukan oleh fungsi handle fun ke array A dan B, dengan singleton ekspansi diaktifkan. ". Apa arti frasa "dengan perluasan tunggal diaktifkan"?

Colin T Bowers
sumber
4
Perhatikan bahwa kecepatan membaca yang Anda peroleh bergantung pada tes yang Anda lakukan. Jika Anda menjalankan kode di atas setelah me-restart Matlab dan hanya meletakkan tic...tocsekitar baris, kecepatan kode akan bergantung pada keharusan membaca fungsi ke dalam memori.
Jonas
@Jonas Ya, saya baru mempelajarinya dengan membaca tentang timeitfungsi di tautan yang Anda / angainor / Dan sediakan.
Colin T Bowers

Jawaban:

153

Ada tiga alasan saya menggunakan bsxfun( dokumentasi , link blog )

  1. bsxfunlebih cepat dari repmat(lihat di bawah)
  2. bsxfun membutuhkan lebih sedikit mengetik
  3. Menggunakan bsxfun, seperti menggunakan accumarray, membuat saya merasa nyaman dengan pemahaman saya tentang Matlab.

bsxfunakan mereplikasi larik masukan sepanjang "dimensi tunggal" mereka, yaitu dimensi dengan ukuran larik adalah 1, sehingga cocok dengan ukuran dimensi yang sesuai dari larik lainnya. Inilah yang disebut "ekspasi tunggal". Selain itu, dimensi singleton adalah salah satu yang akan dihapus jika Anda menelepon squeeze.

Ada kemungkinan bahwa untuk masalah yang sangat kecil, repmatpendekatannya lebih cepat - tetapi pada ukuran larik itu, kedua operasi tersebut sangat cepat sehingga kemungkinan besar tidak akan membuat perbedaan apa pun dalam hal kinerja secara keseluruhan. Ada dua alasan penting untuk bsxfunlebih cepat: (1) kalkulasi terjadi dalam kode yang dikompilasi, yang berarti replikasi larik yang sebenarnya tidak pernah terjadi, dan (2) bsxfunadalah salah satu fungsi Matlab multithread.

Saya telah menjalankan perbandingan kecepatan antara repmatdan bsxfundengan R2012b pada laptop saya yang cukup cepat.

masukkan deskripsi gambar di sini

Bagi saya, bsxfunini sekitar 3 kali lebih cepat dari repmat. Perbedaannya menjadi lebih jelas jika larik menjadi lebih besar

masukkan deskripsi gambar di sini

Lompatan dalam runtime repmatterjadi di sekitar ukuran array 1Mb, yang mungkin ada hubungannya dengan ukuran cache prosesor saya - bsxfuntidak seburuk lompatan, karena hanya perlu mengalokasikan array output.

Di bawah ini Anda menemukan kode yang saya gunakan untuk pengaturan waktu:

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
sumber
Terima kasih atas tanggapan yang luar biasa +1. Saya telah menandai ini sebagai jawaban karena ini adalah diskusi yang paling komprehensif dan juga (pada saat ini) telah menerima suara terbanyak.
Colin T Bowers
41

Dalam kasus saya, saya menggunakan bsxfunkarena menghindari saya untuk memikirkan masalah kolom atau baris.

Untuk menulis contoh Anda:

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

Saya harus menyelesaikan beberapa masalah:

1) size(A,1)atausize(A,2)

2) ones(sizes(A,1),1)atauones(1,sizes(A,1))

3) ones(size(A, 1), 1) * mean(A)ataumean(A)*ones(size(A, 1), 1)

4) mean(A)ataumean(A,2)

Saat saya menggunakan bsxfun, saya hanya perlu menyelesaikan yang terakhir:

a) mean(A)ataumean(A,2)

Anda mungkin berpikir itu malas atau semacamnya, tetapi ketika saya menggunakan bsxfun, saya memiliki lebih sedikit bug dan saya memprogram lebih cepat .

Selain itu, lebih pendek, yang meningkatkan kecepatan mengetik dan keterbacaan .

Oli
sumber
1
Terima kasih atas tanggapannya Oli. +1 karena menurut saya jawaban ini menyumbangkan sesuatu selain tanggapan angainor dan Jonas. Saya sangat menyukai cara Anda menguraikan jumlah masalah konseptual yang perlu diselesaikan dalam baris kode tertentu.
Colin T Bowers
17

Pertanyaan yang sangat menarik! Saya baru-baru ini tersandung pada situasi yang persis seperti itu saat menjawab pertanyaan ini . Pertimbangkan kode berikut yang menghitung indeks dari jendela geser ukuran 3 melalui vektor 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

Dalam hal bsxfunini hampir dua kali lebih cepat! Ini berguna dan cepat karena menghindari alokasi memori secara eksplisit untuk matriks idx0dan idx1, menyimpannya ke memori, lalu membacanya lagi hanya untuk menambahkannya. Karena bandwidth memori adalah aset berharga dan seringkali menjadi hambatan pada arsitektur saat ini, Anda ingin menggunakannya dengan bijak dan mengurangi persyaratan memori kode Anda untuk meningkatkan kinerja.

bsxfunmemungkinkan Anda melakukan hal itu: membuat matriks berdasarkan penerapan operator arbitrer ke semua pasangan elemen dua vektor, alih-alih beroperasi secara eksplisit pada dua matriks yang diperoleh dengan mereplikasi vektor. Itu adalah ekspansi tunggal . Anda juga bisa menganggapnya sebagai produk luar dari BLAS:

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

Anda mengalikan dua vektor untuk mendapatkan matriks. Hanya saja hasil perkalian luarnya hanya melakukan perkalian, dan bsxfunbisa menerapkan operator sembarang. Sebagai catatan tambahan, sangat menarik untuk dilihat, bsxfunsecepat produk luar BLAS. Dan BLAS biasanya dianggap untuk memberikan yang kinerja ..

Sunting Berkat komentar Dan, berikut adalah artikel bagus dari Loren yang membahas hal itu.

angainor.dll
sumber
7
Artikel ini mungkin relevan: blogs.mathworks.com/loren/2008/08/04/…
Dan
@Dan Terima kasih atas referensi yang bagus.
angainor
Terima kasih untuk angainor respon yang bagus. 1 karena menjadi orang pertama yang dengan jelas menyatakan keuntungan utama bsxfundengan contoh yang baik.
Colin T Bowers
15

Mulai R2016b, Matlab mendukung Ekspansi Implisit untuk berbagai macam operator, jadi dalam banyak kasus, tidak perlu lagi menggunakan bsxfun:

Sebelumnya, fungsi ini tersedia melalui bsxfunfungsi tersebut. Sekarang Anda disarankan untuk mengganti sebagian besar penggunaan bsxfundengan panggilan langsung ke fungsi dan operator yang mendukung perluasan implisit . Dibandingkan dengan penggunaan bsxfun, ekspansi implisit menawarkan kecepatan yang lebih cepat , penggunaan memori yang lebih baik , dan keterbacaan kode yang lebih baik .

Ada diskusi rinci dari Ekspansi Implisit dan kinerjanya di blog Loren ini. Untuk mengutip Steve Eddins dari MathWorks:

Di R2016b, ekspansi implisit berfungsi secepat atau lebih cepat daripada bsxfundi kebanyakan kasus. Keuntungan performa terbaik untuk ekspansi implisit adalah dengan ukuran matriks dan array yang kecil. Untuk ukuran matriks besar, ekspansi implisit cenderung kurang lebih sama dengan kecepatan bsxfun.

nirwana-msu
sumber
9

Hal-hal tidak selalu konsisten dengan 3 metode umum repmat:, expension by ones indexing, dan bsxfun. Ini menjadi lebih menarik ketika Anda meningkatkan ukuran vektor lebih jauh. Lihat plot:

perbandingan

bsxfunsebenarnya menjadi sedikit lebih lambat dari dua lainnya di beberapa titik, tetapi yang mengejutkan saya adalah jika Anda meningkatkan ukuran vektor lebih banyak lagi (> elemen keluaran 13E6), bsxfun tiba-tiba menjadi lebih cepat lagi sekitar 3x. Kecepatan mereka tampaknya melompat-lompat dan urutannya tidak selalu konsisten. Dugaan saya adalah mungkin saja tergantung pada ukuran prosesor / memori, tetapi secara umum saya pikir saya akan tetap menggunakannya bsxfunjika memungkinkan.

Justin Wong
sumber