แบ็กเอนด์ C++ แบบใหม่สำหรับคอมไพเลอร์ OCaml
(github.com/ocaml)- มีการเสนอ แบ็กเอนด์ใหม่ที่รองรับการสร้างโค้ด C++ ให้กับคอมไพเลอร์ OCaml เพื่อชดเชยข้อจำกัดของแบ็กเอนด์ C แบบเดิม
- โค้ดที่แปลงแล้วจะเขียนในรูปแบบ ฟังก์ชันล้วน และมีการนำบางส่วนของโมดูล
Listมาเขียนใหม่ โดยไม่ใช้สถานะแบบเปลี่ยนแปลงได้หรือไลบรารีมาตรฐาน - การรันต้องใช้ คอมไพเลอร์ C++ (
g++) และรองรับออปชันสำหรับปลดข้อจำกัดความลึกของเทมเพลตหรือส่งอาร์กิวเมนต์ - ประสิทธิภาพแตกต่างกันไปตามคอมไพเลอร์ และเมื่อใช้ อัลกอริทึม sieve แบบอิง priority queue ที่ปรับปรุงแล้ว จะช่วยเพิ่มความเร็วและประสิทธิภาพด้านหน่วยความจำ
- ชุมชนมองว่านี่คือ การทดลองผสานภาษาเชิงฟังก์ชันกับเทมเพลตเมตาโปรแกรมมิง และยังมีการพูดถึงความเป็นไปได้ในการขยายไปยัง Rust
ข้อเสนอเพิ่มแบ็กเอนด์ C++
- มีการเสนอแพตช์เพื่อ เพิ่มแบ็กเอนด์ C++ ให้กับคอมไพเลอร์ OCaml (
ocamlc)- เป็นรูปแบบที่ปรับปรุงจากแบ็กเอนด์ C แบบไม่เป็น incremental ซึ่งใช้อยู่ในรันไทม์และ FFI เดิม
- สามารถแปลงโค้ด OCaml เป็น C++ ได้ด้วยคำสั่ง
ocamlc -incr-c primes.ml
- โค้ด C++ ที่แปลงแล้วจะเขียนในรูปแบบ ฟังก์ชันล้วน และไม่รองรับสถานะแบบเปลี่ยนแปลงได้
- ด้วยเหตุนี้จึง ไม่สามารถใช้ไลบรารีมาตรฐานได้ และในตัวอย่างมีการเขียนบางส่วนของโมดูล
Listขึ้นใหม่ในสไตล์ฟังก์ชันล้วน - เอาต์พุตจะแสดงในรูปโครงสร้างซ้อนแบบ
Cons<hd, tl>และใช้โครงสร้างแยกเพื่อหลีกเลี่ยงการชนกับตัวดำเนินการ::ของ C++
- ด้วยเหตุนี้จึง ไม่สามารถใช้ไลบรารีมาตรฐานได้ และในตัวอย่างมีการเขียนบางส่วนของโมดูล
- การรันต้องใช้ คอมไพเลอร์ C++ (
g++) และสามารถส่งอาร์กิวเมนต์ได้ด้วยออปชัน-Dlimit=100- ผลลัพธ์การรันจะแสดงในรูปแบบข้อความผิดพลาดของคอมไพเลอร์
- หากเป็นการคำนวณขนาดใหญ่ สามารถปลดข้อจำกัดความลึกของเทมเพลตได้ด้วยออปชัน
-ftemplate-depth=999999
- ประสิทธิภาพแตกต่างกันไปตามคอมไพเลอร์
g++ใช้เวลาประมาณ 30 วินาทีและหน่วยความจำ 11GiB สำหรับการคำนวณจำนวนเฉพาะไม่เกิน 10000clang++เตือนภายในไม่ถึง 1 วินาทีก่อนเกิด segmentation fault- เมื่อใช้งาน อัลกอริทึม sieve ของ O’Neill ที่อิง priority queue ประสิทธิภาพดีขึ้นเป็น 8 วินาทีและใช้หน่วยความจำ 3.1GiB
- มีการกล่าวถึง ทิศทางการขยายในอนาคต ว่าอาจรองรับ Rust ได้
- มีการระบุว่าหาก Rust ทำ
impl specializationแบบบางส่วนได้สมบูรณ์ ก็อาจสามารถรันโปรแกรม OCaml ได้
- มีการระบุว่าหาก Rust ทำ
ปฏิกิริยาและการถกเถียงของชุมชน
-
การทดสอบฟีเจอร์และฟีดแบ็ก
redianthusถามว่ารองรับ ชนิดข้อมูลเวียนเกิดแบบไม่เป็นเอกพันธ์ หรือไม่stedolanแก้ข้อผิดพลาดที่เกิดจาก%predintยังไม่ได้ถูก implement และยืนยันว่าชนิดข้อมูลดังกล่าวทำงานได้ตามปกติ
-
มุกตลกและปฏิกิริยา
avsmแซวว่า “อยากได้ C-- แต่ดันเป็น C++ งั้นประนีประนอมด้วย C# แล้วกัน”stedolanตอบกลับว่า “ปีหน้าจะลองกับจำนวนเชิงซ้อน ℂ”- มีปฏิกิริยาด้วยอีโมจิอย่าง 😂, ❤️, 🚀 จำนวนมาก สะท้อนการตอบรับเชิงบวกจากชุมชน
-
ข้อเสนอเชิงเทคนิค
AdelKSเสนอทางเลือกโดยใช้ การประเมินผลแบบconstexprแทนเทมเพลต- พร้อมแชร์โค้ดตัวอย่างที่คำนวณจำนวนเฉพาะตอนคอมไพล์และใส่ผลลัพธ์ลงในไบนารีโดยตรง
LoganDarkตอบแบบขำ ๆ ว่า “แบบนั้นมันไม่ใช่ความสนุกแท้ ๆ” เพื่ออธิบายเหตุผลที่ใช้เทมเพลต
-
การอภิปรายเพิ่มเติม
redianthusกล่าวว่า “ตอนนี้ C++ กลายเป็นภาษาเชิงฟังก์ชันอย่างแท้จริงแล้ว”- พร้อมเน้นว่าสามารถนำโครงสร้างข้อมูลแบบฟังก์ชันล้วนของ OCaml ไปทำใน C++ ได้
dzmitry-lahodaกล่าวถึงโปรเจกต์ที่ทำให้รัน OCaml บน Rust ได้แล้ว (contextgeneric/cgp)
ตัวอย่างประสิทธิภาพและการรัน
- ตัวอย่างพื้นฐาน: โปรแกรมคำนวณจำนวนเฉพาะ
ocamlc -incr-c primes.ml→ สร้างprimes.cpp- รัน
g++ -Dlimit=100 primes.cppแล้วจะแสดงรายการจำนวนเฉพาะ
-
การตั้งค่าเพื่อประสิทธิภาพสูง
g++ -ftemplate-depth=999999 -Dlimit=10000 primes.cpp- ใช้เวลาประมาณ 30 วินาที และหน่วยความจำ 11GiB
-
เมื่อใช้อัลกอริทึมที่ปรับปรุงแล้ว
- ประสิทธิภาพดีขึ้นเป็น 8 วินาที และใช้หน่วยความจำ 3.1GiB
บทสรุป
- PR นี้คือ การทดลองแบ็กเอนด์แบบใหม่ที่แปลง OCaml เป็น C++ ซึ่งแสดงให้เห็นความเป็นไปได้ของการผสานภาษาเชิงฟังก์ชันเข้ากับเทมเพลตเมตาโปรแกรมมิง
- ชุมชนมองว่านี่เป็น กรณีตัวอย่างของการผสานมุกเชิงเทคนิคกับการทดลองอย่างสร้างสรรค์ และมีการตอบรับอย่างคึกคัก
- ยังมีการชี้ถึงความเป็นไปได้ในการขยายไปยังภาษาอื่นอย่าง Rust ด้วย
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
เนื้อหายอดเยี่ยมมาก อยากให้ทิปอย่างหนึ่งเวลาต้องเขียน โค้ด C++ ที่รันนาน ๆ
น่าแปลกที่ C++ interpreter ไม่มี tail call optimization เลย
เพราะงั้นโค้ด C++ แบบที่เป็นสำนวนส่วนใหญ่จึงมักเขียนฟังก์ชันอย่าง
reverse,map,range,filterขึ้นมาเองเพื่อไม่ให้สแตกพังถ้าเขียนแบบนี้ คนที่ต้องมาดูแลต่อจะสบายกว่ามาก อย่าไปพึ่ง command-line flag เลย ใช้ แนวทางที่พกพาได้ จะดีกว่า
พออ่านประโยคที่ว่า “ถ้าใช้โครงสร้างข้อมูลระดับสูงแบบนี้ g++ จะคำนวณจำนวนเฉพาะที่น้อยกว่า 10000 ได้ในเวลาแค่ 8 วินาที และใช้หน่วยความจำเพียง 3.1GiB” ก็หลุดขำออกมา
ในที่สุดก็ คำนวณจำนวนเฉพาะบนโน้ตบุ๊ก ของฉันได้แล้ว
เห็นตรงที่บอกว่า “โค้ดนี้ถูกแปลเป็น C++ ที่ idiomatic และอ่านง่าย” แล้วรู้สึกเห็นด้วยมาก
ในฐานะคนที่ชอบ C++ ฉันคิดว่านี่คือ โค้ด C++ ที่อ่านง่าย จริง ๆ
typedef I<((I<((n::val (p::val))>::val) != (I<0>::val))> res;นี่มัน ลูกเล่นเทมเพลตระดับเวทมนตร์ ชัด ๆนี่เป็นครั้งแรกที่ฉันเห็นการใส่นิพจน์เงื่อนไขไว้ใน type definition ของ C++
พอมาดูทีหลังถึงรู้ว่านี่ไม่ใช่โค้ดจริง แต่เป็นส่วนหนึ่งของตรรกะการประเมินเทมเพลต
พูดอีกอย่างคือ มีการ โยนบางส่วนของตรรกะคอมไพเลอร์ของภาษาระดับสูงไปให้ template engine จัดการ
วิธีแบบนี้อาจมีประสิทธิภาพกว่าการไปเสียเวลากับ parser เพิ่มก็ได้
เลยทำให้ตอนนี้ฉันเริ่มคิดว่าอาจลองใช้ C++ เป็นเป้าหมายการ lowering ของ elevate compiler framework ที่กำลังพัฒนาอยู่
ตอนเห็นประโยค “C++ is a purely functional language” ตอนแรกฉันนึกว่าเป็นพิมพ์ผิดจนต้องเลิกคิ้ว
แต่พอรู้ว่าไม่ได้พิมพ์ผิดก็ยิ่งน่าสนใจเข้าไปอีก เนื้อหาที่เหลือก็ดีเยี่ยมด้วย
บทความนี้ทำให้ทั้งวันของฉันสดใสขึ้นเลย ขอบคุณ
Stephen Dolan ไม่เคยทำให้ผิดหวังเลย สร้างความประหลาดใจได้ทุกครั้ง
ตอนอ่านตรงที่ว่า “C++ เป็นภาษาฟังก์ชันบริสุทธิ์ และไม่รองรับ mutable state การจะรันโปรแกรม C++ ต้องใช้ C++ interpreter”
แวบแรกฉันนึกว่าเป็น มุกวันโกหกเดือนเมษายน เสียอีก ทั้งที่เมษายนก็ผ่านไปแล้ว