1 คะแนน โดย GN⁺ 2026-04-25 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • คอมไพเลอร์แบบ AOT ที่ทำ การอนุมานชนิดข้อมูลทั้งโปรแกรม จากซอร์ส Ruby แล้วแปลงเป็นโค้ด C เพื่อ สร้างไบนารีเนทีฟแบบสแตนด์อโลน
  • พัฒนาโดย matz ผู้สร้าง Ruby โดยตรง และตัวคอมไพเลอร์แบ็กเอนด์เองก็เขียนด้วย Ruby เป็นสถาปัตยกรรมแบบ self-hosting ที่ คอมไพล์ตัวเองได้
  • ประสิทธิภาพ เร็วขึ้นประมาณ 11.6 เท่า เมื่อเทียบกับ miniruby (Ruby 4.1.0dev), Conway's Game of Life เร็วขึ้น 86.7 เท่า, ackermann 74.8 เท่า, mandelbrot 58.1 เท่า
  • ไปป์ไลน์การคอมไพล์จะแปลง Ruby เป็น ข้อความ AST ด้วยพาร์เซอร์ที่อิง Prism ก่อน จากนั้นแบ็กเอนด์แบบ self-hosting จะทำการอนุมานชนิดและสร้างโค้ด C แล้วใช้คอมไพเลอร์ C มาตรฐานสร้างไบนารีแบบ standalone
  • รองรับฟีเจอร์ Ruby ได้กว้างขวาง เช่น class, inheritance, block, การจัดการข้อยกเว้น, Fiber, เอนจิน Regexp แบบ NFA ในตัว, Bigint แบบยกระดับอัตโนมัติ, pattern matching เป็นต้น
  • คลาสขนาดเล็กที่มีฟิลด์สเกลาร์ไม่เกิน 8 ฟิลด์จะถูก จัดสรรบนสแตกอัตโนมัติเป็น value type ทำให้ตัด GC overhead ได้ทั้งหมด (การจัดสรร 1 ล้านครั้ง 85ms → 2ms)
  • ทำ flatten การเชนสตริง a + b + c + d ด้วย malloc เพียงครั้งเดียว และ split ภายในลูปจะ นำ sp_StrArray กลับมาใช้ซ้ำ เพื่อตัดการจัดสรรที่ไม่จำเป็น
  • มีการเพิ่มประสิทธิภาพตอนคอมไพล์หลายอย่าง เช่น hoisting ความยาวที่ไม่เปลี่ยนในลูป, constant propagation, inline เมธอดอัตโนมัติเมื่อมีไม่เกิน 3 statement, และจบการอนุมานแบบวนซ้ำก่อนกำหนด (ช่วยลดเวลา bootstrap ได้ราว 14%)
  • ไบนารีที่สร้างขึ้น ไม่มี runtime dependency เลย, สามารถรันได้ด้วยเพียง libc + libm
  • ไม่รองรับ eval, metaprogramming, Thread และ Mutex เป็นต้น (รองรับเฉพาะ Fiber)
  • ไลเซนส์ MIT

1 ความคิดเห็น

 
GN⁺ 2026-04-25
ความคิดเห็นจาก Hacker News
  • ถ้าเป็นสิ่งที่ Matz ทำเอง ก็น่าจะเข้าใจข้อจำกัดของ Ruby semantics ดีอยู่แล้ว เลยรู้สึกเชื่อถือได้
    วิทยานิพนธ์ปริญญาโทของผมก็เป็น AOT JS compiler เหมือนกัน มันใช้งานได้จริง แต่ข้อจำกัดของข้อมูลขาเข้ามีเยอะมาก สุดท้ายเลยพับไป
    ตอนนั้นนักพัฒนา JS ยังไม่ค่อยคุ้นกับการรักษาข้อจำกัดเหล่านั้นด้วยตัวเอง และอินพุตที่ไม่อาจรู้ล่วงหน้าได้โดยเนื้อแท้อย่าง JSON.parse ก็เป็นอุปสรรคสำคัญ
    ตอนนี้ด้วย TypeScript มันอาจเป็นเรื่องที่ทำได้จริงมากกว่าสมัยนั้นมาก
    แค่ดู lambda calculus ทั่วไปก็จะเห็นชัดว่าการอนุมานชนิดข้อมูลมีขีดจำกัด และในงานของ Matt Might หรือโปรเจ็กต์ Shed-skin Python ก็มีข้อจำกัดคล้ายกันโผล่มาเหมือนกัน
    ผมสงสัยว่า eval, send, method_missing, define_method พบได้บ่อยแค่ไหนในโค้ด Ruby จริง ๆ และสงสัยด้วยว่าปกติจัดการกับการพาร์สแบบไม่มีชนิดข้อมูล เช่น อินพุต JSON กันอย่างไร

    • งานออกแบบนี้ดู pragmatic มาก
      การพาร์ส Ruby ยากจนแทบจะยากกว่าการแปลเสียอีก เลยใช้ Prism และผลลัพธ์คือสร้าง C ออกมา
      ตัว Ruby semantics พื้นฐานจริง ๆ ไม่ได้ยากขนาดนั้นที่จะอิมพลีเมนต์
      ตรงกันข้าม ผมกำลังง่วนอยู่กับ self-hosting AOT compiler เก่าที่เขียนด้วย Ruby ล้วน และดันยืนกรานจะใช้ parser ของตัวเอง เลยเลือกเส้นทางที่ยากกว่ามากโดยตั้งใจ
      ผมเรียนรู้ตั้งแต่เนิ่น ๆ ว่า 80% แรกนั้นทำแบบคร่าว ๆ ก็ยังรันโค้ด Ruby ได้เยอะพอสมควร แต่ “80% ที่สอง” ที่ยากจริง ๆ กลับไปกองอยู่กับสิ่งที่ Matz ตัดออกจากโปรเจ็กต์นี้และ mruby เช่น encoding หรือฟีเจอร์จิปาถะรอบข้างสารพัด
      พูดตามตรง Ruby มีฟีเจอร์อยู่ไม่น้อยที่ผมไม่เคยเห็นในโค้ดจริงเลยสักครั้ง ดังนั้นถ้าบางอย่างจะ deprecated ก็คงไม่แปลก
      send, method_missing, define_method เจอบ่อยมาก
      ข้อจำกัดก็คล้าย mruby และถึงมีข้อจำกัดแบบนั้นก็ยังมีที่ให้ใช้งานอยู่
      การรองรับ send, method_missing, define_method ค่อนข้างทำได้ไม่ยาก
      แต่การรองรับ eval() นั้นทรมานมาก
      อย่างไรก็ตาม สัดส่วนใหญ่ของ eval() ใน Ruby สามารถลดรูปแบบคงที่ให้เป็น instance_eval แบบ block version ได้ และในกรณีนั้น AOT compile ก็จะง่ายขึ้นมาก
      ตัวอย่างเช่น ถ้ารู้สตริงที่ใส่เข้า eval() แบบคงที่ได้ หรือสามารถแยกวิเคราะห์มันได้ ก็มีทางแก้ได้เยอะ
      ในทางปฏิบัติ การใช้ eval() จำนวนมากนั้นไม่จำเป็นหรือแทบเป็นแค่การอ้อมข้อจำกัดของ introspection แบบง่าย ๆ ซึ่งจัดการได้ด้วยการตรวจสอบแบบสถิต
      คอมไพเลอร์ของผมเองก็คิดว่าจะไปลงมือที่จุดนั้นก่อนถ้ามันกลายเป็นคอขวด
    • ต้องมีฟีเจอร์แบบนี้อยู่เยอะพอสมควรถึงจะทำ magic แบบ Rails ได้
      การรับ JSON แบบไม่มีชนิดข้อมูลก็น่าจะใช้กลไกพวกนี้เหมือนกัน
      ถ้าตัดสิ่งเหล่านั้นออกไป ก็จะเหลือภาษาที่เล็กและอ่านง่าย ซึ่งแม้จะไม่ strong type เท่า Crystal แต่ก็ไม่ได้พึ่ง metaprogramming หนักเท่า Ruby ทางการ
      เพราะงั้นศักยภาพก็ดูสูงพอสมควร แต่สุดท้ายก็คงต้องรอเวลาเป็นตัวตัดสิน
    • ถ้าเป็นวิธีคอมไพล์ Ruby ไปเป็น Objective-C ก็เหมือนจะรองรับฟีเจอร์ Ruby ได้ครบทั้งหมด พร้อมทั้งเร็วกว่า Ruby แบบ interpreter ได้ด้วย
    • ผมเป็นฝั่งที่ใช้ eval บ่อย
      จะไม่ใช้ก็ได้แหละ แต่สำหรับผมแบบนั้นมัน ergonomic กว่า
    • จากประสบการณ์ของผม สิ่งที่น่าสนใจคือแพตเทิร์นการใช้ eval, exec, define_method และการสร้างคลาสใหม่ด้วย Class.new, Struct.new
      การใช้งานส่วนใหญ่ของพวกนี้มักกระจุกอยู่ตอน boot แอป หรือระหว่าง require ไฟล์ และในแง่หนึ่งมันก็คล้ายขั้นตอนคอมไพล์อยู่แล้ว
  • นี่คือสิ่งที่ Matz เพิ่งประกาศในงาน RubyKaigi 2026
    แม้จะยังเป็นงานทดลอง แต่ก็ทำขึ้นในเวลาประมาณหนึ่งเดือนด้วยความช่วยเหลือจาก Claude และเดโมสดก็สำเร็จด้วย
    ชื่อนี้มาจากแมวตัวใหม่ของ Matz และชื่อแมวนั้นก็มาจากชื่อแมวใน Card Captor Sakura ซึ่งต่อจากนั้นก็ไปจับคู่กับตัวละครที่ชื่อ Ruby อีกที

    • คนชอบพูดกันว่า AI จะสร้างโปรแกรมตั้งแต่ต้นจนจบได้หมด แต่ผมว่าภาพที่สมจริงกว่าคือมันจะเปลี่ยน 10x programmer ให้กลายเป็น 100x programmer
      สำหรับคนอย่าง Matz มันอาจเหมือนดันจาก 100x ไปเป็น 500x เลยก็ได้
    • ภาพจำล่าสุดของผมเกี่ยวกับ Spinel คือฝั่ง Steven Universe เลยไม่เก็ตมุก Spinel/Ruby (Moon) pun เลยสักนิด แต่พอรู้แล้วก็อารมณ์ดีไปทั้งวัน
    • ตอนแรกผมก็นึกว่าเป็นเรื่องของ แร่ spinel แน่นอนเลย :)
      https://en.wikipedia.org/wiki/Spinel
    • ขอบคุณ
      ดูเหมือนว่าวิดีโอยังไม่ขึ้นเป็นไลฟ์ และน่าจะกำลังทยอยอัปขึ้นช่องนี้ทีละคลิป
      https://www.youtube.com/@rubykaigi4884/videos
    • เรื่องที่มาของชื่อแมวตัวนั้นฟังดูน่าสงสัยพอสมควร ถ้านึกถึง ดราม่า Ruby Central กับความสัมพันธ์กับผู้ก่อตั้ง Spinel.coop
      แม้แต่ชื่อโปรเจ็กต์เองก็ให้ความรู้สึกเหมือนตั้งจากอารมณ์
  • เห็นได้ชัดว่าน่าประทับใจมาก แต่ก็ดูเหมือนจะบำรุงรักษาไม่ได้เลยถ้าไม่มี AI agent
    spinel_codegen.rb ยาว 21,000 บรรทัด และบางเมธอดซ้อนลึกถึง 15 ชั้น
    โค้ดคอมไพเลอร์เดิมทีก็ยากที่จะเขียนให้สวยอยู่แล้ว แต่ชิ้นนี้ดูยากมากเป็นพิเศษสำหรับให้คนดูแลต่อ

    • โค้ดคอมไพเลอร์จริง ๆ แล้วทำให้ สวย ได้ ถ้ามีเวลาเพียงพอ
      คอมไพเลอร์มีเส้นแบ่ง subsystem ชัดเจน และ handoff ตามแต่ละขั้นก็ค่อนข้างชัด จึงเป็นงานประเภทที่ modular ได้ง่ายที่สุดอย่างหนึ่งด้วยซ้ำ
      ปัญหามักอยู่ที่ทำให้มันรันได้ก่อนแล้วไม่มีเวลา refactor หลังจากนั้น พอเป็นแบบนั้นความเละก็จะพอกพูนต่อไปเรื่อย ๆ
    • spinel_codegen.rb นี่แทบระดับ eldritch horror เลย
      เวลาใช้ Claude ผมก็ได้ spaghetti code แบบนี้ออกมาตลอด เลยเคยสงสัยว่าตัวเองทำอะไรผิดหรือเปล่า
      แต่พอเห็นว่าแม้แต่โปรเจ็กต์ที่น่าสนใจจริง ๆ จากคนที่ผมมองว่าเป็นโปรแกรมเมอร์ระดับท็อปก็ยังมีคุณภาพโค้ดแย่เป็นหย่อม ๆ เหมือนกัน ก็รู้สึกว่าไม่ได้เป็นอยู่คนเดียว
      อย่าง infer_comparison_type() ยังไม่ใช่กรณีเลวร้ายสุด และก็ไม่ได้อ่านยากอะไร แต่จริง ๆ มีวิธีเขียนที่ง่ายและชัดเจนกว่านี้มาก ทว่า Claude กลับไปไม่ถึง
      ถ้ารวม comparison operator ไว้ใน Set แล้วใช้ include? จัดการ ก็จะสั้นกว่า เร็วกว่า อ่านง่ายกว่า และดูแลง่ายกว่า
      แต่ Claude มักจะไหลไปทาง สาย if-return ต่อเนื่อง เสมอ แถมให้ความรู้สึกว่าไม่ค่อยคุ้นแม้แต่กับ if-else ด้วย
      โค้ดเบสที่ Claude เขียนให้ผมก็เต็มไปด้วยแพตเทิร์นแบบนั้น และตอนนี้ก็รู้แล้วว่าไม่ได้เป็นกับผมคนเดียว
      ในทางกลับกัน ไฟล์อื่น ๆ ดีกว่ามาก โดยเฉพาะไดเรกทอรี lib ที่ดูเหมือนจะสอดคล้องกับไดเรกทอรี ext ของ Ruby repo หลัก และคุณภาพก็ถือว่าดี
      ตัว API ก็ได้รับอิทธิพลจาก MRI Ruby อย่างชัดเจน และแม้อิมพลีเมนเทชันจะแตกต่างอยู่มาก แต่เหมือน Matz จะชี้นำให้บางส่วนของ API ต้นฉบับยังคงหน้าตาคล้ายเดิม เลยทำให้ผลลัพธ์ดูเป็นระเบียบขึ้น
      [1] https://github.com/matz/spinel/blob/98d1179670e4d6486bbd1547...
    • ในช่วงนี้ผมว่าเรื่องที่คนจะดูแลต่อด้วยมือได้หรือไม่ยังไม่สำคัญขนาดนั้น
      ขอแค่ผ่านทั้งการทดสอบและ benchmark ผมก็พอใจแล้ว
      แต่ไฟล์ขนาดมหึมาแบบนี้ AI เองจะจัดการได้ง่ายหรือเปล่าก็ยังน่าสงสัย
      ผมพยายามจำกัดไฟล์ไม่ให้เกินประมาณ 300 บรรทัด และคิดว่าโค้ดที่คนเข้าใจง่ายก็น่าจะง่ายสำหรับ coding agents ด้วยเหมือนกัน
  • ข้อจำกัดมีดังนี้
    No eval: eval, instance_eval, class_eval
    No metaprogramming: send, method_missing, define_method (แบบไดนามิก)
    No threads: Thread, Mutex (รองรับ Fiber)
    No encoding: สมมติว่าเป็น UTF-8/ASCII
    No general lambda calculus: -> x { } แบบซ้อนลึกพร้อมการเรียก []
    สำหรับผม การสมมติว่าเป็น UTF-8/ASCII ไม่ใช่ข้อจำกัดใหญ่ แต่ที่เหลือน่าจะเป็นข้อจำกัดจริงสำหรับโปรแกรมจำนวนมากพอสมควร
    และถ้าจะใส่กลับเข้าไป ก็ดูเหมือนต้องใช้แรงงานอีกมาก

    • แบบนี้ magic ของ Ruby ก็หายไปเยอะเลย
  • ผมใช้ Ruby มานาน และจากมุมของคนที่เคยใช้ฟีเจอร์ทั้งหมดที่ไล่มานั้น จริง ๆ แล้วสิ่งที่ผมอยากได้หลังผ่านการคัดสรรตามธรรมชาติกลับเป็น Ruby แบบเรียบง่าย อย่างนี้
    มันง่ายกว่า เข้าใจง่ายกว่า แต่ยังคงสุนทรียะแบบ Ruby เอาไว้
    ตอนนี้ด้วย LLM ประสิทธิภาพการสร้างโค้ดสูงมาก จนไม่จำเป็นต้องลด boilerplate ด้วย metaprogramming เพื่อเพิ่ม productivity แบบเมื่อก่อนอีกแล้ว
    เพราะสัดส่วนที่นักพัฒนาต้องเขียนโค้ดเองจริง ๆ ก็น้อยลงอยู่แล้ว

    • ถ้าสิ่งที่ต้องการมีแค่ Ruby aesthetic จริง ๆ Crystal ก็อาจเหมาะมาก
      ไวยากรณ์คล้ายกันและมี static type system จึงต่อยอดไปสู่โค้ดที่คอมไพล์แล้วมีประสิทธิภาพมากกว่า
    • การไม่มี eval ผมยังมองว่าอาจดีกว่า แต่การไม่มี threads และ mutexes ด้วยนี่น่าเสียดาย
      การไม่มี define_method ก็พอเข้าใจได้เมื่อคิดถึงการใช้งานของมัน
      แต่ send กับ method_missing นั้นพบได้บ่อยในไลบรารีเดิม และก็ดูไม่น่าจะยากมากที่จะอิมพลีเมนต์ด้วยการสร้าง memory lookup table ตั้งแต่ตอนคอมไพล์
      เลยไม่แน่ใจว่านี่เป็นสิ่งที่จงใจตัดออก หรือแค่ยังไปไม่ถึงตรงนั้น
      ผมหวังว่าอย่างหลัง แต่ไม่ว่าอย่างน้อยตอนนี้ก็คงยังเอาไปใช้จริงในงานได้ยากเพราะปัญหา compatibility
    • ข้อดีของ metaprogramming เดิมทีไม่ใช่ การเขียนโค้ดให้น้อยลง
      แต่มันคือ การลดปริมาณโค้ดที่ต้องอ่าน
  • นี่เจ๋งมากจริง ๆ และผมรอ AOT compiler สำหรับ Ruby มานานแล้ว
    แค่เสียดายที่ไม่มี fallback สำหรับ eval หรือ metaprogramming แต่ก็ดูเหมือนตั้งใจโฟกัสที่ subset เล็ก ๆ ที่ประสิทธิภาพสูง
    ผมหวังว่า gem ที่สร้างด้วย AOT compiler นี้จะทำงานร่วมกับ MRI ได้ดี
    ฝั่งการแพ็กหรือบันเดิล Ruby มาตรฐานกับ gem นั้นยังคงต้องพึ่ง tebako, kompo, ocran และในอดีตก็เคยมีโปรเจ็กต์อย่าง ruby-packer, traveling ruby, jruby warbler
    การมีตัวเลือกเพิ่มมาอีกหนึ่งอย่างถือว่าดี แต่ผมก็ยังอยากเห็น เวอร์ชันตัดสินเกม ที่มี UX ฝั่งนักพัฒนาดีกว่านี้

    • ใช่เลย ช่วงหลัง ๆ ผมเองก็ต้อง fork warbler เหมือนกัน
      เพราะมันไม่ได้อัปเดตมานานเกินไปแล้ว
  • ผมสงสัยว่าทำไมถึง no threads
    Ruby scheduler กับ pthread implementation ข้างใต้ก็ดูเหมือนน่าจะทำงานได้ดีในฝั่ง C อยู่แล้ว หรือว่าตั้งใจจะทำแบบ zero dependency
    ถ้าไม่ได้มีแผนจะใส่เป็น optional extension ทีหลัง หรือไม่ได้แค่ยังไม่ทันทำ การเลือกแบบนี้ก็ดูแปลกอยู่เหมือนกัน

    • ผมยังไม่เห็นหลักฐานว่ามีการ ตัดสินใจว่าจะไม่รองรับ เรื่องนี้โดยเจตนา
      น่าจะเป็นแค่ว่ายังทำไปไม่ถึงตรงนั้นมากกว่า
      การทำ multithreading ให้ถูกต้องนั้นเดิมทีก็ยากมากอยู่แล้ว
  • น่าทึ่งที่ทำได้ในเวลาแค่เดือนเศษ
    จะพูดเรื่อง AI ยังไงก็ตาม ถ้าอยู่ในมือของ นักพัฒนาที่มีฝีมือ มันสร้างแรงเร่งความเร็วได้มหาศาลจริง ๆ

    • ทั้งอุตสาหกรรมกำลังเริ่มด้วยการเซ็ต agent harness, SOUL.md, permission settings, skills, MCPs, hooks, env กันเต็มไปหมด
      แต่ Matz ให้ความรู้สึกว่าแค่ gem env|info กับ find ก็พอแล้ว
  • ในเมื่อ Matz เป็นคนทำเอง ก็สงสัยว่ามีโอกาสจริงจังแค่ไหนที่สิ่งนี้จะกลายเป็นส่วนหนึ่งของ Ruby core ในอนาคต
    แล้วถ้าเป็นแบบนั้น มันจะเป็นภัยต่อ Crystal มากแค่ไหนด้วย

    • Crystal มี static type system แบบชัดเจน และตัวภาษาก็ถูกออกแบบมาเพื่อ AOT compile ตั้งแต่ระดับภาษา
      คุณลักษณะพวกนี้แทบจะจำเป็นสำหรับการคอมไพล์และดูแลโปรแกรมขนาดใหญ่
      ขณะที่สิ่งนี้รองรับแค่ subset ของ Ruby ที่มีข้อจำกัด ดังนั้น gem ยอดนิยมส่วนใหญ่ของ Ruby ก็น่าจะรันตรง ๆ ไม่ได้
      ในแง่ที่มันเป็น subset ของภาษาที่มุ่งสู่การคอมไพล์เป็น C มันดูใกล้กับ PreScheme มากกว่า
      ณ ตอนนี้ผมยังไม่มองว่าทั้งสองอย่างกำลังแข่งกันตรง ๆ ในพื้นที่เดียวกัน
      Ruby แบบสมบูรณ์นั้นแทบแน่นอนว่ายังต้องพึ่ง JIT
      [1]: https://prescheme.org/
    • ถ้ามองอีกมุม สุดท้ายแล้ว LLM ก็น่าจะไปถึงจุดที่สามารถพ่น formal specification ออกมาในภาษาอะไรก็ได้ที่เราต้องการ
      มันเหมือนเป็นการล้างแค้นของเครื่องมืออย่าง Rational Unified Process กับ Enterprise Architect
      ต่างกันแค่ว่าแทนที่จะได้ UML diagram เราจะได้ไฟล์ markdown มาแทน
  • สิ่งนี้น่าจะมีประโยชน์ในฝั่ง infrastructure tools
    เช่น อาจจินตนาการถึง bundler ที่เขียนด้วย Ruby แต่คอมไพล์แบบสถิตได้ จนทำหน้าที่เป็นเครื่องมือติดตั้ง Ruby แบบ RVM ไปด้วยในตัว
    buildpack ของ Ruby แบบเดิมก็เขียนด้วย Ruby แต่ต้อง bootstrap ด้วย bash ทำให้ชวนหงุดหงิดและเกิด edge case ตามมา
    CNB เขียนด้วย Rust เพื่อเลี่ยงปัญหานั้น และแนวคิดเรื่องการแจกจ่ายเป็น single binary แบบไร้ dependency นั้นทรงพลังมากจริง ๆ