คุณสามารถรับแนวคิดได้โดยการเรียกใช้โค้ดเวอร์ชันอื่น ๆ พิจารณาเขียนการคำนวณอย่างชัดเจนแทนที่จะใช้ฟังก์ชันในลูปของคุณ
tic
Soln3 = ones(T, N);
for t = 1:T
for n = 1:N
Soln3(t, n) = 3*x(t, n)^2 + 2*x(t, n) - 1;
end
end
toc
เวลาคำนวณบนคอมพิวเตอร์ของฉัน:
Soln1 1.158446 seconds.
Soln2 10.392475 seconds.
Soln3 0.239023 seconds.
Oli 0.010672 seconds.
ตอนนี้ในขณะที่การแก้ปัญหาอย่างเต็มที่ 'vectorized' เป็นอย่างชัดเจนที่เร็วที่สุดที่คุณจะเห็นว่าการกำหนดฟังก์ชั่นที่จะเรียกว่ารายการ x ทุกคนเป็นอย่างมากค่าใช้จ่าย เพียงแค่เขียนการคำนวณอย่างชัดเจนก็ทำให้เราได้รับปัจจัยที่ 5 เร่งความเร็ว ผมคิดว่าการแสดงนี้ว่า MATLABs JIT คอมไพเลอร์ไม่สนับสนุนฟังก์ชั่นแบบอินไลน์ ตามคำตอบของ gnovice ที่นั่นการเขียนฟังก์ชันปกติจะดีกว่าการเขียนแบบไม่ระบุตัวตน ลองมัน.
ขั้นตอนต่อไป - ลบ (vectorize) วงใน:
tic
Soln4 = ones(T, N);
for t = 1:T
Soln4(t, :) = 3*x(t, :).^2 + 2*x(t, :) - 1;
end
toc
Soln4 0.053926 seconds.
การเร่งความเร็วปัจจัยที่ 5 อีกประการหนึ่ง: มีบางอย่างในข้อความเหล่านี้ที่บอกว่าคุณควรหลีกเลี่ยงการวนซ้ำใน MATLAB ... หรือมีจริง? ลองดูที่นี่แล้ว
tic
Soln5 = ones(T, N);
for n = 1:N
Soln5(:, n) = 3*x(:, n).^2 + 2*x(:, n) - 1;
end
toc
Soln5 0.013875 seconds.
ใกล้เคียงกับเวอร์ชัน vectorized 'เต็ม' มากขึ้น Matlab จัดเก็บเมทริกซ์คอลัมน์ที่ชาญฉลาด คุณควรจัดโครงสร้างการคำนวณของคุณ (ถ้าเป็นไปได้) ให้เป็นเวกเตอร์ 'คอลัมน์ที่ชาญฉลาด'
เราสามารถกลับไปที่ Soln3 ได้แล้ว ลำดับการวนซ้ำคือ 'row-wise' มาเปลี่ยนกันเถอะ
tic
Soln6 = ones(T, N);
for n = 1:N
for t = 1:T
Soln6(t, n) = 3*x(t, n)^2 + 2*x(t, n) - 1;
end
end
toc
Soln6 0.201661 seconds.
ดีขึ้น แต่ก็ยังแย่มาก ห่วงเดียว - ดี ห่วงคู่ - ไม่ดี ฉันเดาว่า MATLAB ทำงานได้ดีในการปรับปรุงประสิทธิภาพของลูป แต่ยังคงมีค่าใช้จ่ายของลูปอยู่ที่นั่น หากคุณมีงานที่หนักกว่าอยู่ข้างในคุณจะไม่สังเกตเห็น แต่เนื่องจากการคำนวณนี้มีการ จำกัด แบนด์วิธของหน่วยความจำคุณจึงเห็นค่าใช้จ่ายแบบวนซ้ำ และคุณจะเห็นค่าใช้จ่ายในการโทร Func1 ที่นั่นได้ชัดเจนยิ่งขึ้น
แล้ว arrayfun เกิดอะไรขึ้น? ไม่มีฟังก์ชัน inlinig เช่นกันค่าใช้จ่ายจำนวนมาก แต่ทำไมแย่กว่าการวนซ้ำซ้อนกันมากนัก? อันที่จริงเรื่องของการใช้ cellfun / arrayfun ได้รับการกล่าวถึงอย่างกว้างขวางหลายครั้ง (เช่นที่นี่ , ที่นี่ , ที่นี่และที่นี่ ) ฟังก์ชันเหล่านี้ทำงานช้าคุณไม่สามารถใช้สำหรับการคำนวณแบบละเอียดเช่นนี้ได้ คุณสามารถใช้มันเพื่อความกะทัดรัดของโค้ดและการแปลงแบบแฟนซีระหว่างเซลล์และอาร์เรย์ แต่ฟังก์ชันจะต้องหนักกว่าที่คุณเขียน:
tic
Soln7 = arrayfun(@(a)(3*x(:,a).^2 + 2*x(:,a) - 1), 1:N, 'UniformOutput', false);
toc
Soln7 0.016786 seconds.
โปรดทราบว่า Soln7 เป็นเซลล์ในขณะนี้ .. บางครั้งก็มีประโยชน์ ตอนนี้ประสิทธิภาพของโค้ดค่อนข้างดีและหากคุณต้องการเซลล์เป็นเอาต์พุตคุณไม่จำเป็นต้องแปลงเมทริกซ์ของคุณหลังจากที่คุณใช้โซลูชันแบบเวกเตอร์เต็มรูปแบบแล้ว
เหตุใด arrayfun จึงช้ากว่าโครงสร้างลูปธรรมดา น่าเสียดายที่เราไม่สามารถพูดได้อย่างแน่นอนเนื่องจากไม่มีซอร์สโค้ด คุณสามารถเดาได้ว่าเนื่องจาก arrayfun เป็นฟังก์ชันวัตถุประสงค์ทั่วไปซึ่งจัดการโครงสร้างข้อมูลและอาร์กิวเมนต์ที่แตกต่างกันทุกประเภทจึงไม่จำเป็นต้องเร็วมากในกรณีง่ายๆซึ่งคุณสามารถแสดงเป็นรังแบบวนซ้ำได้โดยตรง ค่าใช้จ่ายมาจากไหนเราไม่สามารถทราบได้ สามารถหลีกเลี่ยงค่าใช้จ่ายได้โดยการใช้งานที่ดีกว่าหรือไม่? อาจจะไม่. แต่น่าเสียดายที่สิ่งเดียวที่เราทำได้คือศึกษาประสิทธิภาพเพื่อระบุกรณีที่ใช้งานได้ดีและกรณีที่ทำไม่ได้
อัปเดตเนื่องจากเวลาดำเนินการของการทดสอบนี้สั้นเพื่อให้ได้ผลลัพธ์ที่เชื่อถือได้ฉันจึงเพิ่มการวนรอบการทดสอบ:
for i=1:1000
% compute
end
บางครั้งระบุไว้ด้านล่าง:
Soln5 8.192912 seconds.
Soln7 13.419675 seconds.
Oli 8.089113 seconds.
คุณจะเห็นว่า arrayfun ยังคงแย่อยู่ แต่อย่างน้อยก็ไม่ใช่คำสั่งขนาดสามคำสั่งที่แย่กว่าโซลูชันเวกเตอร์ ในทางกลับกันลูปเดียวที่มีการคำนวณแบบคอลัมน์จะเร็วพอ ๆ กับเวอร์ชันเวคเตอร์ทั้งหมด ... ซึ่งทั้งหมดนี้ทำได้ใน CPU ตัวเดียว ผลลัพธ์ของ Soln5 และ Soln7 จะไม่เปลี่ยนแปลงหากฉันเปลี่ยนเป็น 2 คอร์ - ใน Soln5 ฉันจะต้องใช้พาร์ฟอร์เพื่อให้มันขนานกัน ลืมเรื่อง speedup ... Soln7 ไม่ทำงานแบบขนานเนื่องจาก arrayfun ไม่ทำงานแบบขนาน Olis vectorized version ในทางกลับกัน:
Oli 5.508085 seconds.