เป็นคำถามที่ดีมาก
การใช้งานแบบมัลติเธรดของฟังก์ชัน Fibonacci ไม่เร็วกว่าเวอร์ชั่นเธรดเดี่ยว ฟังก์ชั่นนั้นแสดงเฉพาะในโพสต์บล็อกเป็นตัวอย่างของเล่นเกี่ยวกับความสามารถในการเธรดใหม่โดยเน้นว่ามันช่วยให้วางไข่หลายเธรดในฟังก์ชั่นต่าง ๆ และตัวกำหนดตารางเวลาจะคำนวณปริมาณงานที่เหมาะสมที่สุด
ปัญหาคือว่า@spawn
มีค่าใช้จ่ายที่ไม่น่ารำคาญ1µs
ดังนั้นถ้าคุณวางไข่เธรดเพื่อทำงานที่ใช้เวลาน้อยกว่า1µs
คุณอาจทำร้ายการแสดงของคุณ ความหมายของ recursive fib(n)
มีความซับซ้อนเวลาชี้แจงของการสั่งซื้อ1.6180^n
[1] ดังนั้นเมื่อคุณโทรหาfib(43)
คุณบางสิ่งบางอย่างเพื่อวางไข่ของ1.6180^43
หัวข้อ หากแต่ละคนใช้เวลา1µs
ในการวางไข่จะใช้เวลาประมาณ 16 นาทีในการวางไข่และกำหนดเวลาเธรดที่ต้องการและนั่นก็ไม่ได้คำนึงถึงเวลาที่ใช้ในการคำนวณจริงและการรวมเธรด / การซิงค์อีกครั้ง เวลามากขึ้น.
สิ่งต่าง ๆ เช่นนี้ที่คุณวางไข่เธรดสำหรับแต่ละขั้นตอนของการคำนวณจะทำให้รู้สึกได้ก็ต่อเมื่อแต่ละขั้นตอนของการคำนวณใช้เวลานานเมื่อเทียบกับ@spawn
ค่าใช้จ่าย
โปรดทราบว่ามีงานที่จะช่วยลดค่าใช้จ่าย@spawn
แต่ด้วยฟิสิกส์ของชิปซิลิโคนหลายคอร์ฉันสงสัยว่ามันจะเร็วพอสำหรับการfib
ใช้งานข้างต้น
หากคุณอยากรู้เกี่ยวกับวิธีที่เราสามารถปรับเปลี่ยนfib
ฟังก์ชั่นเธรดให้เป็นประโยชน์จริง ๆ สิ่งที่ง่ายที่สุดที่จะทำก็คือเพียงวางไข่fib
เธรดถ้าเราคิดว่ามันจะใช้เวลานานกว่า1µs
จะรันอย่างมีนัยสำคัญ บนเครื่องของฉัน (ทำงานบน 16 คอร์กายภาพ) ฉันได้รับ
function F(n)
if n < 2
return n
else
return F(n-1)+F(n-2)
end
end
julia> @btime F(23);
122.920 μs (0 allocations: 0 bytes)
ดังนั้นมันจึงเป็นคำสั่งที่ดีสองคำในเรื่องของการวางไข่ ดูเหมือนว่าเป็นการตัดยอดเยี่ยมที่จะใช้:
function fib(n::Int)
if n < 2
return n
elseif n > 23
t = @spawn fib(n - 2)
return fib(n - 1) + fetch(t)
else
return fib(n-1) + fib(n-2)
end
end
ตอนนี้ถ้าฉันทำตามวิธีการวัดประสิทธิภาพที่เหมาะสมด้วย BenchmarkTools.jl [2] ฉันพบ
julia> using BenchmarkTools
julia> @btime fib(43)
971.842 ms (1496518 allocations: 33.64 MiB)
433494437
julia> @btime F(43)
1.866 s (0 allocations: 0 bytes)
433494437
@Anush ถามในความคิดเห็น: นี่คือปัจจัยของ 2 ความเร็วในการใช้ 16 แกนดูเหมือนว่า เป็นไปได้หรือไม่ที่จะเพิ่มความเร็ว 16 ระดับให้เร็วขึ้น?
ใช่แล้ว. ปัญหาเกี่ยวกับฟังก์ชั่นด้านบนคือร่างกายมีขนาดใหญ่กว่าของที่F
มีเงื่อนไขจำนวนมากวางไข่ฟังก์ชัน / เธรดและทั้งหมดนั้น @code_llvm F(10)
@code_llvm fib(10)
ผมขอเชิญคุณที่จะเปรียบเทียบ ซึ่งหมายความว่าfib
เป็นการยากสำหรับจูเลียที่จะเพิ่มประสิทธิภาพ ค่าใช้จ่ายพิเศษนี้ทำให้โลกของความแตกต่างสำหรับn
กรณีเล็ก ๆ
julia> @btime F(20);
28.844 μs (0 allocations: 0 bytes)
julia> @btime fib(20);
242.208 μs (20 allocations: 320 bytes)
ไม่นะ! รหัสพิเศษทั้งหมดที่ไม่เคยสัมผัสn < 23
คือการทำให้เราช้าลงด้วยลำดับความสำคัญ! มีวิธีแก้ไขที่ง่าย: เมื่อn < 23
ไม่หักเงินกลับคืนให้fib
โทรหาเธรดเดี่ยวF
แทน
function fib(n::Int)
if n > 23
t = @spawn fib(n - 2)
return fib(n - 1) + fetch(t)
else
return F(n)
end
end
julia> @btime fib(43)
138.876 ms (185594 allocations: 13.64 MiB)
433494437
ซึ่งให้ผลลัพธ์ใกล้เคียงกับสิ่งที่เราคาดหวังสำหรับเธรดจำนวนมาก
[1] https://www.geeksforgeeks.org/time-complexity-recursive-fibonacci-program/
[2] @btime
มาโครBenchmarkTools จาก BenchmarkTools.jl จะเรียกใช้ฟังก์ชั่นหลาย ๆ ครั้งข้ามการรวบรวมเวลาและผลลัพธ์โดยเฉลี่ย