- ปรับปรุงประสิทธิภาพของฟังก์ชัน JSON.stringify ในเอนจิน V8 ให้เร็วขึ้นมากกว่าสองเท่า ส่งผลให้การซีเรียลไลซ์ข้อมูลทำได้รวดเร็วยิ่งขึ้น
- เพิ่มเส้นทางการทำงานแบบเพิ่มประสิทธิภาพสำหรับ อ็อบเจ็กต์ที่ไม่มี side effect โดยตัดตรรกะการตรวจสอบเชิงป้องกันจำนวนมากออกไป จึงได้ความเร็วที่ดีขึ้นมากกับอ็อบเจ็กต์ข้อมูลทั่วไป
- ในส่วนของ การประมวลผลสตริง มีการใช้วิธีที่ซับซ้อนมากขึ้นทั้งด้านฮาร์ดแวร์และหน่วยความจำ เช่น การแยก 1 ไบต์/2 ไบต์, การใช้ SIMD และการเปลี่ยนโครงสร้างบัฟเฟอร์ชั่วคราว
- ในกระบวนการ แปลงตัวเลข ได้เปลี่ยนอัลกอริทึมจาก Grisu3 เดิมเป็น Dragonbox ทำให้การแปลงที่รวดเร็วขึ้นเป็นไปได้ในภาพรวมของการเรียก
Number.toString() ด้วย
- แม้ในอาร์กิวเมนต์หรือรูปแบบบางอย่างจะย้อนกลับไปใช้เส้นทางซีเรียลไลซ์ปกติ แต่ใน สถานการณ์การพัฒนาเว็บส่วนใหญ่ จะได้รับผลของการเพิ่มประสิทธิภาพนี้โดยอัตโนมัติ
ภาพรวม
JSON.stringify เป็นฟังก์ชันหลักสำหรับ แปลงข้อมูลเป็นสตริงใน JavaScript
- การเพิ่มประสิทธิภาพของฟังก์ชันนี้ส่งผลดีต่องานสำคัญบนเว็บอย่างการส่งคำขอผ่านเครือข่ายหรือการบันทึกลง localStorage ด้วย
- ด้วยวิศวกรรมล่าสุดของ V8 ความเร็วของความสามารถนี้ได้รับการปรับปรุง มากกว่าสองเท่า และมีการอธิบายแนวทางการเพิ่มประสิทธิภาพหลักอย่างละเอียด
เส้นทาง Fast Path สำหรับกรณีไม่มี side effect
- แกนหลักของการเพิ่มประสิทธิภาพคือการใช้ เส้นทางซีเรียลไลซ์แบบเร็ว ที่ใช้ได้เฉพาะใน สถานการณ์ที่ไม่มี side effect
- ในกรณีเช่นนี้ จะเดินอ็อบเจ็กต์ด้วยโครงสร้างแบบ iterative แทนการเรียกซ้ำแบบ recursive ทำให้ไม่ต้องตรวจสอบ stack overflow และยังลองซีเรียลไลซ์อ็อบเจ็กต์ที่ลึกกว่าเดิมได้
- เมื่ออ็อบเจ็กต์ข้อมูลมีโครงสร้างเรียบง่าย V8 จะใช้ Fast Path นี้แทนตรรกะทั่วไปที่ช้ากว่า เพื่อลดการตรวจสอบจำนวนมากและเพิ่มความเร็ว
การจัดการรูปแบบสตริงที่หลากหลาย
- V8 จัดเก็บสตริงต่างกันตามว่าเป็น อักขระ 1 ไบต์/2 ไบต์ (ASCII/ไม่ใช่ ASCII) และถ้ามีอักขระที่ไม่ใช่ ASCII เพียงตัวเดียว ก็จะจัดการทั้งชุดเป็น 2 ไบต์
- เพื่อประสิทธิภาพในการซีเรียลไลซ์สตริง จึงคอมไพล์ อัลกอริทึมแยกตามชนิดของสตริง ไว้ต่างหาก
- ระหว่างประมวลผลจำเป็นต้องตรวจสอบชนิด instance ของสตริง ดังนั้นหากพบสตริง 2 ไบต์ ตัวซีเรียลไลเซอร์สำหรับ 2 ไบต์ที่เหมาะสมจะรับช่วงสถานะต่อไป
- ด้วยเหตุนี้ ภาระจากการสลับเส้นทางตามการเข้ารหัสสตริงจึงแทบไม่มีเลย
- ผลลัพธ์จะถูกสร้างเป็นบัฟเฟอร์ 1 ไบต์และ 2 ไบต์แยกกัน ก่อนจะรวมเข้าด้วยกันอย่างง่ายในตอนท้าย
เพิ่มประสิทธิภาพการซีเรียลไลซ์สตริงด้วย SIMD
- สตริงใน JavaScript อาจมีอักขระที่ต้อง escape เมื่อทำ JSON serialization
- สตริงที่ยาวจะตรวจสอบหลายไบต์พร้อมกันด้วย คำสั่งฮาร์ดแวร์ SIMD (เช่น ARM64 Neon)
- สตริงสั้นจะใช้ วิธีแบบ SWAR เพื่อประมวลผลหลายอักขระพร้อมกันผ่านการคำนวณบิตในรีจิสเตอร์ทั่วไป
- ไม่ว่าจะใช้วิธีใด ในกรณีส่วนใหญ่ก็สามารถคัดลอกทั้งสตริงได้อย่างรวดเร็วโดยแทบไม่ต้องแปลงเพิ่มเติม
เพิ่ม Express Lane (เส้นทางความเร็วสูงพิเศษ)
- แม้อยู่ภายใน Fast Path ก็ยังมีการเพิ่ม Express Lane เพื่อให้ซีเรียลไลซ์ได้ด้วยการคัดลอกคีย์โดยตรง โดยไม่ต้องทำงานซ้ำอย่างการตรวจสอบพร็อพเพอร์ตี
- ใช้ hidden class flag ของอ็อบเจ็กต์เพื่อตรวจว่าในคีย์ไม่มี Symbol และทั้งหมดเป็น enumerable รวมถึงสามารถซีเรียลไลซ์ได้โดยไม่ต้อง escape จากนั้นทำเครื่องหมายเป็น
fast-json-iterable
- เมื่อซีเรียลไลซ์อ็อบเจ็กต์อื่นที่มี hidden class เดียวกัน ก็จะสามารถ คัดลอกคีย์ได้ทันทีโดยไม่ต้องตรวจสอบเพิ่มเติม
- เทคนิคนี้ยังถูกนำไปประยุกต์ใช้กับ
JSON.parse เพื่อให้การเปรียบเทียบคีย์เร็วขึ้นด้วย
อัลกอริทึม double-to-string ที่เร็วขึ้น
- กระบวนการแปลงตัวเลขเป็นสตริงก็เกิดขึ้นบ่อยและมีความซับซ้อนสูง
- การ เปลี่ยนอัลกอริทึมจาก Grisu3 เดิมเป็น Dragonbox ทำให้เกิดผลด้านประสิทธิภาพดีขึ้นในภาพรวมของการเรียก
Number.prototype.toString() ด้วย
ปรับโครงสร้างบัฟเฟอร์ชั่วคราวให้เหมาะสม
- เดิมระหว่างการสร้างสตริงใช้ บัฟเฟอร์ต่อเนื่องก้อนเดียว ซึ่งเมื่อพื้นที่ไม่พอจะเกิดภาระจากการคัดลอกข้อมูลทั้งหมดทุกครั้ง
- วิธีใหม่ใช้ โครงสร้างบัฟเฟอร์แบบแบ่งส่วน (segmented) โดยต่อบัฟเฟอร์ขนาดเล็กหลายก้อนเข้าด้วยกันตามความจำเป็น
- ด้วยเหตุนี้ เมื่อพื้นที่ไม่พอจึง เพียงจัดสรรบัฟเฟอร์ใหม่ โดยไม่ต้องคัดลอกทั้งหมด
ข้อจำกัด
- Fast Path ทำงานได้เฉพาะกับการซีเรียลไลซ์ข้อมูลแบบเรียบง่าย
- หากไม่ตรงตามเงื่อนไขต่อไปนี้จะใช้เส้นทางปกติ
- ไม่สามารถใช้พารามิเตอร์ replacer หรือ space ได้ (ไม่รองรับ Pretty-Print หรือการแปลงรูปแบบ)
- ต้องเป็นอ็อบเจ็กต์ธรรมดาที่ไม่มีเมธอด toJSON แบบกำหนดเอง
- หากมี พร็อพเพอร์ตีแบบอิงดัชนี จะย้ายไปใช้เส้นทางช้า
- ไม่รองรับ สตริงพิเศษ เช่น ConsString
- แต่สำหรับการซีเรียลไลซ์ข้อมูลทั่วไป การสร้าง API response หรือการแคชค่าคอนฟิกในงานทั่วไป ผลของการเพิ่มประสิทธิภาพนี้จะถูกใช้โดยอัตโนมัติ
บทสรุป
JSON.stringify ได้รับการปรับแนวทางใหม่ในทุกด้านตั้งแต่ การออกแบบพื้นฐาน การจัดการหน่วยความจำ ไปจนถึงการจัดการอักขระ จนทำความเร็วได้ มากกว่า 2 เท่า ตามเกณฑ์ JetStream2 benchmark
- การปรับปรุงนี้สามารถใช้งานได้ทันทีใน V8 เวอร์ชัน 13.8 (Chrome 138) ขึ้นไป
ยังไม่มีความคิดเห็น