Swift ภาษาโปรแกรมมิ่งสมัยใหม่ของ Apple กำลังเผชิญกับเสียงวิจารณ์ที่เพิ่มขึ้นจากนักพัฒนาเกี่ยวกับประสิทธิภาพของตัวตรวจสอบประเภท (type checker) การอภิปรายล่าสุดในชุมชนนักพัฒนาได้เน้นย้ำถึงความไม่พอใจอย่างกว้างขวางต่อเวลาคอมไพล์ที่อาจยืดเยื้อไปถึงหลักสิบวินาทีสำหรับนิพจน์ที่ดูเหมือนเรียบง่าย ซึ่งทำให้เกิดคำถามเกี่ยวกับความเหมาะสมของภาษาสำหรับการพัฒนาในระดับใหญ่
ปัญหาประสิทธิภาพที่ไม่มีทีท่าว่าจะหายไป
นักพัฒนารายงานว่าพบความล่าช้าในการตรวจสอบประเภทซึ่งส่งผลกระทบอย่างมีนัยสำคัญต่อประสิทธิภาพการทำงานของพวกเขา ตัวอย่างหนึ่งที่บอกเล่าได้เป็นอย่างดีเกี่ยวข้องกับนิพจน์การต่อสายอักขระ (string concatenation) ที่ตรงไปตรงมา ซึ่งรายงานว่าต้องใช้เวลา 10 วินาทีในการตรวจสอบประเภทในเวอร์ชัน Swift ก่อนหน้า โดยการปรับปรุงต่างๆ ลดเวลานี้เหลือเพียง 6 วินาทีในการอัปเดตล่าสุดเท่านั้น สำหรับนักพัฒนาที่ทำงานบนฮาร์ดแวร์ Apple รุ่นใหม่ๆ อย่างเช่น Mac รุ่น M2 ปัญหาประสิทธิภาพเหล่านี้ก็น่ากังวลเป็นพิเศษเมื่อพิจารณาถึงพลังการประมวลผลของฮาร์ดแวร์ที่มีอยู่
ฉันคาดว่านิพจน์แบบนั้นน่าจะตรวจสอบประเภทได้เร็วกว่าที่เป็นอยู่เป็นล้านเท่า – อย่างน้อยก็ควรเป็นอย่างนั้น
ปัญหาหลักเกิดจากระบบการตรวจสอบประเภทแบบใช้ข้อจำกัด (constraint-based type checking system) ของ Swift ซึ่งต้องจัดการกับชุด組み合わせที่ซับซ้อนของโอเปอเรเตอร์ที่โอเวอร์โหลดและการอนุมานประเภท ด้วยการโอเวอร์โหลดโอเปอเรเตอร์ + ถึง 17 รูปแบบ และมี 9 ประเภทที่นำโปรโตคอล ExpressibleByStringLiteral ไปใช้ใน standard library เพียงอย่างเดียว คอมไพเลอร์ต้องเผชิญกับความซับซ้อนแบบเอกซ์โพเนนเชียลเมื่อต้องแก้ไขแม้แต่นิพจน์พื้นฐาน
ตัวการสำคัญที่ส่งผลต่อประสิทธิภาพใน Swift
- operator
+มี overload ถึง 17 แบบในไลบรารีมาตรฐาน - มี 9 ประเภทที่ใช้ protocol
ExpressibleByStringLiteral - ความซับซ้อนของ bidirectional type inference
- การแก้ไขปัญหา ad-hoc overloading resolution
ทางเลือกด้านสถาปัตยกรรมภายใต้การตรวจสอบ
คุณสมบัติเฉพาะของภาษาสองประการดูเหมือนจะเป็นตัวการสำคัญต่อปัญหาประสิทธิภาพ: การอนุมานประเภทแบบสองทิศทาง (bidirectional type inference) และการโอเวอร์โหลดแบบเฉพาะกิจ (ad-hoc overloading) การอนุมานแบบสองทิศทางอนุญาตให้ประเภทข้อมูลไหลทั้งขึ้นและลงผ่านต้นไม้ของนิพจน์ (expression trees) ในขณะที่การโอเวอร์โหลดทำให้ฟังก์ชันหลายฟังก์ชันสามารถใช้ชื่อเดียวกันได้โดยมีประเภทพารามิเตอร์ที่แตกต่างกัน แม้ว่าคุณสมบัติเหล่านี้จะช่วยให้ไวยากรณ์ของ Swift สะอาดและแสดงออกได้ดี แต่ก็มาพร้อมกับต้นทุนการคำนวณที่สูง
แผนพัฒนาของทีม Swift ยอมรับถึงความท้าทายเหล่านี้แต่ยังไม่ถึงขั้นเสนอวิธีแก้ไขที่รุนแรง เช่น การลบคุณสมบัติการโอเวอร์โหลดออกไปทั้งหมด โดยตระหนักดีว่าการเปลี่ยนแปลงดังกล่าวจะทำให้ไม่สามารถใช้งานร่วมกับฐานโค้ด Swift ที่มีอยู่ได้ แทนที่จะทำเช่นนั้น พวกเขากำลังมุ่งเน้นไปที่การปรับปรุงอัลกอริธึมตัวแก้ไขข้อจำกัด (constraint solver) และระบบการวินิจฉัยปัญหาแบบค่อยเป็นค่อยไป
ผลกระทบในโลกจริงต่อเวิร์กโฟลว์การพัฒนา
ปัญหาด้านประสิทธิภาพส่งผลกระทบเชิงปฏิบัติต่อเวิร์กโฟลว์ประจำวันของนักพัฒนา หลายคนรายงานว่าใช้เวลาอย่างมากในการรอการคอมไพล์ เพียงเพื่อจะได้รับข้อความแสดงข้อผิดพลาดที่เข้าใจยากซึ่งให้คำแนะนำเพียงเล็กน้อยสำหรับการแก้ไขปัญหาเบื้องต้น ปัญหานี้รุนแรงเป็นพิเศษในการพัฒนา SwiftUI ซึ่งลำดับชั้นของวิว (view hierarchies) ที่ซับซ้อนและไวยากรณ์ทราลิงคลอเจอร์ (trailing closure syntax) สามารถสร้างฝันร้ายในการตรวจสอบประเภทได้
นักพัฒนาบางส่วนได้นำแนวทางการเขียนโปรแกรมแบบรับมือ (defensive programming practices) มาใช้เพื่อแก้ไขข้อจำกัดเหล่านี้ รวมถึงการเพิ่มคำอธิบายประเภท (type annotations) อย่างชัดเจนในทุกที่ และการหลีกเลี่ยงการเชื่อมต่อการดำเนินการ (chains of operations) ที่ยาวๆ อย่างไรก็ตาม วิธีการแก้ปัญหาเหล่านี้บ่อนทำลายเป้าหมายของ Swift ในการให้ไวยากรณ์ที่สะอาดและอ่านง่าย และเพิ่มภาระให้กับนักพัฒนาในการจัดการด้วยตนเองสิ่งที่ควรจะถูกจัดการอัตโนมัติโดยคอมไพเลอร์
วิธีแก้ปัญหาของนักพัฒนา
- การระบุประเภทข้อมูลอย่างชัดเจนสำหรับตัวแปรทั้งหมด
- ห้ามผสมจำนวนเต็มและจำนวนทศนิยมโดยไม่มีการแปลงประเภทข้อมูลอย่างชัดเจน
- แยกการดำเนินการที่ยาวและซับซ้อนออกเป็นคำสั่งแยกกัน
- ลดการใช้ trailing closures ในนิพจน์ที่ซับซ้อน
การตอบสนองของชุมชนและแพลตฟอร์มทางเลือก
ปัญหาประสิทธิภาพที่ยืดเยื้อได้นำไปสู่การที่นักพัฒนาบางส่วนทบทวนการเลือกแพลตฟอร์มของพวกเขาใหม่ ความหงุดหงิดใจเห็นได้ชัดในการอภิปรายภายในชุมชน โดยมีผู้แสดงความคิดเห็นหลายคนแสดงความสงสัยเกี่ยวกับการลงทุนเพิ่มเติมในระบบนิเวศ Swift บางคนระบุว่าประมาณหนึ่งในสามของแอปพลิเคชันใน App Store ในปัจจุบันถูกสร้างขึ้นโดยใช้ Flutter แทนที่จะเป็น Swift ดั้งเดิม ซึ่งชี้ให้เห็นว่าปัญหาประสิทธิภาพอาจกำลังผลักดันให้นักพัฒนาไปสู่เทคโนโลยีทางเลือก
สถานการณ์นี้ยังทำให้มีการเปรียบเทียบกับแนวทางของ Chris Lattner กับ Mojo ภาษาที่ใหม่กว่าซึ่งใช้ทิศทางทางสถาปัตยกรรมที่แตกต่างออกไปสำหรับระบบประเภทของมัน ในขณะที่ Swift ยังคงพัฒนาต่อไป ชุมชนดูเหมือนจะแบ่งออกระหว่างผู้ที่ยอมทนกับข้อจำกัดในปัจจุบันและผู้ที่แสวงหาวิธีแก้ไขปัญหาที่เป็นพื้นฐานมากกว่า
การปรับปรุงประสิทธิภาพการตรวจสอบประเภทของ Swift
- Swift 5.6: การเพิ่มประสิทธิภาพเบื้องต้นสำหรับ constraint solver
- Swift 5.7: การลดเวลาในการตรวจสอบประเภทลงอีก (จาก 7 วินาที → 0.6 วินาทีในบางกรณี)
- Development branch: การปรับปรุงอัลกอริทึมเพิ่มเติม (ลงเหลือ 0.17 วินาทีสำหรับนิพจน์เฉพาะ)
มองไปข้างหน้า
ทีมพัฒนาของ Swift ยังคงทำงานเพื่อการปรับปรุง โดยเวอร์ชันล่าสุดแสดงให้เห็นถึงความก้าวหน้าที่วัดผลได้ Swift 5.6 และ 5.7 แสดงให้เห็นถึงการเพิ่มขึ้นของประสิทธิภาพอย่างมีนัยสำคัญในบางสถานการณ์ โดยนิพจน์ที่ซับซ้อนบางอย่างมีเวลาในการตรวจสอบประเภทลดลงจาก 7 วินาทีเหลือ 0.6 วินาที งานที่กำลังดำเนินอยู่มุ่งเน้นไปที่การเพิ่มประสิทธิภาพการอนุมานแบบดิสจังชัน (disjunction inference) การจัดการโซลูชันบางส่วน (partial solutions) ให้มีประสิทธิภาพมากขึ้น และการปรับปรุงการวินิจฉัยปัญหาในโหมดเรฟยูจ (refuge mode diagnostics)
อย่างไรก็ตาม ความตึงเครียดพื้นฐานระหว่างไวยากรณ์ที่แสดงออกได้ดีของ Swift กับการคอมไพล์ที่มีประสิทธิภาพยังคงไม่ได้รับการแก้ไข ขณะที่ภาษายังคงเติบโตอย่างต่อเนื่อง การปรับสมดุลระหว่างลำดับความสำคัญที่แข่งขันกันเหล่านี้จะเป็นสิ่งสำคัญสำหรับการรักษาความพึงพอใจและการยอมรับจากนักพัฒนา ชุมชนกำลังจับตาดูอย่างใกล้ชิด โดยหวังว่าการอัปเดตในอนาคตจะส่งมอบทั้งไวยากรณ์ที่สะอาดซึ่งพวกเขารักและประสิทธิภาพที่พวกเขาต้องการ
หมายเหตุ: การตรวจสอบประเภทแบบใช้ข้อจำกัด (Constraint-based type checking) ใช้ระบบของข้อจำกัดประเภทที่ต้องเป็นไปตามเงื่อนไข คล้ายกับการแก้ปริศนาที่แต่ละชิ้นส่วนต้องเข้ากันได้กับกฎเฉพาะ หมายเหตุ: การอนุมานประเภทแบบสองทิศทาง (Bidirectional type inference) อนุญาตให้คอมไพเลอร์ deduce ประเภทโดยการวิเคราะห์ทั้งบริบทที่ใช้นิพจน์และส่วนประกอบของนิพจน์
