โครงสร้างภายในคอมไพเลอร์

การตัดสินใจออกแบบหลักที่ทำให้ Perry เร็ว

01NaN-Boxing

ค่าถูกเก็บเป็น float 64 บิตพร้อม bit pattern พิเศษสำหรับ pointer ทำให้ union type ทำงานได้โดยไม่มี overhead ที่รันไทม์ ซึ่งทำให้ (string | number)[] ทำงานได้อย่างมีประสิทธิภาพ

โดยการเข้ารหัสข้อมูล type โดยตรงใน bit payload ของ NaN ตามมาตรฐาน IEEE 754 Perry หลีกเลี่ยงความจำเป็นของ tagged union หรือ boxed value คำ 64 บิตเดียวสามารถแทนค่า JavaScript type ใดก็ได้ — ตัวเลขใช้การแสดงผลตามธรรมชาติ ในขณะที่ string, object และค่า heap-allocated อื่น ๆ ใช้ pointer bit pattern ที่อยู่ในช่วง NaN

02Monomorphization

Generic ถูกเฉพาะทางในเวลาคอมไพล์ เหมือน Rust การ instantiate type แต่ละครั้งจะสร้างโค้ดที่ปรับให้เหมาะสม กำจัด overhead ของการตรวจสอบ type ที่รันไทม์

เมื่อคุณเขียน Array'<'number'>' และ Array'<'string'>' Perry จะสร้าง implementation ที่แยกกันสองตัวซึ่งปรับให้เหมาะสมอย่างเต็มที่ นั่นหมายความว่าการดำเนินการ array บนตัวเลขใช้เลขคณิตเครื่องโดยตรง — ไม่มี type guard, ไม่มี dynamic dispatch, ไม่มี boxing

03Static Dispatch

ไม่มี virtual table การเรียก method ถูกแก้ไขในเวลาคอมไพล์ ทำให้สามารถเรียกฟังก์ชันโดยตรงและเพิ่มประสิทธิภาพด้วย inlining

Runtime OOP แบบดั้งเดิมใช้ vtable สำหรับการแก้ไข method ซึ่งเพิ่มชั้นของ indirection ในทุกการเรียก Perry แก้ไขการเรียก method ทั้งหมดแบบ static ระหว่างการคอมไพล์ เปลี่ยนการเรียก method ของ interface เป็น jump โดยตรง ซึ่งยังปลดล็อก inlining แบบเข้มข้น — method ขนาดเล็กมักถูกรวมเข้ากับจุดเรียกโดยตรง

04Zero-Cost Abstractions

คลาส, interface และ generic ของ TypeScript คอมไพล์เป็นโค้ดเนทีฟที่มีประสิทธิภาพโดยไม่มี overhead ของ runtime representation

ระบบ type ของ TypeScript มีอยู่เฉพาะเวลาคอมไพล์ — และ Perry รับเรื่องนี้อย่างตรงตัว Interface ไม่สร้างโค้ดรันไทม์ ลำดับชั้นของคลาสถูกทำให้แบน Generic constraint ถูกแก้ไขเป็น type ที่เป็นรูปธรรม ไบนารีที่คอมไพล์แล้วมีเพียงลอจิกจริง โดยไม่มีกลไกนามธรรมที่ interpreter มักแบกรับ