ใน Matlab ควรใช้ bsxfun เมื่อใด


136

คำถามของฉัน:ฉันสังเกตเห็นว่าคำตอบที่ดีมากมายสำหรับคำถาม 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) ใช้การดำเนินการไบนารีองค์ประกอบต่อองค์ประกอบที่ระบุโดยฟังก์ชันจับ fun กับอาร์เรย์ A และ B โดยใช้ singleton เปิดใช้งานส่วนขยาย ". วลี "พร้อมเปิดใช้การขยายซิงเกิลตัน" หมายความว่าอย่างไร


4
โปรดทราบว่าความเร็วในการอ่านที่คุณได้รับนั้นขึ้นอยู่กับการทดสอบที่คุณทำ หากคุณเรียกใช้โค้ดด้านบนหลังจากรีสตาร์ท Matlab และเพียงแค่วางtic...tocรอบ ๆ บรรทัดความเร็วของโค้ดจะขึ้นอยู่กับการต้องอ่านฟังก์ชันในหน่วยความจำ
Jonas

@Jonas ใช่ฉันเพิ่งเรียนรู้เกี่ยวกับเรื่องนี้โดยอ่านเกี่ยวกับtimeitฟังก์ชันในลิงค์ที่คุณ / angainor / Dan ให้มา
Colin T Bowers

คำตอบ:


153

มีสามเหตุผลคือผมใช้bsxfun( เอกสาร , บล็อกการเชื่อมโยง )

  1. bsxfunเร็วกว่าrepmat(ดูด้านล่าง)
  2. bsxfun ต้องพิมพ์น้อยลง
  3. การใช้bsxfunเช่นการใช้accumarrayทำให้ฉันรู้สึกดีกับความเข้าใจของ Matlab

bsxfunจะทำซ้ำอาร์เรย์อินพุตตาม "มิติเดี่ยว" ของพวกเขานั่นคือมิติตามขนาดของอาร์เรย์คือ 1 เพื่อให้ตรงกับขนาดของมิติที่สอดคล้องกันของอาร์เรย์อื่น นี่คือสิ่งที่เรียกว่า "singleton expasion" squeezeเช่นกันขนาดเดี่ยวเป็นคนที่จะลดลงถ้าคุณโทร

เป็นไปได้ว่าสำหรับปัญหาเล็ก ๆrepmatแนวทางจะเร็วกว่า - แต่ด้วยขนาดอาร์เรย์นั้นการดำเนินการทั้งสองจะรวดเร็วมากจนไม่น่าจะสร้างความแตกต่างในแง่ของประสิทธิภาพโดยรวม มีสาเหตุสำคัญสองประการbsxfunคือเร็วกว่า: (1) การคำนวณเกิดขึ้นในโค้ดที่คอมไพล์ซึ่งหมายความว่าการจำลองแบบจริงของอาร์เรย์จะไม่เกิดขึ้นและ (2) bsxfunเป็นหนึ่งในฟังก์ชัน Matlab แบบมัลติเธรด

ฉันได้ทำการเปรียบเทียบความเร็วระหว่างrepmatและbsxfunกับ R2012b บนแล็ปท็อปที่เร็วพอสมควร

ป้อนคำอธิบายภาพที่นี่

สำหรับผมbsxfunคือประมาณ 3 repmatครั้งเร็วกว่า ความแตกต่างจะเด่นชัดมากขึ้นหากอาร์เรย์มีขนาดใหญ่ขึ้น

ป้อนคำอธิบายภาพที่นี่

การกระโดดในรันไทม์repmatเกิดขึ้นรอบ ๆ อาร์เรย์ขนาด 1Mb ซึ่งอาจมีส่วนเกี่ยวข้องกับขนาดของแคชโปรเซสเซอร์ของฉัน - 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

41

ในกรณีของฉันฉันใช้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
ขอบคุณสำหรับการตอบกลับ Oli +1 ในขณะที่ฉันคิดว่าคำตอบนี้มีส่วนช่วยบางอย่างนอกเหนือจากคำตอบของ angainor และ Jonas ฉันชอบวิธีที่คุณระบุจำนวนปัญหาเชิงแนวคิดที่ต้องแก้ไขในบรรทัดโค้ดที่กำหนด
Colin T Bowers

17

คำถามที่น่าสนใจมาก! ฉันได้สะดุดเมื่อเร็ว ๆ นี้กับสถานการณ์ดังกล่าวว่าในขณะที่ตอบนี้คำถาม พิจารณารหัสต่อไปนี้ที่คำนวณดัชนีของหน้าต่างบานเลื่อนขนาด 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 และหน่ายมักจะคิดว่าการส่งมอบประสิทธิภาพ ..

แก้ไขขอบคุณความคิดเห็นของ Dan นี่เป็นบทความที่ยอดเยี่ยมโดย Loren ที่พูดถึงเรื่องนั้น


7
บทความนี้อาจเกี่ยวข้อง: blogs.mathworks.com/loren/2008/08/04/…
แดน

@ แดนขอบคุณสำหรับการอ้างอิงที่ดี
angainor

ขอบคุณสำหรับคำตอบที่ดีเยี่ยม +1 สำหรับการเป็นคนแรกที่ระบุข้อได้เปรียบหลักอย่างชัดเจนbsxfunพร้อมตัวอย่างที่ดี
Colin T Bowers

15

ตั้งแต่ R2016b Matlab รองรับImplicit Expansionสำหรับตัวดำเนินการที่หลากหลายดังนั้นในกรณีส่วนใหญ่จึงไม่จำเป็นต้องใช้bsxfun:

ก่อนหน้านี้ใช้งานได้ผ่านทางbsxfunฟังก์ชัน ก็จะแนะนำว่าตอนนี้คุณสามารถแทนที่การใช้งานมากที่สุดของbsxfunกับสายตรงไปยังฟังก์ชั่นและผู้ประกอบการที่สนับสนุนการขยายตัวโดยปริยาย เทียบกับการใช้bsxfun, โดยปริยายการขยายตัวของข้อเสนอความเร็วได้เร็วขึ้น , ใช้หน่วยความจำที่ดีขึ้นและการอ่านที่ดีขึ้นของรหัส

มีการอภิปรายโดยละเอียดเกี่ยวกับการขยายโดยนัยและประสิทธิภาพในบล็อกของลอเรน การพูดของสตีฟ Eddins จาก MathWorks:

ใน R2016b การขยายโดยปริยายจะทำงานได้เร็วหรือเร็วกว่าbsxfunในกรณีส่วนใหญ่ การเพิ่มประสิทธิภาพที่ดีที่สุดสำหรับการขยายโดยปริยายคือขนาดของเมทริกซ์และอาร์เรย์ขนาดเล็ก bsxfunสำหรับขนาดเมทริกซ์ขนาดใหญ่ขยายตัวโดยปริยายมีแนวโน้มที่จะประมาณความเร็วเดียวกับ


9

สิ่งที่ไม่เคยสอดคล้องกับ 3 วิธีการทั่วไป: repmat, Expension bsxfunโดยการจัดทำดัชนีคนและ มันค่อนข้างน่าสนใจมากขึ้นเมื่อคุณเพิ่มขนาดเวกเตอร์ให้มากขึ้น ดูพล็อต:

การเปรียบเทียบ

bsxfunจริง ๆ แล้วจะช้ากว่าอีกสองตัวเล็กน้อยในบางจุด แต่สิ่งที่ทำให้ฉันประหลาดใจคือถ้าคุณเพิ่มขนาดเวกเตอร์ให้มากขึ้น (> องค์ประกอบเอาต์พุต 13E6) bsxfun ก็จะเร็วขึ้นอีกครั้งโดยประมาณ 3x ความเร็วของพวกเขาดูเหมือนจะก้าวกระโดดและลำดับไม่สม่ำเสมอเสมอไป ฉันเดาว่ามันอาจขึ้นอยู่กับขนาดของโปรเซสเซอร์ / หน่วยความจำด้วย แต่โดยทั่วไปฉันคิดว่าฉันจะใช้bsxfunทุกครั้งที่ทำได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.