หากคุณต้องการดึงประสิทธิภาพสูงสุดจากโปรเจกต์ Ruby ของคุณ เราต้องสำรวจ fibers และวิธีที่พวกมันทำงาน
มาเริ่มด้วยคำถามง่ายๆ...
Fibers คืออะไร?
Fibers คือผู้ทำงาน (workers) พวกมันรันโค้ดและติดตามความคืบหน้าของตัวเอง กล่าวอีกนัยหนึ่ง fibers เป็นกลไก concurrency (คอนคอร์เรนซี)
ใช่!
เหมือนกับ threads (เธรด) แตต่างตรงที่คุณมีการควบคุม fibers มากกว่า threads
ในความหมายไหนที่คุณมีการควบคุมมากกว่า?
จริงๆ แล้ว
ระบบปฏิบัติการของคุณตัดสินใจว่าจะรัน threads เมื่อไหร่ และหยุดพวกมันเมื่อไหร่
นั่นไม่ใช่กรณีกับ fibers!
คุณต้องบอก fiber ว่าพอดีรันเมื่อไหร่ และหยุดเมื่อไหร่
Fibers vs Threads
Threads รู้สึกเหมือนว่าพวกมันรันในพื้นหลัง ทำงานของพวกมัน
Fibers ไม่ทำอย่างนั้น
ขณะที่ fiber กำลังรัน มันจะกลายเป็นโปรแกรมหลักจนกว่าคุณจะหยุดมัน
มาดูตัวอย่างโค้ด!
การใช้ Fibers: ตัวอย่าง
สร้าง fiber ด้วย Fiber.new และบล็อก
ตัวอย่าง:
f = Fiber.new { puts 1 }
จากนั้นรัน fiber โดยใช้เมธอด resume
แบบนี้:
f.resume
สิ่งนี้พิมพ์ 1 และส่งคืนการควบคุมไปยังโปรแกรมหลักของคุณ
แตคุณหยุด fiber ยังไง?
ด้วยเมธอด Fiber.yield ซึ่งแตกต่างจากคีย์เวิร์ด yield ที่ใช้สำหรับบล็อก
ตัวอย่าง:
f = Fiber.new { puts 1; Fiber.yield; puts 2 }
f.resume # 1
f.resume # 2
สิ่งนี้จะพิมพ์ 1 หลังจากเริ่ม fiber ด้วย resume แล้วหยุด
หากคุณเรียก resume อีกครั้ง...
มันจะดำเนินต่อจากที่หยุดไว้พอดี และพิมพ์ 2
หมายเหตุ: การ resume fiber นี้อีกครั้ง ทำให้เกิด
FiberError: dead fiber calledเพราะไม่มีโค้ดเหลือให้รัน
นี่คือเหตุผลที่ fibers มีประโยชน์!
การเรียก Fiber.yield ภายใน fiber เหมือนกดปุ่ม pause (หยุดชั่วคราว) อนุญาตให้คุณหยุดกลางลูป หรือโค้ดใดๆ ที่คุณเขียนภายในบล็อกของ fiber
Fibers และ Loops: ลำดับไม่มีที่สิ้นสุด
เราสามารถใช้เอฟเฟกต์ "ปุ่ม pause" นี้เพื่อสร้างลำดับไม่มีที่สิ้นสุด (infinite sequence)
ส่วนผสม:
- Fibers
- Loop
- Counter
ตัวอย่าง เลขแฟกทอเรียล (factorial numbers):
factorial = Fiber.new do
count = 1
loop do
Fiber.yield (1..count).inject(:*)
count += 1
end
end
คุณสามารถใช้ fiber นี้หลายครั้งเท่าที่ต้องการ ด้วยเมธอด resume เพื่อได้หมายเลขถัดไปในลำดับ
ตัวอย่าง:
Array.new(5) { factorial.resume } # [1, 2, 6, 24, 120]
เยี่ยม!
Fibers และ IO: การดำเนินการ Async
Fibers เร็วและมีประสิทธิภาพกว่า threads สำหรับสิ่งที่คุณต้องรอ เช่น การเชื่อมต่อเครือข่าย
ทำไม?
เพราะ fibers ทำให้เกิด context switches น้อยกว่า
Context switch คือเมื่อ CPU เปลี่ยนจากงานปัจจุบันไปงานอื่น
สิ่งนี้มีค่าใช้จ่ายน้อย แตสะสมกันไป!
ผมทดสอบบ้างโดยใช้ perf tool ของ Linux และพบว่าในแอปพลิเคชัน Ruby 単純 fibers สร้าง context switches น้อยกว่า threads 3 เท่า
ดูดีไหม?
คุณสามารถเริ่มใช้ fibers ตอนนี้:
- ด้วยเมธอด
IO.select+ รูปแบบการออกแบบ Reactor - กับ async gem
- กับ falcon application server
จำไว้...
ไม่มี magic bullets (วิธีแก้ไขทุกอย่าง) แตคุ้มค่าที่จะทดสอบและดูว่า fibers ทำอะไรให้คุณได้ 🙂
สรุป
คุณได้เรียนรู้เกี่ยวกับ Fibers ใน Ruby แล้ว! Fiber ช่วยให้คุณสร้างหน่วยโค้ดที่สามารถ หยุดชั่วคราวและเริ่มต่อได้ตามความต้องการของคุณ
ตอนนี้ถึงคุณลองทำดูสิ
ขอบคุณที่อ่าน!