Trong Matlab, khi nào thì tối ưu để sử dụng bsxfun?


135

Câu hỏi của tôi: Tôi nhận thấy rằng rất nhiều câu trả lời hay cho các câu hỏi Matlab về SO thường xuyên sử dụng chức năng này bsxfun. Tại sao?

Động lực: Trong tài liệu Matlab cho bsxfun, ví dụ sau được cung cấp:

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

Tất nhiên chúng ta có thể thực hiện thao tác tương tự bằng cách sử dụng:

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

Và trên thực tế, một bài kiểm tra tốc độ đơn giản cho thấy phương pháp thứ hai nhanh hơn khoảng 20%. Vậy tại sao phải sử dụng phương pháp đầu tiên? Tôi đoán có một số trường hợp sử dụng bsxfunsẽ nhanh hơn nhiều so với phương pháp "thủ công". Tôi thực sự thích thú khi thấy một ví dụ về tình huống như vậy và một lời giải thích về lý do tại sao nó nhanh hơn.

Ngoài ra, một yếu tố cuối cùng cho câu hỏi này, một lần nữa từ tài liệu Matlab cho bsxfun: "C = bsxfun (vui vẻ, A, B) áp dụng thao tác nhị phân từng phần tử được chỉ định bởi hàm xử lý vui cho mảng A và B, với singleton cho phép mở rộng. ". Cụm từ "với mở rộng singleton được bật" nghĩa là gì?


4
Lưu ý rằng tốc độ đọc bạn nhận được phụ thuộc vào bài kiểm tra bạn thực hiện. Nếu bạn chạy mã trên sau khi khởi động lại Matlab và chỉ cần đặt tic...tocxung quanh các dòng, tốc độ của mã sẽ phụ thuộc vào việc phải đọc các chức năng vào bộ nhớ.
Jonas

@Jonas Vâng, tôi vừa tìm hiểu về điều này bằng cách đọc về timeitchức năng trong liên kết mà bạn / angainor / Dan cung cấp.
Colin T Bowers

Câu trả lời:


152

Có ba lý do tôi sử dụng bsxfun( tài liệu , liên kết blog )

  1. bsxfunnhanh hơn repmat(xem bên dưới)
  2. bsxfun yêu cầu ít gõ
  3. Sử dụng bsxfun, giống như sử dụng accumarray, làm cho tôi cảm thấy tốt về sự hiểu biết của tôi về Matlab.

bsxfunsẽ sao chép các mảng đầu vào dọc theo "kích thước đơn" của chúng, tức là kích thước dọc theo kích thước của mảng là 1, sao cho chúng khớp với kích thước của kích thước tương ứng của mảng khác. Đây là những gì được gọi là "đơn lẻ". Bên cạnh đó, kích thước của singleton là kích thước sẽ bị loại bỏ nếu bạn gọi squeeze.

Có thể là đối với các vấn đề rất nhỏ, repmatcách tiếp cận nhanh hơn - nhưng với kích thước mảng đó, cả hai thao tác đều nhanh đến mức có thể sẽ không tạo ra bất kỳ sự khác biệt nào về hiệu suất tổng thể. Có hai lý do quan trọng bsxfunlà nhanh hơn: (1) phép tính xảy ra trong mã được biên dịch, điều đó có nghĩa là sự sao chép thực tế của mảng không bao giờ xảy ra và (2) bsxfunlà một trong các hàm Matlab đa luồng.

Tôi đã chạy một so sánh tốc độ giữa repmatbsxfunvới R2012b trên máy tính xách tay nhanh chóng của tôi.

nhập mô tả hình ảnh ở đây

Đối với tôi, bsxfunnhanh hơn khoảng 3 lần repmat. Sự khác biệt trở nên rõ rệt hơn nếu các mảng trở nên lớn hơn

nhập mô tả hình ảnh ở đây

Bước nhảy trong thời gian chạy repmatxảy ra xung quanh kích thước mảng 1Mb, có thể có liên quan đến kích thước bộ đệm của bộ xử lý của tôi - bsxfunkhông tệ như một bước nhảy, bởi vì nó chỉ cần phân bổ mảng đầu ra.

Dưới đây bạn tìm thấy mã tôi đã sử dụng cho thời gian:

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

Cảm ơn bạn đã phản hồi xuất sắc +1. Tôi đã đánh dấu câu trả lời này vì đây là cuộc thảo luận toàn diện nhất và cũng (tại thời điểm này) đã nhận được nhiều phiếu bầu nhất.
Colin T Bowers

40

Trong trường hợp của tôi, tôi sử dụng bsxfunvì nó tránh cho tôi suy nghĩ về các vấn đề cột hoặc hàng.

Để viết ví dụ của bạn:

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

Tôi phải giải quyết một số vấn đề:

1) size(A,1)hoặcsize(A,2)

2) ones(sizes(A,1),1)hoặcones(1,sizes(A,1))

3) ones(size(A, 1), 1) * mean(A)hoặcmean(A)*ones(size(A, 1), 1)

4) mean(A)hoặcmean(A,2)

Khi tôi sử dụng bsxfun, tôi chỉ phải giải quyết vấn đề cuối cùng:

a) mean(A)hoặcmean(A,2)

Bạn có thể nghĩ rằng nó là lười biếng hoặc một cái gì đó, nhưng khi tôi sử dụng bsxfun, tôi có ít lỗi hơn và tôi lập trình nhanh hơn .

Hơn nữa, nó ngắn hơn, giúp cải thiện tốc độ gõkhả năng đọc .


1
Cảm ơn đã trả lời Oli. +1 như tôi nghĩ câu trả lời này đã đóng góp một cái gì đó ngoài câu trả lời của kẻ gây án và Jonas. Tôi đặc biệt thích cách bạn đặt ra số lượng các vấn đề khái niệm cần được giải quyết trong một dòng mã nhất định.
Colin T Bowers

16

Câu hỏi rất thú vị! Gần đây tôi đã vấp phải tình huống chính xác như vậy trong khi trả lời câu hỏi này . Hãy xem xét đoạn mã sau để tính các chỉ số của cửa sổ trượt có kích thước 3 thông qua một vectơ 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

Trong trường hợp bsxfunnày là nhanh hơn gần gấp đôi! Nó rất hữu ích và nhanh chóng vì nó tránh sự phân bổ bộ nhớ rõ ràng cho ma trận idx0idx1, lưu chúng vào bộ nhớ, sau đó đọc lại chúng chỉ để thêm chúng. Vì băng thông bộ nhớ là một tài sản có giá trị và thường là nút cổ chai trên các kiến ​​trúc ngày nay, bạn muốn sử dụng nó một cách khôn ngoan và giảm các yêu cầu bộ nhớ trong mã của bạn để cải thiện hiệu suất.

bsxfuncho phép bạn làm điều đó: tạo ma trận dựa trên việc áp dụng toán tử tùy ý cho tất cả các cặp phần tử của hai vectơ, thay vì hoạt động rõ ràng trên hai ma trận thu được bằng cách sao chép vectơ. Đó là sự mở rộng đơn lẻ . Bạn cũng có thể nghĩ về nó như là sản phẩm bên ngoài từ BLAS:

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

Bạn nhân hai vectơ để thu được ma trận. Chỉ là sản phẩm bên ngoài chỉ thực hiện phép nhân và bsxfuncó thể áp dụng các toán tử tùy ý. Là một lưu ý phụ, rất thú vị khi thấy nó bsxfunnhanh như sản phẩm bên ngoài BLAS. Và BLAS thường được coi là mang lại hiệu suất ..

Chỉnh sửa Cảm ơn bình luận của Dan, đây là một bài viết tuyệt vời của Loren thảo luận chính xác về điều đó.


7
Bài viết này có thể có liên quan: blog.mathworks.com/loren/2008/08/04/ Quảng
Dan

@Dan Cảm ơn bạn đã tham khảo tuyệt vời.
angainor

Cảm ơn cho một angainor phản ứng tuyệt vời. +1 vì là người đầu tiên nêu rõ lợi thế chính bsxfunvới một ví dụ hay.
Colin T Bowers

13

Kể từ R2016b, Matlab hỗ trợ Mở rộng tiềm ẩn cho nhiều nhà khai thác khác nhau, do đó, trong hầu hết các trường hợp, không còn cần thiết phải sử dụng bsxfun:

Trước đây, chức năng này đã có sẵn thông qua bsxfunchức năng. Hiện tại, bạn nên thay thế hầu hết các sử dụng bsxfunbằng các cuộc gọi trực tiếp đến các chức năng và toán tử hỗ trợ mở rộng ngầm . So với việc sử dụng bsxfun, mở rộng ngầm cung cấp tốc độ nhanh hơn , sử dụng bộ nhớ tốt hơncải thiện khả năng đọc mã .

Có một cuộc thảo luận chi tiết của Expansion Implicit và hiệu quả của nó trên blog của Loren của. Để trích dẫn Steve Eddins từ MathWorks:

Trong R2016b, mở rộng ngầm hoạt động nhanh hoặc nhanh hơn bsxfuntrong hầu hết các trường hợp. Hiệu suất tốt nhất để mở rộng ngầm là với kích thước ma trận và mảng nhỏ. Đối với kích thước ma trận lớn, mở rộng ngầm có xu hướng gần bằng tốc độ bsxfun.


8

Mọi thứ không phải lúc nào cũng phù hợp với 3 phương pháp phổ biến : repmat, mở rộng bằng cách lập chỉ mục và bsxfun. Nó trở nên thú vị hơn khi bạn tăng kích thước vector hơn nữa. Xem cốt truyện:

so sánh

bsxfunThực tế trở nên chậm hơn một chút so với hai người kia vào một lúc nào đó, nhưng điều làm tôi ngạc nhiên là nếu bạn tăng kích thước vectơ hơn nữa (> 13E6 phần tử đầu ra), bsxfun đột nhiên trở lại nhanh hơn khoảng 3x. Tốc độ của chúng dường như nhảy theo các bước và thứ tự không phải lúc nào cũng nhất quán. Tôi đoán là nó cũng có thể phụ thuộc vào kích thước bộ nhớ / bộ nhớ, nhưng nói chung tôi nghĩ rằng tôi sẽ gắn bó bsxfunbất cứ khi nào có thể.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.