Multi-Threading แท้จริง, i18n ตอนคอมไพล์ และ watchOS
Perry v0.4.0 เป็นรีลีสที่ใหญ่ที่สุดตั้งแต่โปรเจกต์เริ่มต้น สามครั้งของการกระโดดเวอร์ชันในรอบเดียว — v0.3.0 (i18n), v0.3.2 (watchOS), v0.4.0 (multi-threading) — และตัวคอมไพเลอร์เองก็เป็นแบบขนานแล้ว นี่คือทุกอย่างที่ส่งมอบ
Multi-Threading ที่แท้จริง
Perry ตอนนี้มีการทำงานขนานแบบเธรดระบบปฏิบัติการจริง ไม่ใช่ web workers ที่มีค่าใช้จ่ายในการ serialization ไม่ใช่ SharedArrayBuffer กับ Atomics เธรดจริง — เธรดระบบปฏิบัติการน้ำหนักเบาพร้อม stack 8MB ที่ไม่แชร์อะไรเลยและไม่มีค่าใช้จ่ายเมื่อว่าง
โมดูล perry/thread ใหม่เปิดเผย primitive สามตัว:
import { parallelMap, parallelFilter, spawn } from "perry/thread";
// Split work across all CPU cores, results in order
const results = parallelMap(largeArray, (item) => heavyComputation(item));
// Filter in parallel
const matches = parallelFilter(data, (item) => expensiveCheck(item));
// Spawn a background thread, get a Promise
const result = await spawn(() => {
// runs on a separate OS thread
return computeExpensiveResult();
});parallelMap และ parallelFilter ตรวจจับจำนวนแกน CPU โดยอัตโนมัติและแบ่งอาร์เรย์อินพุตข้ามแกนเหล่านั้น สำหรับอาร์เรย์ขนาดเล็ก จะข้ามการใช้เธรดและรันแบบ synchronous — ไม่มีค่าใช้จ่ายสำหรับงานเล็กน้อย
spawn เปิดเธรดระบบปฏิบัติการในพื้นหลังและคืนค่า Promise ผลลัพธ์ไหลกลับผ่านคิวผลลัพธ์ที่รอดำเนินการซึ่งถูกระบายออกระหว่างการประมวลผล microtask ดังนั้นคุณ await มันเหมือนการดำเนินการ async อื่นๆ
ความปลอดภัยในเวลาคอมไพล์
ส่วนที่สำคัญที่สุดไม่ใช่ API — แต่เป็นสิ่งที่คอมไพเลอร์ป้องกัน Perry ปฏิเสธ closure ที่จับตัวแปรที่เปลี่ยนแปลงได้อย่างเป็นสถิต:
let counter = 0;
// ✗ Compile error: closure captures mutable variable 'counter'
parallelMap(items, (item) => {
counter++; // rejected at compile time
return item * 2;
});ไม่มีสถานะที่เปลี่ยนแปลงได้ร่วมกันหมายความว่าไม่มีการแข่งขันข้อมูล ไม่มี locks ไม่มี mutexes ไม่มี Atomics คอมไพเลอร์บังคับใช้ความปลอดภัยของเธรดก่อนที่โค้ดเครื่องแม้แต่บรรทัดเดียวจะถูกปล่อยออกมา
ภายใต้กลไก
เธรดทำงานแต่ละตัวได้รับ arena หน่วยความจำของตัวเองพร้อมการล้าง Drop — ไม่มีการประสานงาน GC ข้ามเธรด ค่าถูกถ่ายโอนผ่าน deep-copy SerializedValue: ไม่มีค่าใช้จ่ายสำหรับตัวเลข, O(n) สำหรับสตริง อาร์เรย์ และอ็อบเจกต์ การใช้งานอยู่ในไฟล์ Rust ไฟล์เดียว 1,120 บรรทัด (perry-runtime/src/thread.rs) และไม่ต้องเปลี่ยนแปลงตัวเก็บขยะ
เปรียบเทียบกับ V8 isolates ซึ่งต้องการ heaps แยกต่อ worker พร้อมค่าใช้จ่าย ~2MB แต่ละตัว เธรดของ Perry เป็นเพียง pthreads พร้อม arenas
ไปป์ไลน์คอมไพเลอร์แบบขนาน
ตัวคอมไพเลอร์เองก็เป็นแบบขนานแล้วเช่นกัน การสร้างโค้ดโมดูล, transform passes (JS imports, native instances, monomorphization) และการสแกนสัญลักษณ์ nm ทั้งหมดรันข้ามแกน CPU ทั้งหมดผ่าน rayon ร่วมกับการอัปเกรด Cranelift 0.121 (จาก 0.113 — แปดเวอร์ชันย่อยของการจัดสรรรีจิสเตอร์และการปรับปรุง x64) การคอมไพล์เร็วขึ้นอย่างมีนัยสำคัญ
i18n ในเวลาคอมไพล์ (v0.3.0)
ระบบสากลภิวัตน์ของ Perry ไม่มีพิธีรีตอง ตัวอักษรสตริงในวิดเจ็ต UI จะถูกปฏิบัติโดยอัตโนมัติเป็นคีย์ที่แปลเป็นภาษาท้องถิ่นได้ ไฟล์แปลเป็น JSON แบบแบนในไดเรกทอรี locales/ การตรวจสอบทั้งหมดเกิดขึ้นในเวลาคอมไพล์
// locales/en.json
{ "greeting": "Hello, {name}!" }
// locales/de.json
{ "greeting": "Hallo, {name}!" }
// Your code — just use strings normally
Button({ title: "greeting", action: () => {} })คอมไพเลอร์ตรวจสอบทุกอย่าง: การแปลที่ขาดหายไป ความไม่ตรงกันของพารามิเตอร์ ข้อผิดพลาดของรูปแบบพหูพจน์ การแปลถูกฝังในไบนารีเป็นตารางสตริง 2D แบบฝังพร้อมการค้นหาที่เกือบเป็นศูนย์ในขณะรันไทม์ — ไม่มีการ parsing JSON เมื่อเริ่มต้น
สิ่งที่รวมอยู่
- กฎพหูพจน์ CLDR สำหรับมากกว่า 30 locale พร้อมคำต่อท้าย
.one/.other/.few/.many/.zero/.two - Wrapper สำหรับรูปแบบ:
Currency,Percent,ShortDate,LongDate,FormatNumber,FormatTime,Raw - การตรวจจับ locale แบบเนทีฟบนทุกแพลตฟอร์ม:
CFLocaleCopyCurrent(macOS/iOS),GetUserDefaultLocaleName(Windows),system_property_get(Android),LANG/LC_ALL(Linux) perry i18n extractCLI: สแกนไฟล์ TS/TSX สร้างและอัปเดต scaffold JSON ของ locale- การสร้างทรัพยากรเนทีฟของแพลตฟอร์ม: ไดเรกทอรี iOS
.lprojและ Androidvalues-xx/ import { t } from "perry/i18n"สำหรับการแปลสตริงที่ไม่ใช่ UI
กำหนดค่าใน perry.toml:
[i18n]
locales = ["en", "de", "ja", "es", "fr"]
default_locale = "en"
currencies = { USD = "en", EUR = "de", JPY = "ja" }แอปเนทีฟ watchOS (v0.3.2)
Perry ตอนนี้คอมไพล์ไปยัง watchOS — เป้าหมายการคอมไพล์ลำดับที่ 9 นี่ไม่ใช่ wrapper หรือแอปคู่กัน เป็นไบนารี watchOS แบบสแตนด์อโลนพร้อมอินเทอร์เฟซ SwiftUI เนทีฟ
ตัวเรนเดอร์ watchOS ใช้แนวทางที่ขับเคลื่อนด้วยข้อมูล: Perry สร้างต้นไม้ UI ผ่านการเรียก FFI perry_ui_* และ PerryWatchApp.swift ที่ส่งมาจะสอบถามต้นไม้และเรนเดอร์มุมมอง SwiftUI แบบรีแอคทีฟ รองรับวิดเจ็ต 15 ประเภทพร้อม stub สำหรับประเภทที่ไม่รองรับ
# Compile for watchOS
perry compile main.ts --target watchos
# Run on Apple Watch simulator
perry run watchos
# Setup signing for watchOS
perry setup watchosขั้นตอนทั้งหมดใช้งานได้: perry setup watchos แชร์ข้อมูลรับรอง App Store Connect กับ iOS, perry run watchos ตรวจจับ Apple Watch simulator อัตโนมัติ และ perry publish watchos ส่งไปยัง App Store
สิ่งนี้ยังทำให้จำนวนเป้าหมายวิดเจ็ตรวมเป็นสี่: iOS (WidgetKit), Android (Glance), watchOS (WidgetKit) และ Wear OS (Tiles) แต่ละตัวมีเป้าหมายการคอมไพล์และ backend codegen ของตัวเอง
API เสียงและกล้อง
API ฮาร์ดแวร์ใหม่สองตัวในรีลีสนี้:
การจับเสียง (perry/system)
การจับเสียงข้ามแพลตฟอร์มพร้อมการวัด dB(A) แบบถ่วงน้ำหนัก A:
import { audioStart, audioStop, audioGetLevel, audioGetWaveformSamples } from "perry/system";
audioStart();
const level = audioGetLevel(); // dB(A) with EMA smoothing
const waveform = audioGetWaveformSamples(); // 256-sample ring buffer
audioStop();Backend แพลตฟอร์ม: AVAudioEngine (macOS/iOS), AudioRecord ผ่าน JNI (Android), PulseAudio (Linux), WASAPI (Windows), getUserMedia + AnalyserNode (Web)
การจับภาพกล้อง (perry/ui)
การแสดงตัวอย่างกล้องเนทีฟพร้อมการสุ่มตัวอย่างสีระดับพิกเซล (iOS):
import { CameraView, cameraStart, cameraSampleColor } from "perry/ui";
cameraStart();
const [r, g, b] = cameraSampleColor(x, y); // 5x5 averagingแพ็คเกจระบบนิเวศ
แพ็คเกจเนทีฟ first-party สองตัวเปิดตัว:
- perry/push — Binding การแจ้งเตือนแบบพุชสำหรับ iOS/macOS: การขอสิทธิ์, การดึง APNs token, จำนวนตรา stub Android พร้อม FCM ที่วางแผนไว้
- perry/storekit — Binding การซื้อในแอป StoreKit 2: การโหลดสินค้า, การซื้อพร้อมใบเสร็จ JWS, การตรวจสอบการสมัครสมาชิก, การกู้คืน และ listener ธุรกรรม
ทั้งสองเป็นไปตามสถาปัตยกรรมเดียวกัน: การประกาศ TypeScript → FFI crate ของ Rust → สะพาน Swift ติดตั้งเป็น dependency นำเข้าฟังก์ชัน await ผลลัพธ์ คอมไพเลอร์จัดการการเชื่อมต่อเนทีฟทั้งหมด
โครงสร้างพื้นฐาน
- Cranelift 0.113 → 0.121 — แปดเวอร์ชันย่อยของการจัดสรรรีจิสเตอร์ การแก้ไข x64 และการปรับปรุงการจัดเรียง stack slot
- การแบ่งฟังก์ชัน Windows — แบ่งฟังก์ชันที่มีมากกว่า 50 คำสั่งออกเป็น continuation อัตโนมัติเพื่อแก้ไขปัญหา codegen ของ Cranelift บน Windows
- การโหลดตัวแปรโมดูลแบบเลือกสรร — โหลดเฉพาะตัวแปรระดับโมดูลที่ถูกอ้างอิงที่ทางเข้าฟังก์ชัน ลดขนาดไบนารี Windows ลง 26%
- อัปเกรด Array.sort() — จากการเรียงลำดับแบบแทรก O(n²) เป็นแบบผสม TimSort O(n log n)
- perry run android — ไปป์ไลน์สร้าง APK เต็มรูปแบบ: คอมไพล์ สร้างโปรเจกต์ Gradle, assembleDebug, ติดตั้ง, เปิดใช้งาน
- รายการ Info.plist กำหนดเอง —
[ios.info_plist]ใน perry.toml สำหรับคำอธิบายความเป็นส่วนตัว, URL schemes, โหมดพื้นหลัง
ในตัวเลข
- เวอร์ชัน: 0.2.197 → 0.4.0 (สามเหตุการณ์สำคัญ)
- เป้าหมายการคอมไพล์: 8 → 9 (เพิ่ม watchOS)
- เป้าหมายวิดเจ็ต: 1 → 4 (iOS, Android, watchOS, Wear OS)
- Crate ใหม่: perry-ui-watchos, perry-codegen-glance, perry-codegen-wear-tiles
- เอกสารใหม่: threading (4 หน้า), i18n (4 หน้า), watchOS, เอกสารวิดเจ็ตที่ขยาย (3 → 8 หน้า)
- การใช้งาน perry/thread: 1,120 บรรทัดของ Rust ไม่มีการเปลี่ยนแปลง GC
ขั้นตอนถัดไป
รากฐาน threading เปิดโอกาสมากมาย: การประมวลผลคำขอ HTTP แบบขนาน การดำเนินการไฟล์พร้อมกัน และงานหนักด้านการคำนวณที่ก่อนหน้านี้ถูกบล็อกโดยการทำงานแบบ single-threaded ในด้านภาษา การรองรับ regex เต็มรูปแบบยังคงเป็นช่องว่างที่ใหญ่ที่สุด และการขยาย perry/ui (drag and drop, การเข้าถึง, DatePicker) ยังคงดำเนินต่อไป
ติดตามความคืบหน้าบน GitHub, อ่านเอกสารที่ docs.perryts.com, หรือดู แผนงาน สำหรับภาพรวมทั้งหมด