• ปรับปรุงประสิทธิภาพของฟังก์ชัน 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) ขึ้นไป

ยังไม่มีความคิดเห็น

ยังไม่มีความคิดเห็น