- ในระหว่างการ ย้ายฐานโค้ดจาก Scala 2.13 ไป Scala 3 เกิดการลดประสิทธิภาพที่ไม่คาดคิด
- ในสภาพแวดล้อมการทดสอบเริ่มต้นและการ rollout ตัวชี้วัด ทั้งหมดอยู่ในเกณฑ์ปกติ แต่หลังผ่านไปไม่กี่ชั่วโมง Kafka lag เพิ่มขึ้น
- จากผลการทดสอบภายใต้ภาระพบว่าเกิดอัตราการประมวลผลที่ลดลงแบบฮวบฮวบเมื่อมีการประมวลผลข้อความแบบละเอียด
- จากการวิเคราะห์ด้วย async-profiler เปิดเผยว่าเป็นข้อบกพร่องของ การประเมิน chain ที่ไม่มีประสิทธิภาพ ในไลบรารี Quicklens
- หลังจากอัปเดตไลบรารีแล้วประสิทธิภาพกลับคืนสู่ปกติ และเน้นย้ำถึงความสำคัญของการระวังความแตกต่างของการทำงานของไลบรารีระหว่างเวอร์ชัน Scala
กระบวนการย้ายระบบบริการ
- บริการเดิมได้ย้ายจาก Scala 2.13 ไป Scala 3.7.3
- เป็นบริการที่เน้นการเก็บข้อมูลและไม่ใช้ macro โดยไม่ต้องการความสำคัญด้านประสิทธิภาพสูงมาก
- หลังปรับ dependency, ตัวเลือกคอมไพเลอร์, ชนิดข้อมูล และไวยากรณ์แล้วจึงคอมไพล์ผ่านสำเร็จ
- ในสภาพแวดล้อมทดสอบและการ rollout แบบค่อยเป็นค่อยไป ทั้ง log และ metric ถูกมองเห็นปกติ
- ตัวชี้วัดระดับ infrastructure, JVM และแอปพลิเคชันทั้งหมดถูกตรวจพบว่ามีสุขภาพที่ดี
ภาวะลดประสิทธิภาพที่ไม่ทราบสาเหตุ
- หลังการ deploy ประมาณ 5~6 ชั่วโมง เกิดอาการ Kafka lag เพิ่มขึ้น
- อัตราการประมวลผลต่อ instance ลดลงแม้ไม่มีสภาวะ data spike
- หลัง rollback อัตราการประมวลผลฟื้นตัวกลับมาได้ทันที จึงยืนยันได้ว่าการเปลี่ยนโค้ดเป็นต้นเหตุ
วิเคราะห์ประสิทธิภาพและติดตามหาสาเหตุ
- ในการทดสอบโหลดไม่เกิดการถดถอยของประสิทธิภาพซ้ำได้ในระยะแรก
- เกิดการลดลงของอัตราการประมวลผลอย่างรุนแรงเฉพาะในกรณีที่มีการทำการแบ่งย่อยและ payload ไม่สม่ำเสมอ
- ทดสอบย้อนกลับ dependency library (เช่น serialization, DB SDK, Docker image, library สำหรับ configuration ฯลฯ) ทีละตัว แต่ไม่พบความเปลี่ยนแปลงใด
- หลังจากทำ CPU profiling ด้วย async-profiler
- ใน Scala 3 พบว่า การใช้งาน CPU ของ JIT compiler และขั้นตอนการ decode เพิ่มขึ้นอย่างมาก
- ในหน้า Flamegraph โค้ดส่วนบนสุด แสดงให้เห็นว่า การเรียก Quicklens ใช้เวลาการทำงานของ CPU ครึ่งหนึ่ง
- ใน Scala 2.13 การเรียกแบบเดียวกันนี้มีเพียงระดับ 0.5% เท่านั้น
สาเหตุรากฐานของปัญหา
- ใน Scala 3 เกิด บั๊กการประเมิน chain ที่ไม่ประสิทธิผล ของไลบรารี Quicklens
- การแก้ไขที่เกี่ยวข้องถูกผนวกไว้ใน GitHub PR #115
- หลังอัปเดตไลบรารีแล้วความต่างด้านประสิทธิภาพระหว่าง Scala 3 และ 2.13 ก็ถูกยกเลิก
บทเรียนและคำแนะนำ
- การพึ่งพา metaprogramming ของไลบรารี อาจทำให้เกิดความต่างด้านประสิทธิภาพข้ามเวอร์ชัน Scala
- แม้การย้ายระบบเสร็จสิ้นแล้วก็ควร benchmark hotspot และจุดคอขวด
- สำหรับบริการที่เน้นประสิทธิภาพ การยืนยันแบบวัดผลจริงเป็นสิ่งจำเป็น แทนการตั้งสมมติฐานว่า 'ทำงานได้ตามปกติ'
- จำเป็นต้องมีการตรวจสอบล่วงหน้าเพื่อป้องกันสถานการณ์ที่ benchmark เป็นตัวเปิดเผยจุดคอขวดแทนการพิจารณาจากโค้ดอย่างเดียว
1 ความคิดเห็น
ความคิดเห็นใน Hacker News
บล็อกเทคนิคควรเขียนแบบนี้แหละ AI คงยากที่จะมาแทนที่กระบวนการคิดระดับนี้ได้
คำถามแรกนั้นง่ายมาก — “ทำไมต้องอัปเกรดด้วย?”
inlineทำงานเป็นส่วนหนึ่งของระบบ macroถ้าใส่
inlineกับพารามิเตอร์ ก็เป็นการสั่งคอมไพเลอร์ให้อินไลน์ expression ณ จุดที่เรียกใช้แต่ถ้ามันใหญ่ ก็จะสร้างภาระหนักให้กับ JIT compiler
ใน Scala 2 นั้น
@inlineเป็นแค่คำแนะนำ แต่ใน 3 จะถูกบังคับใช้เสมอดังนั้นการเปลี่ยน
@inlineเป็นinlineแบบตรง ๆ จึงเป็นความผิดพลาดครั้งใหญ่registerใน C/C++ เคยเจอช่วงแรกมันมีผลแบบบังคับ แต่เมื่อการทำ optimization พัฒนาขึ้น มันก็กลายเป็นแค่คำแนะนำและสุดท้ายก็ถูกละเลย
inlineของ C++ ก็ผ่านกระบวนการคล้ายกันinlineอย่างจริงจังแทบทุกที่เพื่อตัด overhead ของ lambdaในฟังก์ชันอย่าง
mapแทบไม่ค่อยมีปัญหาด้านประสิทธิภาพ แต่พอรวมกับระบบ macro ของ Scala ก็อาจเกิด expression ที่ซับซ้อนและกลายเป็นปัญหาได้
ตอนอัปเกรด Ruby 2→3 ก็เคยมีประสบการณ์คล้ายกัน
ไม่ใช่แค่อัปเกรดภาษาอย่างเดียว แต่ต้องอัปเดต dependency ทั้งชุดให้ใหม่ด้วยถึงจะทำให้ระบบเสถียร
ปัญหา type inference ของ Scala 2 ก็ยังไม่ได้รับการแก้ไข แต่กลับเปลี่ยนภาษาแทน
เหมือนสร้างผลิตภัณฑ์ที่ไม่มีใครอยากได้โดยไม่สนความต้องการของตลาด
PS: ควรสร้าง unit test suite จริงจังให้คอมไพเลอร์
แต่การ rewrite Scala 3 ก็ไม่สามารถแก้ปัญหาความเร็วคอมไพล์และ toolingได้ และทำให้โปรเจกต์สูญเสียโมเมนตัมไปหมด
ตอนนี้ในปี 2025 จะยังมีใครเริ่มโปรเจกต์ใหม่ด้วย Scala ไหมก็น่าสงสัย
Scala ดูเหมือนเป็นภาษาที่นักวิชาการสร้างขึ้น และที่มันเคยฮิตในอุตสาหกรรมอยู่พักหนึ่งกลับดูแปลกเสียด้วยซ้ำ
ตอนนี้ทุกเครื่องมือต้องปรับให้เข้ากับ Scala 3 และแม้แต่ IntelliJ ก็ยังรองรับได้ไม่สมบูรณ์
ถ้าค่อย ๆ ปรับปรุง Scala 2 ไปทีละนิดก็คงดี แต่ดูเหมือนจะโฟกัสแต่ความสำเร็จเชิงวิชาการ
เช่นเรื่อง
early returnที่ยังถกเถียงกันมากในบทความของ tpolecat แต่ Kotlin รองรับได้แบบไม่มีปัญหาคอมไพเลอร์ Scala มีเทสต์อยู่เป็นหลักพันหลักหมื่น และ
ไดเรกทอรีทดสอบทางการ กับ
ระบบ community build ใช้ตรวจสอบโค้ดนับล้าน LOC
โดยเฉพาะกับการเปลี่ยนใหญ่ ๆ อย่างการอัปเกรดเวอร์ชันภาษา ถือว่าจำเป็นมาก
เรา benchmark เครื่องมือที่เขียนด้วย C++ อย่างต่อเนื่อง แต่เพราะnoise จากสภาพแวดล้อมจึงทำให้ได้ผลลัพธ์ที่สม่ำเสมอยาก
ตอนนี้กำลังคิดเรื่องรันหลายรอบบนเครื่องเดียวกันแล้วค่อยเปรียบเทียบ
การสร้าง syntax ชุดที่สองแล้วผลักดันว่ามันคืออนาคตคือปัญหา
สิ่งนี้ยังทำให้ ecosystem ด้าน tooling ช้าลงด้วย
จะใช้คอมไพเลอร์หรือ scalafmt แปลงสไตล์อัตโนมัติก็ได้
ตอนนี้เลยมีทั้ง syntax แบบวงเล็บปีกกาและแบบย่อหน้าซ้อนเพิ่มเป็นสองเท่า
โครงสร้าง
matchดูยืดยาวเกินไปและเหมือนเลียนแบบ Pythonตอนนั้น Scala ได้รับความสนใจเพราะ Spark แต่ก็พลาดโอกาสในการเติบโตเป็นภาษาสำหรับเชิงพาณิชย์
ตอนนี้ Spark ไปอยู่กับ Python แล้ว และตำแหน่งภาษาสมัยใหม่บน JVM ก็ถูก Kotlin ยึดไป
สุดท้าย Scala เลยให้ความรู้สึกเหมือนกลับไปเป็นภาษาเชิงวิชาการอีกครั้ง
ดูได้จาก Scala Adoption Tracker
ความสามารถใหม่ ๆ ของ Scala 3 ยังมีศักยภาพในการสร้างนวัตกรรมให้ ecosystem ของภาษาได้อีก
เช่น: คำอธิบาย Capture Checking
Java เองก็เพิ่มความสามารถเชิง functional เข้ามา ทำให้ดึงเสน่ห์บางส่วนของ Scala ไป
จากประสบการณ์ของฉันเองความต้องการในตลาดก็ต่ำมาก
เป็นแบบนั้นเพราะ Google จำกัดการรองรับ Java
ในตลาด JVM ทั้งหมดมันมีส่วนแบ่งแค่ราว 10% เท่านั้น
ถ้าต้องผ่านการตรวจสอบความปลอดภัยอย่าง PIC-DSS เป็นต้น การคงไลบรารีให้ทันสมัยถือเป็นสิ่งจำเป็น
ฉันกลับเป็นคนที่ชอบคง dependency ให้อยู่เวอร์ชันเก่ามากกว่า
เวอร์ชันใหม่ก็มาพร้อมบั๊กใหม่ การเปลี่ยนผู้ดูแล หรือความเสี่ยงด้านความปลอดภัยได้เหมือนกัน
ดูเหมือนตอนแรกจะอัปแค่บางส่วน การแบ่งทำทีละขั้นเล็ก ๆ เป็นเรื่องปกติ แต่ครั้งนี้คงโชคไม่ดี
ปัญหาไม่ได้อยู่ที่ Scala 3 เอง แต่เป็นปฏิสัมพันธ์ของหลายปัจจัย
แต่ไลบรารีเฉพาะทาง Scala มักใส่เวอร์ชัน Scala ไว้ในเวอร์ชันด้วย จึงต้องระวัง
แสดงให้เห็นถึงความ expressive ของ Scala และtype safetyที่โดดเด่น
เหมือนในบทความของ Li Haoyi ที่ชี้ว่ามันก็น่าสนใจพอจะเป็นภาษาทดแทน Python ได้
ยิ่งมีไลบรารีที่ใช้ฟีเจอร์แนว magic มากเท่าไร ก็ยิ่งสำคัญ