คำร้องเชิงเทคนิคจากนักพัฒนา Anukari ถึง Apple
(anukari.com)- Anukari เป็น ซินธิไซเซอร์ฟิสิกส์ 3D แบบเรียลไทม์ จึงต้องคำนวณโมเดล spring-mass ขนาดใหญ่บน GPU แต่บน Apple silicon macOS หากความถี่สัญญาณนาฬิกา GPU ไม่เร่งขึ้นพอ ก็จะทำให้ยากต่อการรักษาเงื่อนไขด้าน latency ของเสียง
- โครงสร้างที่ DAW เรียกปลั๊กอินทีละบล็อกของออดิโอบัฟเฟอร์ ทำงานซ้อนกับ power management heuristic ของ macOS จน GPU อาจดูเหมือนกำลังพักระหว่างบล็อก และค้างอยู่ในสถานะประสิทธิภาพต่ำ
- ใน Metal profiler ของ Xcode Instruments เมื่อตั้ง Performance State เป็น Maximum จะทำงานได้ตามปกติ แต่เมื่อเป็น Minimum ประสิทธิภาพจะแย่ลงมาก ยืนยันได้ว่าคอขวดหลักคือความเร็วสัญญาณนาฬิกาของ GPU
- ปัจจุบันใช้วิธีแก้ชั่วคราวแบบ “waste makes haste” โดยสร้าง spin kernel ขนาดเล็กเพื่อเพิ่มภาระ GPU แบบเทียม แต่บนฮาร์ดแวร์ Apple บางรุ่นระดับ Pro/Max ก็ยังมีปัญหาอยู่
- นักพัฒนาขอให้ทีม Apple Metal พิจารณาการขยาย Audio Workgroup ไปยัง GPU, เพิ่มออปชันความไวต่อ real-time ให้กับ MTLCommandQueue หรืออย่างน้อยชี้แนวทางแก้ที่มีอยู่ โดยมองว่าบน Windows ไม่จำเป็นต้องมี spin loop แบบเดียวกัน
ปัญหาประสิทธิภาพ GPU บน macOS ที่ Anukari กำลังเผชิญ
- Anukari 3D Physics Synthesizer จำลอง โมเดลฟิสิกส์ spring-mass ขนาดใหญ่แบบเรียลไทม์เพื่อสร้างเสียง
- หากต้องรองรับวัตถุฟิสิกส์จำนวนมาก จำเป็นต้องใช้ GPU และโค้ดฟิสิกส์นี้ใกล้เคียงกับการติดคอขวดที่ ALU มากกว่าหน่วยความจำ
- สถานะที่เปลี่ยนแปลงได้ของการจำลองถูกเก็บไว้ใน threadgroup memory ของ GPU
- เป็นโครงสร้างที่คล้ายแคช L1 ที่จัดสรรเอง จึงเร็วมาก
- รูปแบบการใช้งานทั่วไปคือรันเป็นปลั๊กอิน AU หรือ VST3 ภายใน DAW อย่าง Pro Tools หรือ Ableton
- DAW จะเรียก Anukari ในทุกบล็อกของออดิโอบัฟเฟอร์
- Anukari จะรันเคอร์เนลจำลองฟิสิกส์บน GPU สำหรับแต่ละบล็อก รอผลลัพธ์ แล้วจึงส่งกลับ
- บล็อกของออดิโอบัฟเฟอร์สามารถช่วยเฉลี่ย latency จากการ scheduling ของ GPU kernel ไปในหลาย sample ได้ แต่เวลารันของตัว kernel เองยังคงเป็นปัจจัยชี้ขาด
การชนกันระหว่างการจัดการพลังงานของ macOS กับออดิโอแบบเรียลไทม์
- Apple silicon สามารถลด ความเร็วสัญญาณนาฬิกา ของชิปเพื่อประหยัดพลังงาน และ macOS จะคงความถี่ต่ำไว้หากมองว่าความต้องการประมวลผลยังต่ำ
- รูปแบบที่ Anukari ทำงานภายใน DAW ไม่สอดคล้องกับวิธีที่ macOS ประเมินความต้องการใช้ GPU
- GPU จะว่างระหว่างบล็อกของออดิโอบัฟเฟอร์ ทำให้ภาระเฉลี่ยอาจดูเหมือนมีเพียงประมาณ 60% เป็นต้น
- ไม่ทราบ heuristic จริงของ macOS แต่คาดว่าอาจคล้าย load average
- ภาระระดับนี้อาจไม่ถึงเกณฑ์ที่ทำให้ GPU เร่งความถี่ขึ้น
- Anukari ต้องการ latency ต่ำเพื่อให้ผ่านข้อจำกัดแบบเรียลไทม์ และจึงต้องการ GPU clock ที่สูง
- ไม่ทราบแน่ชัดว่า Apple GPU สามารถลดความถี่ลงได้ต่ำแค่ไหน แต่ต่ำพอที่จะทำให้ Anukari ใช้งานไม่ได้
ปัญหาเรื่อง clock ที่ยืนยันได้ด้วย Metal profiler
- ใช้ Metal profiler ใน Apple Instruments ที่มาพร้อม Xcode เพื่อตรวจยืนยันว่า Anukari เป็นงานแบบ ALU-bound
- Metal profiler สามารถเลือก Metal “Performance State” ระหว่างการโปรไฟล์ได้
- ตั้งค่านี้จากภายนอก profiler ไม่ได้
- ใน performance state แบบ Maximum นั้น Anukari ทำงานได้สมบูรณ์
- ใน performance state แบบ Minimum ประสิทธิภาพแย่ลงมาก
- ความต่างระหว่างสองสถานะนี้ทำให้เห็นชัดว่าความเร็วสัญญาณนาฬิกา GPU คือหัวใจของปัญหาประสิทธิภาพของ Anukari
วิธีแก้ชั่วคราวแบบ “waste makes haste” และข้อจำกัด
- เพราะ macOS ไม่เร่งความถี่ GPU ให้ในจังหวะที่ต้องการ Anukari จึงใช้วิธีแก้ชั่วคราวเฉพาะ
- รันงาน GPU ชุดที่สองควบคู่กับงานคำนวณออดิโอบน GPU เพื่อสร้าง ภาระเฉลี่ยที่สูง และกระตุ้นให้ macOS เร่ง clock ขึ้น
- งานนี้ถูกปรับให้ใช้ทรัพยากร GPU ให้น้อยที่สุด แต่ยังพอไปกระตุ้น heuristic ด้าน clock ได้
- โดยพื้นฐานคือ spin loop ที่คอย “อุ่น” GPU
- กลยุทธ์นี้ถูกเรียกว่า “waste makes haste” และมี บันทึกรายละเอียด ไว้ใน devlog
- บน MacBook M1 ของผู้พัฒนา วิธีนี้แก้ปัญหาได้สมบูรณ์และทำให้ Anukari ทำงานเสถียร
- แต่หลังจากเปิดตัว Anukari Beta ก็พบว่าผู้ใช้ macOS บางรายยังมีปัญหา
- โดยเฉพาะผู้ใช้ฮาร์ดแวร์ Apple ระดับ Pro หรือ Max ดูเหมือนจะพบปัญหาประสิทธิภาพมากกว่า
- บทความตั้งสมมุติฐานถึงความเป็นไปได้เรื่อง clock แยกอิสระต่อ GPU chiplet หรือภาระ spin workload อาจเบาเกินไปสำหรับ GPU ที่แรงกว่า
แนวทางแก้ที่ต้องการจาก Apple
- โดยตั้งอยู่บนสมมุติฐานว่า Apple น่าจะรู้ดีกว่า ผู้พัฒนาจึงเสนอแนวทางแก้ที่เป็นไปได้บางข้อ
- Solution 1: ขยายแนวคิดของ Audio Workgroup ไปสู่การประมวลผลบน GPU
- การประมวลผลออดิโอบน macOS ทำงานอยู่ในเธรดหรือกลุ่มเธรดที่เรียกว่า Audio Workgroup
- ระบบปฏิบัติการเข้าใจว่าเธรดเหล่านี้มีข้อจำกัดแบบเรียลไทม์และให้ลำดับความสำคัญ
- จึงอาจมอง MTLCommandQueue ที่ควบคุมโดยเธรดใน Audio Workgroup ว่าเป็นงานแบบเรียลไทม์และปรับ clock ของ GPU ตามนั้นได้
- Solution 2: เพิ่มออปชันใน Metal API ของ MTLCommandQueue เพื่อระบุความไวต่อ real-time
- จะได้ปรับความถี่ของ GPU chiplet ที่ประมวลผล queue นั้นให้เหมาะสม
- Solution 3: หากมีวิธีทำให้ได้ผลลัพธ์ที่ต้องการอยู่แล้ว เพียง Apple บอกวิธีก็เพียงพอ
- ตอนต้นบทความยังระบุว่า Apple ได้ติดต่อมาแล้ว และมีรายละเอียดใน อีกบทความหนึ่ง
Game Mode และการเปรียบเทียบกับ Windows
- Game Mode ของ Apple ดูคล้ายกับสิ่งที่ Anukari ต้องการ แต่ใช้งานจริงได้ยาก
- Game Mode เป็นแบบ ระดับโปรเซส
- Anukari ส่วนใหญ่ถูกใช้เป็นปลั๊กอินภายในอีกโปรเซสหนึ่ง และโปรเซสนั้นไม่รองรับ Game Mode
- Anukari ควบคุมสิ่งนี้เองไม่ได้
- Game Mode ยังต้องการ fullscreen แต่โดยปกติ Anukari ไม่ได้ทำงานแบบ fullscreen
- ปัญหานี้ไม่เกิดบน Windows
- ไม่แน่ชัดว่าเป็นเพราะ Windows เปิดให้ผู้ใช้ควบคุม performance state ได้มากกว่า หรือเพราะไดรเวอร์ NVIDIA ระวังการใช้พลังงานน้อยกว่า
- บน Windows ไม่จำเป็นต้องมี spin loop
- มีการยกตัวอย่างเปรียบเทียบว่า PC Windows ที่มี GPU ไม่แรงมากยังรัน Anukari ได้ดี ขณะที่ Mac M4 Max ราคาแพงอาจเกิดอาการ stutter ได้
เหตุผลที่การทำ pipelining ไม่เหมาะ
- การทำให้โค้ด GPU เป็น pipeline เพื่อให้ GPU ทำงานเต็มกำลัง เหมาะกับงานที่เน้น throughput แต่ Anukari เป็นงานที่ ไวต่อ latency
- หาก schedule เคอร์เนลจำลองฟิสิกส์หลายตัวล่วงหน้า CPU ก็อาจเตรียมบล็อกถัดไปได้ระหว่างที่ GPU ประมวลผลบล็อกออดิโอปัจจุบัน
- แต่ pipelining จะเพิ่ม throughput แลกกับการเพิ่ม latency
- แต่ละการรัน kernel ของ Anukari ต้องเข้าถึง ข้อมูลอินพุตออดิโอแบบเรียลไทม์ เช่นสัญญาณจากไมโครโฟน
- การประมวลผลบล็อกออดิโอถัดไปล่วงหน้าแบบ speculative execution จึงใช้ไม่ได้ เพราะยังไม่มีข้อมูลอินพุตที่จำเป็น
ปัญหาของการใส่ spin kernel ลงใน MTLCommandQueue เดียวกัน
- หากต้นเหตุจริงคือ spin kernel กับ physics kernel ไปรันอยู่บน GPU chiplet คนละตัว การใส่ทั้งคู่ใน MTLCommandQueue เดียวกันอาจดูเหมือนเป็นทางแก้
- มีการทดลองวิธีนี้จริง แต่ไม่ได้ผล
- สาเหตุคือ Anukari เป็นงานที่ไวต่อ latency
- spin kernel บางครั้งรันนานเกินไปเล็กน้อย
- เวลาส่วนนั้นไปกินช่วงเวลาของ physics kernel
- ยังมีการทดลองใช้ spin kernel ขนาดเล็กกับ volatile unified memory โดยให้ CPU เขียนแฟล็ก “exit kernel early”
- ถึงจะมีเทคนิคนี้ ก็ยังเกิดกรณีที่ spin kernel เบียดเวลาของ physics kernel อยู่ดี
เหตุผลที่ทำ GPU kernel hedging ได้ยาก
- ยังมีการพิจารณาวิธีคล้าย request hedging ในระบบกระจาย คือรัน physics kernel หลายชุดแล้วใช้ผลลัพธ์จากชุดที่เสร็จก่อน
- วิธีนี้ช่วยลด tail latency และความแปรปรวนของ latency ได้ อีกทั้งยังช่วยสร้างภาระ GPU เพื่อกระตุ้นให้ OS ยก performance state ได้พร้อมกัน
- แต่สำหรับ Anukari มีปัญหาหลายด้าน
- หาก physics kernel ชุดหนึ่งใช้เวลานานกว่าหนึ่งรอบของบล็อกออดิโอ สตรีม kernel นั้นจะเริ่มตามไม่ทัน
- สตรีม kernel ที่ช้ากว่าจะต้องไล่ตามในบล็อกอนาคต และต้องมีการ fast-forward ด้วยการคัดลอกสถานะภายในจากสตรีมอื่น
- การคัดลอกสถานะภายในมีต้นทุนสูง
- สถานะภายในที่ใหญ่ที่สุดคือออดิโอบัฟเฟอร์สำหรับ delay line
- เก็บประวัติออดิโอย้อนหลัง 1 วินาทีต่อไมโครโฟน
- ขนาดคือ
48,000 samples * 50 mics * 2 channels * 16 voices * 4 bytesหรือ 307MB - หากใช้ sample rate สูงกว่านี้ก็จะยิ่งใหญ่ขึ้น
- หากจะทำให้มีประสิทธิภาพ ต้องติดตาม dirty region ของแต่ละ hedged kernel stream อย่างละเอียด และคัดลอกเฉพาะส่วนนั้น
- แต่การจัดวางหน่วยความจำของบัฟเฟอร์ถูกปรับแต่งมาเพื่อโหลดการอ่านของ physics kernel
- ต่อให้คัดลอกเฉพาะส่วนขั้นต่ำ ก็ยังต้องคัดลอกพื้นที่ที่กระจายอยู่ทั่วบัฟเฟอร์ จึงช้า
- การเปลี่ยนโมเดลโดยผู้ใช้ก็ต้องถูกส่งต่อไปยัง hedged kernel ทุกตัวด้วย
- physics kernel มี GPU footprint ใหญ่กว่า spin kernel ของ “waste makes haste” มาก
- hedging จะสร้างภาระ GPU ส่วนเกินมากขึ้น และอาจลดจำนวนอินสแตนซ์ของ Anukari ที่รันขนานกันได้
- hedge kernel เองก็อาจแย่งทรัพยากรกันจนช้าลงทั้งหมด
การปรับแต่งที่ทำไปแล้ว และเหตุผลที่ยังต้องใช้ GPU
- การจำลองของ Anukari เป็นงานแบบ ALU-bound จึงไม่มีพื้นที่ให้ปรับแต่งแนวทั่วไปอย่างการทำให้รูปแบบการเข้าถึงหน่วยความจำดีขึ้นมากนัก
- หากต้องการเพิ่มประสิทธิภาพ ต้องปรับ throughput เชิงคณิตศาสตร์
- ใช้การคำนวณ FP16 ในจุดที่ทำได้ เพื่อให้ ALU ของ Apple ทำงานได้เต็มมากขึ้น
- ใช้ micro-benchmark ปรับลำดับคำสั่ง
- เก็บสถานะฟิสิกส์ทั้งหมดไว้ใน L1 memory
- จัดลำดับการโหลดใหม่เพื่อช่วยการทำ vectorization
- ยังอาศัยข้อเท็จจริงที่ว่าเธรดใน Apple SIMD-group โดยมากใช้ instruction pointer ร่วมกัน
- วัตถุฟิสิกส์ต่างชนิดกันมักแยก branch path ออกจากกันมาก
- หากจำลองวัตถุสองประเภทใน SIMD-group เดียวกัน จะช้าลงเพราะ instruction masking
- เพื่อหลีกเลี่ยงสิ่งนี้ จึงมีการปรับ memory layout ของวัตถุฟิสิกส์แบบไดนามิก เพื่อลดจำนวนประเภทของวัตถุที่รันอยู่ใน SIMD-group เดียวกัน
- การปรับแต่งนี้อธิบายไว้อย่างละเอียดใน the new warp alignment optimizer
- ยังพอมีพื้นที่ให้ปรับแต่งเชิงคณิตศาสตร์เพิ่มเติม แต่คาดว่าจะได้เพียงระดับเลขหลักเดียวของเปอร์เซ็นต์พอยต์
- บนเครื่องที่แรง Anukari สามารถจำลองวัตถุฟิสิกส์ได้ 768 ถึง 1024 ชิ้น
- แต่ละชิ้นเชื่อมต่อกับชิ้นอื่นได้แบบอิสระ
- โดยทั่วไปวัตถุจะทำ implicit Euler integration ที่ sample rate เสียง 48,000 ตัวอย่างต่อวินาที
- แต่ละวัตถุมีพารามิเตอร์พฤติกรรม 3 ถึง 10 ตัว
- พฤติกรรมบางอย่างมีการคำนวณราคาแพง เช่น vector rotation,
exp(),log() - เพื่อรองรับ polyphony จะรันสำเนาการจำลองฟิสิกส์ทั้งชุดแบบขนานได้สูงสุด 16 ชุด
- วิธีนี้ไม่สามารถทำบน CPU ได้ และต้องพึ่ง ALU จำนวนมากของ GPU, การควบคุม layout ของ L1 cache และโครงสร้าง concurrency อย่าง
threadgroup_barrier - Anukari ไม่สามารถมีอยู่ได้หากไม่มีการประมวลผลบน GPU
เหตุผลที่ GPU Audio API ไม่ใช่คำตอบ
- Alexander Talashov ซีอีโอของ GPU Audio เคยบอกว่า Anukari อาจแก้ปัญหานี้ได้หากใช้ GPU Audio API
- ผู้พัฒนามองว่า GPU Audio เป็นผลิตภัณฑ์ที่ดี และเป็นผลิตภัณฑ์ที่ทำให้เข้าถึง GPU สำหรับ DSP ได้
- แต่ตัดสินว่า GPU Audio ไม่ได้มีประโยชน์กับ Anukari
- Anukari ไม่เหมือนแอปพลิเคชัน DSP แบบดั้งเดิม แต่ใกล้เคียงกับ ตัวอินทิเกรตสมการเชิงอนุพันธ์เชิงตัวเลข มากกว่า
- แม้จะมีส่วนที่เป็น DSP อยู่บ้าง แต่การคำนวณส่วนใหญ่คือ Eulerian integration
- DSP อย่างการบีบอัดสัญญาณไมโครโฟนในโลกฟิสิกส์ จะถูกประมวลผลแบบ inline ภายในงานคำนวณฟิสิกส์บน GPU
- Anukari เขียนโปรแกรม GPU โดยตรงที่ระดับล่างของ Metal
- สิ่งที่ต้องการคือ Apple ทำให้ความเร็วสัญญาณนาฬิกาของ GPU ยกขึ้นอย่างเสถียร
1 ความคิดเห็น
ความคิดเห็นบน Hacker News
บางคนน่าจะเคยเห็น Anukari จากโพสต์ Show HN ของผม: https://news.ycombinator.com/item?id=43873074
ในเธรดนั้นมีการพูดถึง ประสิทธิภาพบน macOS อยู่ Anukari ทำงานได้ดีบน Apple silicon ส่วนใหญ่ รวมถึง M1 รุ่นพื้นฐาน และการทดสอบทั้งหมดของผมก็ทำบน M1 รุ่นพื้นฐาน ซึ่งผลออกมายอดเยี่ยมมาก ฮาร์ดแวร์นี่สุดยอดจริง ๆ
แต่เพื่อให้มันทำงานได้ ผมต้องทำ workaround แปลก ๆ เพื่อบังคับให้ macOS เพิ่ม ความเร็วสัญญาณนาฬิกาของ GPU ให้การประมวลผลเสียงเร็วพอ heuristic ทั่วไปที่ macOS ใช้กำหนดสถานะประสิทธิภาพของ GPU ไม่เข้าใจ workload แปลก ๆ ของ Anukari
สุดท้ายผมเลยเขียนสรุปสถานการณ์ทั้งหมดแบบละเอียดเกินไปหน่อย และอยากขอความช่วยเหลือให้ได้ติดต่อกับคนที่เหมาะสมใน Apple ซึ่งน่าจะเป็นคนที่ดูแลฝั่ง Metal API ช่วยด้วยครับ :)
ถึงจะบอกว่าเป็น “บทความที่ยาวมากและเทคนิคมาก” แต่พออ่านจนจบแล้วก็ไม่ได้ยาวเกินไป แถมเป็น บทความที่ชัดเจนมากและเขียนดีมาก และมีประโยชน์ด้วย เขียนได้ดีจริง ๆ
ผมไม่เคยมี Mac และ PC ของผมก็เก่าแล้วจนไม่มี GPU ที่ดีพอ เลยคงยังไม่มีโอกาสลอง Anukari เร็ว ๆ นี้ แต่มันดูเจ๋งมาก เลยเสียดาย หวังว่าจะแก้ปัญหาได้เร็ว ๆ
สงสัยว่าเคยลอง entitlement นี้หรือยัง: https://developer.apple.com/documentation/bundleresources/en...
อยากรู้ว่า
com.apple.developer.sustained-executionทำงานในทิศทางตรงข้ามด้วยหรือเปล่าเป็นบทความที่น่าสนใจ และปัญหาก็น่าสนใจด้วย ผมคิดว่าสาเหตุที่ไอเดียรันงานในคิวเดียวกันล้มเหลว สุดท้ายก็น่าจะเป็นเหตุผลเดียวกับปัญหาเดิมหรือเปล่า เพราะ ความเร็วสัญญาณนาฬิกาที่เปลี่ยนแปลงได้ ทำให้การจัดตารางเวลาแบบแม่นยำเป็นไปไม่ได้ และขึ้นอยู่กับว่าระบบปฏิบัติการตั้ง clock ของ GPU ไว้อย่างไร จุดหยุด spin ก็จะคลาดจากจุดที่เหมาะสมจนเกิด aliasing
ถ้าอย่างนั้น งาน spin อาจจะยังซับซ้อนไม่พอที่จะดัน GPU ไปถึง clock สูงสุดก็ได้ ถ้ามันทำงานที่ประสิทธิภาพสูงสุดจริง ๆ จุดสิ้นสุดของ spin ก็น่าจะกำหนดได้อย่างเสถียรโดยไม่ต้องเพิ่ม software PLL ผมไม่เห็นคำอธิบายละเอียดว่า spin ถูก implement อย่างไร แต่ spin loop ที่สมจริงกว่าและกดดันส่วนต่าง ๆ ของ GPU มากขึ้นอย่างต่อเนื่อง น่าจะมีประสิทธิภาพกว่าในการรักษา clock ให้อยู่ที่ประสิทธิภาพสูงสุด
ผมพลาด Show HN ไป แต่พอเห็นก็คิดทันทีว่าน่าจะเหมาะมากกับ ซาวด์สเคป ASMR เชิงสร้างสรรค์ และเสียงหลายมิติแบบดื่มด่ำ หวังว่าคุณหรือผู้ใช้สักคนจะทำเดโมให้ดู ยินดีกับโปรเจกต์ และขอให้ได้รับความช่วยเหลือเรื่อง Apple ด้วย
บทความดีและอธิบายชัดเจน ทำให้เข้าใจง่าย ผมแน่ใจว่าเคยเจอปัญหาแบบเดียวกับที่อธิบายในบริบทอื่นมาแล้ว
ทุกคน มันได้ผลแล้ว ผมได้คุยอย่างมีประสิทธิผลมากกับ คนที่ตรงจุดในทีม Metal! ขอบคุณที่ช่วยให้ Apple หันมาสนใจ ไม่คาดคิดเลยว่าจะได้รับการสนับสนุนมากขนาดนี้
https://anukari.com/blog/devlog/productive-conversation-appl...
ดีที่ตอนนี้มี workaround แล้ว แต่ก็น่าขันที่แม้แต่ว่า workaround นั้นคืออะไรก็ยังแชร์ไม่ได้ ซึ่งสะท้อนประโยคสุดท้ายของ https://news.ycombinator.com/item?id=43904921 เรื่องวิธีสื่อสารของ Apple ได้ตรงตัว
ประมาณว่า “ตั้งค่านี้แบบนี้แล้วเปลี่ยนเป็นแบบนั้น แล้วมันจะทำงาน ถึงจะไม่มีเอกสาร แต่ตอนนี้คุณก็รู้แล้วนะ”
ตอน implement workaround หวังว่าจะใส่มันไว้ในฟังก์ชันที่ตั้งชื่อแบบชัดโจ้งได้ เพื่อให้คนอื่นที่เจอข้อจำกัดของ GPU ที่ไวต่อ latency คล้าย ๆ กัน ยังพอหาคำใบ้ของเวทมนตร์นี้จาก disassembly ได้
HN ทำหน้าที่ดั้งเดิมของมันสำเร็จอีกครั้ง นั่นคือการเจาะผ่าน กำแพงราชการ ที่ขวางอยู่หน้าฝ่าย support ของบริษัทใหญ่
ยินดีกับโปรเจกต์ และขอให้โชคดี
ผมเคยทำงานที่บริษัทดังสองแห่งซึ่งมีแอปที่โด่งดังมากบน Apple App Store
ทีม Apple ที่เราคุยด้วยไม่สนใจปัญหาของเราเลย แต่กลับชวนเราไปที่ออฟฟิศบ่อย ๆ เพื่อคุยเรื่องฟีเจอร์ใหม่ล่าสุดที่จะประกาศใน WWDC และแทบจะบังคับให้เรารองรับฟีเจอร์นั้น นั่นคือจุดเริ่มต้นและจุดจบของความสัมพันธ์กับพวกเขา ถ้าอยากหาว่าทำไมซอฟต์แวร์ Apple ที่บั๊กเยอะถึงไม่ทำงาน ก็ต้องเขียน ตั๋วซัพพอร์ตทางเทคนิค เท่านั้น
คนฝ่าย developer relations ของ Apple ไม่ใช่คนที่จริงจัง
อย่างที่โพสต์ต้นทางด้านบนแสดงให้เห็น ดีใจที่ประสบการณ์ของผมไม่ใช่กฎทั่วไป แต่ราว 10 ปีก่อน ตอนผมทำงานที่บริษัทที่มีแอปค่อนข้างดัง อัปเดตหนึ่งทำให้ประสิทธิภาพของแอปพังยับ
ในเวลาเดียวกันพอดี คู่แข่งปล่อยแอปที่ไม่มีปัญหาประสิทธิภาพออกมา ปรากฏว่านักพัฒนาแอปคู่แข่งคนนั้นเพิ่งออกจาก Apple ไม่นาน และได้ทิ้งกับดักที่ไม่มีเอกสารไว้ใน ไดรเวอร์วิดีโอ ของ Apple จนแอปของเราพัง เราต้อง disassemble ไบนารีของคู่แข่งถึงจะพบการเปลี่ยนแปลงที่ไม่มีเอกสารและแก้แอปได้ นักพัฒนาคนนั้นยังส่งอีเมลมาเยาะเย้ย CEO ของเราด้วย โลกช่างยอดเยี่ยมจริง ๆ
Metal profiler มีฟีเจอร์ที่มีประโยชน์มาก ซึ่งให้เลือก สถานะประสิทธิภาพ ของ Metal ระหว่าง profiling แอปพลิเคชันได้ แต่ตั้งค่านอก profiler ไม่ได้
ดูจากตรงนี้น่าจะมี private API อยู่ การ reverse engineer อาจจะง่ายกว่าหรือเปล่า? แน่นอน เว้นแต่ว่ามันต้องใช้สิทธิพิเศษบางอย่างที่เลี่ยงไม่ได้หากไม่ปิด SIP
เรื่องนี้ต้องมี private API แน่นอน ในบทความก็เขียนแบบนี้
“Metal profiler มีฟีเจอร์ที่มีประโยชน์มาก ซึ่งให้เลือก ‘Performance State’ ของ Metal ระหว่าง profiling แอปพลิเคชันได้ แต่ตั้งค่านอก profiler ไม่ได้”
ถ้าไม่ใช่ private API แล้ว Metal profiler จะทำแบบนั้นได้อย่างไร? อาจใช้เครื่องมือ debugging บางอย่างสังเกต profiler เพื่อดูว่าข้างในเกิดอะไรขึ้นได้ไหม?
ปัญหาของการเปิด API นี้ให้ใช้คือ นักพัฒนาจำนวนมากเกินไปจะบังคับเปิด สถานะประสิทธิภาพสูงสุด ไว้ตลอดเวลา ไม่แน่ใจจริง ๆ ว่ามีวิธีที่ดีในการป้องกันเรื่องนั้นพร้อมกับให้ API ไปด้วยหรือไม่
บนอุปกรณ์ที่ใช้แบตเตอรี่ แค่แอปเดียวก็มีวิธีสิ้นเปลืองพลังงานได้ไม่รู้จบอยู่แล้ว สุดท้ายโครงสร้างก็เป็นแบบที่ต้องเชื่อใจนักพัฒนาอยู่แล้วว่า จะไม่รันงานที่กินพลังงานสูงโดยไม่จำเป็น ไม่ว่าจะตั้งใจหรือเผลอทำก็ตาม การมี API เพิ่มมาอีกหนึ่งตัวที่ถ้าใช้ไม่เหมาะสมก็อาจสิ้นเปลืองพลังงานได้ จึงไม่ได้ทำให้ต่างไปมากนัก
บทความยังพูดถึง โหมดเกม ด้วย ซึ่งเป็นฟีเจอร์ในระบบปฏิบัติการ Apple รุ่นใหม่ ๆ ที่ปรับแต่งมาสำหรับกรณีแบบนี้ เมื่อเปิดโหมดเกมจะมีการแจ้งเตือนเด้งขึ้นมา และแอปพลิเคชันส่วนใหญ่น่าจะไม่อยากให้เป็นแบบนั้น จนถึงตอนนี้ยังไม่เคยเห็นกรณีที่นำไปใช้ในทางที่ผิด
นักพัฒนายังไม่ได้เอา audio workgroup ไปใช้ผิด ๆ กับ thread pool ทุกตัวเพื่อให้ได้การจัดคิวบน P-core และลำดับความสำคัญสูง ถ้าอย่างนั้นก็ชี้ให้เห็นได้ว่า เมื่อ audio workgroup ส่งคำสั่งไปยัง GPU อาจตั้ง timeout บางอย่างสำหรับการ downclock ของ GPU โดยอิงจากเวลาล่าสุดที่ workgroup ส่งข้อมูลไปได้
GPU audio เป็นสายเฉพาะทางมากในยุคนี้ แต่บริษัทที่กล่าวถึงในบทความเพิ่งเปิด SDK ไปเมื่อไม่นานมานี้ ก็อาจทำให้แพร่หลายมากขึ้นได้ ถึงอย่างนั้นก็ยังไม่ค่อยน่าเชื่อเท่าไร การนำไปประมวลผลบน GPU แทบจะหมายความว่าไม่สนใจ latency แล้ว ดังนั้นผมคิดว่าแค่เพิ่มขนาดบัฟเฟอร์ I/O ก็พอ
ต่อให้มีการใช้ API ในทางที่ผิด ก็ยังน่าจะมีประสิทธิภาพกว่าการรัน งานยุ่งปลอม ๆ เพื่อทำสิ่งเดียวกัน แอปต่าง ๆ ทำแบบนั้นได้อยู่แล้วแม้ไม่มี API หรือไม่มีสิทธิ์ที่ API อาจกำหนดให้ต้องมี
แล้วการให้สิทธิ์แบบ manual ล่ะ? ต่อให้ซ่อนไว้ที่ไหนสักแห่ง ก็มีโอกาสสูงที่จะจำเป็นสำหรับแอปเฉพาะทางมาก ๆ
แล้วในระดับระบบปฏิบัติการ ก็ให้ Zoom, Teams, เว็บเบราว์เซอร์ถูกปฏิเสธเป็นค่าเริ่มต้นไปเลย :)
วิธีที่ดีที่สุดในการทำเรื่องนี้:
ไล่ดูวิดีโอ WWDC แล้วหา engineer ที่ดูเหมือนเข้าใจปัญหาที่กำลังเจออยู่ดีที่สุด
ถ้าเป็น
Michael Thomsonก็ส่งอีเมลตรงไปในรูปแบบอย่างmthomson@apple.compthomsonนอกเรื่องนิด แต่ Anukari น่าจะออก Mick Gordon sound pack แล้วแบ่งรายได้กับเขา คนคนนั้นกำลังทำอะไรที่สุดโต่งจริง ๆ และเดโมก็สุดยอดมาก ถ้ามีเครื่องมือที่ทรงพลังขนาดนี้แล้ว การร่วมงานกับศิลปินถือเป็นธุรกิจที่ดีและเป็นเรื่องดีต่อโลกด้วย ถ้าชอบ Mick Gordon น่ะนะ ซึ่งผมชอบ
ผมไม่ได้ต้องการแอปนี้เลยสักนิด แต่มันเจ๋งจริง ๆ แอปแบบนี้นำ ความสนุก กลับมาสู่การคอมพิวติง ไม่ได้หมายความว่าตอนนี้ไม่มีความสนุกเลยนะ แต่มันทำให้นึกถึงสมัยก่อนที่มีโปรแกรมเชิงกราฟิกและเชิงทดลองลอยไปลอยมาเยอะกว่าเดี๋ยวนี้ กระทั่ง demoscene ด้วยซ้ำ
อย่าพลาดลิงก์ https://x.com/Mick_Gordon/status/1918146487948919222 ที่อยู่ในย่อหน้ารองสุดท้าย เป็นเดโมที่ Mick Gordon ทำขึ้น และ @anukarimusic ตอบไว้แบบนี้
“ฮ่า ๆ เพิ่งวันที่สองหลังเปิดตัวเอง แต่คุณทำลายเดโมทุกตัวที่ผมใช้เวลาทำทุกวันตลอด 2 ปีจนราบคาบไปแล้ว”
การอัปเดต วัตถุ 1024 ตัว ที่ 48kHz ดูเหมือนจะทำบน CPU ได้เหมือนกัน ขึ้นอยู่กับวิธีเขียนโค้ด ไม่ใช่เท่ากับอัปเดต 48 ล้านครั้งต่อวินาทีเหรอ? ดูเหมาะกับการใช้ OpenMP รันลูปหลาย ๆ ตัวแบบขนานบนหลายคอร์
Anukari รันสำเนาโมเดลฟิสิกส์ทั้งหมดได้สูงสุด 16 ชุดเพื่อรองรับ polyphony ดังนั้นคือ
16 * 1024 * 48Kคงต้องอัปเดตบทความบล็อกแล้วผู้ใช้สามารถเชื่อมวัตถุเข้าหากันได้อย่างอิสระ ดังนั้นแต่ละวัตถุต้องอ่านและประมวลผลการเชื่อมต่อไปยังเอนทิตีอื่น ๆ จำนวน N ตัว
ถ้าจะใช้ CPU ทั้งหมด ทุกขั้นตอนทางฟิสิกส์ต้องมีการซิงโครไนซ์ข้ามคอร์ และสิ่งนี้ช้า
ปริมาณการประมวลผลต่อวัตถุค่อนข้างมาก มีฟังก์ชันทรานเซนเดนทัลจำนวนมาก แม้จะประมาณค่าได้ก็ตาม แต่ตัวฟีเจอร์เองก็เยอะด้วย พารามิเตอร์ทั้งหมดสามารถถูกมอดูเลตได้ ต้องปลอดภัยต่อ NaN และมีเรื่องให้คำนึงถึงอีกมาก
ผู้ใช้อยากรัน Anukari หลายตัวพร้อมกันแบบขนาน สำหรับหลายแทร็ก เอฟเฟกต์ และอื่น ๆ
ถ้ามองอีกแบบคือ
4 GHz / (16 voice * 1024 obj * 4 connections * 48,000 sample) = 1.3 cycles per thingGPU จัดการ workload นี้ได้ในพริบตา โครงสร้างมันเหมาะอย่างสมบูรณ์แบบ สามารถประมวลผล
16 voice * 1024 objทั้งหมดแบบขนานได้เต็มที่ การซิงโครไนซ์ในแต่ละขั้นตอนก็เรียบง่าย และผู้ใช้สามารถจัดการ L1 cache ได้ถ้าคำนวณถูก จะได้ 83 clock cycles ในการคำนวณหนึ่ง sample ถ้าเป็น 16 คอร์ ตามทฤษฎีก็คือ 1333 cycles ซึ่งไม่ได้มากนัก ยิ่งคิดว่าคงใช้ CPU เกือบ 100% ตลอดเวลาไม่ได้ด้วยแล้วก็ยิ่งใช่