Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

MJIT ใน Ruby 2.6 คืออะไรและทำงานอย่างไร

ประสิทธิภาพของ Ruby พัฒนาขึ้นมากในเวอร์ชันแล้วรุ่นเล่า… และทีมพัฒนา Ruby พยายามทุกวิถีทางเพื่อทำให้ Ruby เร็วยิ่งขึ้น!

หนึ่งในความพยายามเหล่านี้คือโครงการ 3×3

เป้าหมาย?

Ruby 3.0 จะเร็วกว่า Ruby 2.0 ถึง 3 เท่า .

ส่วนหนึ่งของโครงการนี้คือคอมไพเลอร์ MJIT ใหม่ ซึ่งเป็นหัวข้อของบทความนี้

อธิบาย MJIT

MJIT ย่อมาจาก “Method Based Just-in-Time Compiler”

หมายความว่าอย่างไร

Ruby รวบรวมโค้ดของคุณเป็น คำแนะนำ YARV , คำแนะนำเหล่านี้ดำเนินการโดย Ruby Virtual Machine

JIT เพิ่มเลเยอร์นี้อีกชั้นหนึ่ง

จะ รวบรวมคำสั่งที่ใช้บ่อย เป็นรหัสไบนารี่

ผลลัพธ์ที่ได้คือไบนารีที่ปรับให้เหมาะสมซึ่งรันโค้ดของคุณเร็วขึ้น

มันทำงานอย่างไร

มาดูกันว่า MJIT ทำงานอย่างไรเพื่อให้เข้าใจดีขึ้น

คุณสามารถเปิดใช้งาน JIT ด้วย Ruby 2.6 &the --jit ตัวเลือก

ถูกใจสิ่งนี้ :

ruby --jit app.rb

Ruby 2.6 มาพร้อมกับชุดตัวเลือกเฉพาะ JIT ที่จะช่วยให้เราค้นพบว่ามันทำงานอย่างไร คุณสามารถดูตัวเลือกเหล่านี้ได้โดยเรียกใช้ ruby --help .

นี่คือรายการตัวเลือก

  • –jit-เดี๋ยว
  • –jit-verbose
  • –jit-save-temps
  • –jit-max-cache
  • –jit-min-calls

ละเอียด Thisนี้ ตัวเลือกดูเหมือนเป็นจุดเริ่มต้นที่ดี!

เราจะใช้ --jit-wait . ด้วย ซึ่งจะทำให้ Ruby รอจนกว่าการรวบรวมโค้ด JIT เสร็จสิ้นก่อนที่จะเรียกใช้

ระหว่างการทำงานปกติ JIT จะคอมไพล์โค้ดในเธรดผู้ปฏิบัติงาน และไม่รอให้จบ

นี่คือคำสั่งที่คุณสามารถเรียกใช้เพื่อทดสอบสิ่งนี้:

ruby --disable-gems --jit --jit-verbose=1 --jit-wait -e "4.times { 123 }"

ภาพพิมพ์นี้ :

Successful MJIT finish

มันไม่น่าสนใจมากใช่ไหม

JIT ไม่ได้ทำอะไรเลย

ทำไม?

เพราะโดยค่าเริ่มต้น JIT จะทำงานเมื่อมีการเรียกเมธอด 5 ครั้งเท่านั้น (jit-min-calls ) ขึ้นไป

ถ้าเราเรียกใช้สิ่งนี้:

ruby --disable-gems --jit --jit-verbose=1 --jit-wait -e "5.times { 123 }"

ตอนนี้เราได้รับสิ่งที่น่าสนใจ :

JIT success (32.1ms): block in <main>@-e:1 -> /tmp/_ruby_mjit_p13921u0.c

นี่พูดว่าอะไรนะ

JIT รวบรวมบล็อกเพราะเราเรียกมัน 5 ครั้ง สิ่งนี้บอกคุณ:

  • ใช้เวลาในการคอมไพล์ (32.1ms )
  • ตรง สิ่งที่รวบรวม (block in <main> )
  • ไฟล์ที่สร้างขึ้น (/tmp/_ruby_mjit_p13921u0.c ) เป็นแหล่งที่มาของการรวบรวมนี้

ไฟล์นี้เป็นซอร์สโค้ด C ซึ่งรวบรวมเป็นไฟล์อ็อบเจ็กต์ (.o ) แล้วลงในไฟล์ไลบรารีที่ใช้ร่วมกัน (.so )

คุณสามารถเข้าถึงไฟล์เหล่านี้ได้หากคุณเพิ่ม --jit-save-temps ตัวเลือก

นี่คือตัวอย่าง :

MJIT ใน Ruby 2.6 คืออะไรและทำงานอย่างไร

นี่คือความเข้าใจในปัจจุบันของฉันเกี่ยวกับวิธีการทำงานของ JIT :

  1. นับการเรียกใช้เมธอด
  2. เมื่อเมธอดหนึ่งถูกเรียก 5 ครั้ง (ค่าเริ่มต้นสำหรับ jit-min-calls ) ทริกเกอร์ JIT
  3. ไฟล์ C ที่มีคำแนะนำสำหรับวิธีนี้ถูกสร้างขึ้น (เป็นคำสั่ง YARV แต่อยู่ในบรรทัด)
  4. การคอมไพล์เกิดขึ้นในเบื้องหลัง (เว้นแต่ --jit-wait ) โดยใช้คอมไพเลอร์ C ปกติเช่น GCC
  5. เมื่อคอมไพล์เสร็จสิ้น ไฟล์ไลบรารีที่ใช้ร่วมกันที่ได้จะถูกใช้เมื่อมีการเรียกเมธอดนี้

มาดูกันว่าวิธีนี้ได้ผลแค่ไหน

การทดสอบ MJIT:เร็วกว่าจริงหรือไม่

เป้าหมายของ MJIT คือทำให้ Ruby เร็วขึ้น

ทำตอนนี้ดีแค่ไหน

มาดูกัน!

ขั้นแรก เกณฑ์มาตรฐานไมโคร:

เกณฑ์มาตรฐาน ผลลัพธ์ (เทียบกับ Ruby 2.6 ที่ไม่มี JIT)
ในขณะที่ เร็วขึ้น 8 เท่า
ในขณะที่ต่อท้ายสตริง เร็วขึ้น 10%
ในขณะที่มีการคูณ (จำนวนเต็ม) เร็วขึ้น 4 เท่า
ในขณะที่มีการคูณ (Bignum) ช้าลง 20%
ตัวพิมพ์ใหญ่ เร็วขึ้น 10%
การจับคู่สตริง ช้าลง 2%
สตริงตรงกันหรือไม่ เร็วขึ้น 10%
อาร์เรย์ที่มีตัวเลขสุ่ม 10k เร็วขึ้น 20%

ดูเหมือนว่าการแสดงจะอยู่ทั่วๆ ไป แต่มีบางอย่างที่เราสรุปได้จากสิ่งนี้…

MJIT ชอบลูปมาก!

แต่การใช้งานที่ซับซ้อนกว่านี้จะเป็นอย่างไร

มาลองใช้แอปซินาตราง่ายๆ :

require 'sinatra'

get '/' do
  "apples, oranges & bananas"
end

มันอาจจะดูไม่มากนัก แต่โค้ดเล็กน้อยนี้ใช้วิธีการต่างๆ มากกว่า 500 วิธี เพียงพอที่จะให้ JIT มีงานทำ!

อย่างเจาะจง นี่คือ Sinatra 2.0.4 กับ Thin 1.7.2

คุณสามารถรันเบนช์มาร์กได้ด้วยคำสั่งนี้ (apache bench):

ab -c 20 -t 10 https://localhost:4567/

นี่คือผลลัพธ์ :

MJIT ใน Ruby 2.6 คืออะไรและทำงานอย่างไร

คุณสามารถบอกได้จากสิ่งเหล่านี้ว่า Ruby 2.6 เร็วกว่า 2.5 แต่ การเปิดใช้งาน JIT ทำให้ Sinatra ช้าลง 11% !

ทำไม?

ฉันไม่รู้ อาจเป็นเพราะค่าโสหุ้ยที่ JIT แนะนำ หรือเพราะโค้ดไม่ได้รับการปรับให้เหมาะสม

การทดสอบของฉันกับ C profiler (callgrind) เผยให้เห็นว่าการใช้โค้ดที่ปรับให้เหมาะสม JIT (ไฟล์ C ที่คอมไพล์ที่เราค้นพบก่อนหน้านี้) นั้นต่ำมากสำหรับ Sinatra (น้อยกว่า 2% ) แต่ก็สูงมาก (24.22% ) สำหรับคำสั่ง while ที่เพิ่มความเร็ว 8 เท่า

ผลลัพธ์ในขณะที่เปรียบเทียบกับ JIT :

MJIT ใน Ruby 2.6 คืออะไรและทำงานอย่างไร

ผลลัพธ์สำหรับการเปรียบเทียบ Sinatra กับ JIT :

MJIT ใน Ruby 2.6 คืออะไรและทำงานอย่างไร

นี่อาจเป็นส่วนหนึ่งของเหตุผล ฉันไม่ใช่ผู้เชี่ยวชาญด้านคอมไพเลอร์ ดังนั้นฉันจึงไม่สามารถสรุปได้จากเรื่องนี้

สรุป

MJIT คือ “Just-in-Time Compiler” ที่มีอยู่ใน Ruby 2.6 ซึ่งสามารถเปิดใช้งานได้ด้วย --jit ธง. MJIT มีแนวโน้มดีและสามารถเร่งความเร็วโปรแกรมเล็กๆ บางโปรแกรมได้ แต่ก็ยังมีงานอีกมากที่ต้องทำ!

หากคุณชอบบทความนี้อย่าลืมแบ่งปันกับเพื่อน Ruby ของคุณ 🙂

ขอบคุณสำหรับการอ่าน