4 คะแนน โดย GN⁺ 2025-12-09 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • ในระหว่างการ ย้ายฐานโค้ดจาก 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 ความคิดเห็น

 
GN⁺ 2025-12-09
ความคิดเห็นใน Hacker News
  • ฉันไม่ได้เป็นแฟน Scala แต่ประทับใจกับกระบวนการวิเคราะห์เชิงลึกและดีบักปัญหา
    บล็อกเทคนิคควรเขียนแบบนี้แหละ AI คงยากที่จะมาแทนที่กระบวนการคิดระดับนี้ได้
    • ตอนรีเฟรชหนึ่งในบริการของเรา เราได้ย้ายจาก Scala 2.13 ไป Scala 3
      คำถามแรกนั้นง่ายมาก — “ทำไมต้องอัปเกรดด้วย?”
  • ใน Scala 3 คีย์เวิร์ด inline ทำงานเป็นส่วนหนึ่งของระบบ macro
    ถ้าใส่ inline กับพารามิเตอร์ ก็เป็นการสั่งคอมไพเลอร์ให้อินไลน์ expression ณ จุดที่เรียกใช้
    แต่ถ้ามันใหญ่ ก็จะสร้างภาระหนักให้กับ JIT compiler
    ใน Scala 2 นั้น @inline เป็นแค่คำแนะนำ แต่ใน 3 จะถูกบังคับใช้เสมอ
    ดังนั้นการเปลี่ยน @inline เป็น inline แบบตรง ๆ จึงเป็นความผิดพลาดครั้งใหญ่
    • ความต่างนี้คล้ายกับสิ่งที่คีย์เวิร์ด register ใน C/C++ เคยเจอ
      ช่วงแรกมันมีผลแบบบังคับ แต่เมื่อการทำ optimization พัฒนาขึ้น มันก็กลายเป็นแค่คำแนะนำและสุดท้ายก็ถูกละเลย
      inline ของ C++ ก็ผ่านกระบวนการคล้ายกัน
    • Kotlin ใช้ inline อย่างจริงจังแทบทุกที่
      เพื่อตัด overhead ของ lambdaในฟังก์ชันอย่าง map
      แทบไม่ค่อยมีปัญหาด้านประสิทธิภาพ แต่พอรวมกับระบบ macro ของ Scala ก็อาจเกิด expression ที่ซับซ้อนและกลายเป็นปัญหาได้
  • พออัปเกรดไลบรารีแล้ว ประสิทธิภาพของ Scala 3 ก็กลับมาแทบเท่าเดิมกับ Scala 2.13
    ตอนอัปเกรด Ruby 2→3 ก็เคยมีประสบการณ์คล้ายกัน
    ไม่ใช่แค่อัปเกรดภาษาอย่างเดียว แต่ต้องอัปเดต dependency ทั้งชุดให้ใหม่ด้วยถึงจะทำให้ระบบเสถียร
  • ปัญหาของ Scala 3 คือไม่มีใครต้องการมัน
    ปัญหา type inference ของ Scala 2 ก็ยังไม่ได้รับการแก้ไข แต่กลับเปลี่ยนภาษาแทน
    เหมือนสร้างผลิตภัณฑ์ที่ไม่มีใครอยากได้โดยไม่สนความต้องการของตลาด
    PS: ควรสร้าง unit test suite จริงจังให้คอมไพเลอร์
    • น่าเศร้าแต่ก็เห็นด้วย Scala เคยดูมีอนาคตราวช่วงปี 2010~15
      แต่การ rewrite Scala 3 ก็ไม่สามารถแก้ปัญหาความเร็วคอมไพล์และ toolingได้ และทำให้โปรเจกต์สูญเสียโมเมนตัมไปหมด
      ตอนนี้ในปี 2025 จะยังมีใครเริ่มโปรเจกต์ใหม่ด้วย Scala ไหมก็น่าสงสัย
    • ฉันพยายามเรียน Scala หลายครั้ง แต่สุดท้ายก็กลับไปหา Clojure
      Scala ดูเหมือนเป็นภาษาที่นักวิชาการสร้างขึ้น และที่มันเคยฮิตในอุตสาหกรรมอยู่พักหนึ่งกลับดูแปลกเสียด้วยซ้ำ
    • จับประเด็นสำคัญได้ดี
      ตอนนี้ทุกเครื่องมือต้องปรับให้เข้ากับ Scala 3 และแม้แต่ IntelliJ ก็ยังรองรับได้ไม่สมบูรณ์
      ถ้าค่อย ๆ ปรับปรุง Scala 2 ไปทีละนิดก็คงดี แต่ดูเหมือนจะโฟกัสแต่ความสำเร็จเชิงวิชาการ
      เช่นเรื่อง early return ที่ยังถกเถียงกันมากในบทความของ tpolecat แต่ Kotlin รองรับได้แบบไม่มีปัญหา
    • คำว่า “ไม่มีเทสต์” เป็นข้อมูลเท็จ
      คอมไพเลอร์ Scala มีเทสต์อยู่เป็นหลักพันหลักหมื่น และ
      ไดเรกทอรีทดสอบทางการ กับ
      ระบบ community build ใช้ตรวจสอบโค้ดนับล้าน LOC
    • ดูเหมือนจะอ่านบทความมาไม่ดี ประเด็นเลยหลุดไปหมด
  • สิ่งที่น่าสนใจที่สุดในบทความนี้คือ ต้องมีทั้งการทดสอบประสิทธิภาพอัตโนมัติและการวิเคราะห์ flamegraphเป็นพื้นฐาน
    โดยเฉพาะกับการเปลี่ยนใหญ่ ๆ อย่างการอัปเกรดเวอร์ชันภาษา ถือว่าจำเป็นมาก
    • เทสต์ benchmark ต่างจากเทสต์ทั่วไปตรงที่ต้องตั้งค่าอย่างละเอียด
      เรา benchmark เครื่องมือที่เขียนด้วย C++ อย่างต่อเนื่อง แต่เพราะnoise จากสภาพแวดล้อมจึงทำให้ได้ผลลัพธ์ที่สม่ำเสมอยาก
      ตอนนี้กำลังคิดเรื่องรันหลายรอบบนเครื่องเดียวกันแล้วค่อยเปรียบเทียบ
    • สงสัยว่าช่วงนี้บน JVM เขาใช้เครื่องมือทดสอบประสิทธิภาพอะไรบ้าง
  • ปัญหาเดียวของ Scala 3 คือความอิจฉา Python
    การสร้าง syntax ชุดที่สองแล้วผลักดันว่ามันคืออนาคตคือปัญหา
    สิ่งนี้ยังทำให้ ecosystem ด้าน tooling ช้าลงด้วย
    • ตอนนี้เครื่องมือส่วนใหญ่รองรับ syntax ใหม่แล้ว
      จะใช้คอมไพเลอร์หรือ scalafmt แปลงสไตล์อัตโนมัติก็ได้
    • สมเป็น Scala ที่ทำสิ่งเดียวกันได้หลายแบบ
      ตอนนี้เลยมีทั้ง syntax แบบวงเล็บปีกกาและแบบย่อหน้าซ้อนเพิ่มเป็นสองเท่า
    • ถ้าเทียบกับ Haskell น่าจะดูน่าสนใจกว่า
    • ในฐานะแฟน Scala สมัยก่อน ฉันรู้สึกงงมากเมื่อเห็นตัวอย่าง syntax ใหม่
      โครงสร้าง match ดูยืดยาวเกินไปและเหมือนเลียนแบบ Python
  • ฉันเคยย้าย Scala 2.x มาก่อน และสิ่งที่ยากที่สุดคือการรอ dependency
    ตอนนั้น Scala ได้รับความสนใจเพราะ Spark แต่ก็พลาดโอกาสในการเติบโตเป็นภาษาสำหรับเชิงพาณิชย์
    ตอนนี้ Spark ไปอยู่กับ Python แล้ว และตำแหน่งภาษาสมัยใหม่บน JVM ก็ถูก Kotlin ยึดไป
    สุดท้าย Scala เลยให้ความรู้สึกเหมือนกลับไปเป็นภาษาเชิงวิชาการอีกครั้ง
    • แต่ Scala ก็ยังถูกใช้อย่างแพร่หลายในบริษัทใหญ่
      ดูได้จาก Scala Adoption Tracker
      ความสามารถใหม่ ๆ ของ Scala 3 ยังมีศักยภาพในการสร้างนวัตกรรมให้ ecosystem ของภาษาได้อีก
      เช่น: คำอธิบาย Capture Checking
    • ยังไม่แน่ใจว่า Kotlin ครอง JVM จริงไหม
      Java เองก็เพิ่มความสามารถเชิง functional เข้ามา ทำให้ดึงเสน่ห์บางส่วนของ Scala ไป
    • ฝั่ง server-side JVM แทบไม่ค่อยเห็น Kotlin มีตัวตน
      จากประสบการณ์ของฉันเองความต้องการในตลาดก็ต่ำมาก
    • Kotlin แทบจะเป็นภาษาสำหรับ Android โดยเฉพาะ
      เป็นแบบนั้นเพราะ Google จำกัดการรองรับ Java
      ในตลาด JVM ทั้งหมดมันมีส่วนแบ่งแค่ราว 10% เท่านั้น
  • รู้สึกแปลกที่ย้ายไป Scala 3 โดยไม่อัปเกรดเวอร์ชันไลบรารี
    ถ้าต้องผ่านการตรวจสอบความปลอดภัยอย่าง PIC-DSS เป็นต้น การคงไลบรารีให้ทันสมัยถือเป็นสิ่งจำเป็น
    • คำพูดที่ว่า “การตามเวอร์ชันล่าสุดเป็นนิสัยที่ดี” เป็นการปิดกั้นการถกเถียง
      ฉันกลับเป็นคนที่ชอบคง dependency ให้อยู่เวอร์ชันเก่ามากกว่า
      เวอร์ชันใหม่ก็มาพร้อมบั๊กใหม่ การเปลี่ยนผู้ดูแล หรือความเสี่ยงด้านความปลอดภัยได้เหมือนกัน
    • ฉันก็งงเหมือนกัน ในบทความบอกว่า “อัปเดต dependency แล้ว” แต่ต่อมากลับบอกว่า “หลังอัปเดตแล้วประสิทธิภาพเท่ากัน”
      ดูเหมือนตอนแรกจะอัปแค่บางส่วน การแบ่งทำทีละขั้นเล็ก ๆ เป็นเรื่องปกติ แต่ครั้งนี้คงโชคไม่ดี
    • พอรู้สาเหตุของบั๊กแล้วก็รู้สึกประทับใจน้อยลง
      ปัญหาไม่ได้อยู่ที่ Scala 3 เอง แต่เป็นปฏิสัมพันธ์ของหลายปัจจัย
    • ยิ่งเป็นการเปลี่ยนใหญ่แบบอัปเกรดเวอร์ชันภาษา ก็ยิ่งควรเปลี่ยนทีละอย่าง
    • ถ้าตั้ง version constraint ใน Maven/Gradle/SBT ก็จะคงไว้แยกจากเวอร์ชันภาษาได้
      แต่ไลบรารีเฉพาะทาง Scala มักใส่เวอร์ชัน Scala ไว้ในเวอร์ชันด้วย จึงต้องระวัง
  • รายงานบั๊กของ SoftwareMill และ Scala บน GitHub เป็นการแก้ไขที่เล็กแต่แม่นยำ
    แสดงให้เห็นถึงความ expressive ของ Scala และtype safetyที่โดดเด่น
    เหมือนในบทความของ Li Haoyi ที่ชี้ว่ามันก็น่าสนใจพอจะเป็นภาษาทดแทน Python ได้
  • บทเรียนสำคัญคือ เมื่อมีการอัปเกรดใหญ่ของภาษาหรือเฟรมเวิร์ก ก็ควรอัปเดตไลบรารีให้ใหม่ไปพร้อมกันด้วย
    ยิ่งมีไลบรารีที่ใช้ฟีเจอร์แนว magic มากเท่าไร ก็ยิ่งสำคัญ