การปรับปรุงประสิทธิภาพการช่วยเหลือพิเศษของ Chromium

โพสต์นี้มาจาก Ahmed Elwasefi ผู้มีส่วนร่วมของ Chromium ซึ่งแชร์ว่าทําไมเขาจึงกลายมาเป็น Contributor ผ่าน Google Summer of Code และปัญหาด้านประสิทธิภาพการช่วยเหลือพิเศษที่เขาพบและแก้ไข

เมื่อใกล้จะจบปีสุดท้ายในสาขาวิศวกรรมคอมพิวเตอร์ที่มหาวิทยาลัยเยอรมันในไคโร ฉันตัดสินใจที่จะมองหาโอกาสในการมีส่วนร่วมกับโอเพนซอร์ส ฉันจึงเริ่มสำรวจรายการปัญหาที่เหมาะสำหรับผู้เริ่มต้นของ Chromium และพบว่าตัวเองสนใจการช่วยเหลือพิเศษเป็นพิเศษ การค้นหาคำแนะนำทำให้ฉันได้พบกับ AaronLeventhal ซึ่งความเชี่ยวชาญและความเต็มใจที่จะให้ความช่วยเหลือของเขาเป็นแรงบันดาลใจให้ฉันร่วมทำโปรเจ็กต์กับเขา การทำงานร่วมกันครั้งนี้กลายเป็นประสบการณ์ของฉันในGoogle Summer of Code ซึ่งฉันได้รับเลือกให้ทำงานร่วมกับทีมการช่วยเหลือพิเศษของ Chromium

หลังจากเข้าร่วม Google Summer of Code จนเสร็จสมบูรณ์แล้ว ฉันยังคงแก้ไขปัญหาการเลื่อนที่ยังไม่ได้รับการแก้ไขต่อไป โดยมุ่งเน้นที่การปรับปรุงประสิทธิภาพ ขอขอบคุณที่ให้เงินสนับสนุน 2 รายการจากโปรแกรม OpenCollective ของ Google เราจึงทํางานในโปรเจ็กต์นี้ได้อย่างต่อเนื่อง ทั้งยังรับงานอื่นๆ ที่มุ่งเน้นการปรับปรุงโค้ดให้มีประสิทธิภาพดีขึ้นด้วย

บล็อกโพสต์นี้จะแชร์เส้นทางของฉันกับ Chromium ในช่วง 1 ปีครึ่งที่ผ่านมา โดยอธิบายรายละเอียดการปรับปรุงทางเทคนิคที่เราทำ โดยเฉพาะในด้านประสิทธิภาพ

ผลกระทบของโค้ดการช่วยเหลือพิเศษต่อประสิทธิภาพใน Chrome

โค้ดการช่วยเหลือพิเศษของ Chrome ช่วยให้เทคโนโลยีความช่วยเหลือพิเศษ เช่น โปรแกรมอ่านหน้าจอ เข้าถึงเว็บได้ อย่างไรก็ตาม เมื่อเปิดใช้ ฟีเจอร์นี้อาจส่งผลต่อเวลาในการโหลด ประสิทธิภาพ และอายุการใช้งานแบตเตอรี่ ดังนั้น หากไม่จําเป็น โค้ดนี้จะยังคงไม่มีการใช้งานเพื่อไม่ให้ประสิทธิภาพช้าลง ผู้ใช้ประมาณ 5-10% เปิดใช้โค้ดการช่วยเหลือพิเศษ ซึ่งมักเกิดจากเครื่องมือต่างๆ เช่น เครื่องมือจัดการรหัสผ่านและซอฟต์แวร์ป้องกันไวรัสที่ใช้ API การช่วยเหลือพิเศษของแพลตฟอร์ม เครื่องมือเหล่านี้ใช้ API เหล่านี้เพื่อโต้ตอบและแก้ไขเนื้อหาหน้าเว็บ เช่น ค้นหาช่องรหัสผ่านสำหรับเครื่องมือจัดการรหัสผ่านและโปรแกรมช่วยกรอกแบบฟอร์ม

เรายังไม่ทราบการลดลงทั้งหมดของเมตริกหลัก แต่การทดสอบล่าสุดที่เรียกว่า "ปิดการช่วยเหลือพิเศษโดยอัตโนมัติ" (ปิดการช่วยเหลือพิเศษเมื่อไม่ได้ใช้งาน) แสดงให้เห็นว่าการลดลงค่อนข้างสูง ปัญหานี้เกิดขึ้นเนื่องจากมีการคำนวณและการสื่อสารจำนวนมากใน 2 พื้นที่หลักของโค้ดเบสการช่วยเหลือพิเศษของ Chrome ได้แก่ โปรแกรมแสดงผลและเบราว์เซอร์ โปรแกรมแสดงผลจะรวบรวมข้อมูลเกี่ยวกับเนื้อหาเว็บและการเปลี่ยนแปลงเนื้อหา รวมถึงคํานวณพร็อพเพอร์ตี้การช่วยเหลือพิเศษสําหรับลําดับชั้นของโหนด จากนั้นระบบจะจัดรูปแบบโหนดที่เปลี่ยนแปลงแล้วและส่งผ่านไปป์ไปยังเธรด UI หลักของกระบวนการเบราว์เซอร์ เทรดนี้รับและแปลงข้อมูลนี้ให้เป็นรูปแบบต้นไม้ของโหนดที่เหมือนกัน จากนั้นจึงแปลงเป็นรูปแบบที่เหมาะกับเทคโนโลยีความช่วยเหลือพิเศษของบุคคลที่สาม เช่น โปรแกรมอ่านหน้าจอ

การปรับปรุงการช่วยเหลือพิเศษของ Chromium

โปรเจ็กต์ต่อไปนี้เสร็จสมบูรณ์ในช่วง Summer of Code และหลังจากนั้นได้รับเงินทุนสนับสนุนจากโปรแกรม OpenCollective ของ Google

การปรับปรุงแคช

ใน Chrome มีโครงสร้างข้อมูลพิเศษที่เรียกว่าต้นไม้การช่วยเหลือพิเศษซึ่งจะแสดงภาพต้นไม้ DOM ซึ่งใช้เพื่อช่วยเทคโนโลยีความช่วยเหลือพิเศษในการเข้าถึงเนื้อหาบนเว็บ บางครั้งเมื่ออุปกรณ์ต้องการข้อมูลจากต้นไม้นี้ ข้อมูลอาจยังไม่พร้อมใช้งาน ดังนั้นเบราว์เซอร์จึงต้องกำหนดเวลาคำขอเหล่านั้นไว้ภายหลัง

ก่อนหน้านี้ การจัดการการกําหนดเวลานี้ใช้เมธอดที่เรียกว่า Closure ซึ่งเกี่ยวข้องกับการวางการเรียกกลับลงในคิว แนวทางนี้ทําให้งานเพิ่มขึ้นเนื่องจากวิธีประมวลผลการปิด

เราจึงเปลี่ยนไปใช้ระบบที่ใช้ enum เพื่อปรับปรุงเรื่องนี้ แต่ละงานจะได้รับค่า Enum ที่เฉพาะเจาะจง และเมื่อต้นไม้การช่วยเหลือพิเศษพร้อมแล้ว ระบบจะเรียกใช้เมธอดที่ถูกต้องสำหรับงานนั้น การเปลี่ยนแปลงนี้ทําให้โค้ดเข้าใจง่ายขึ้นและปรับปรุงประสิทธิภาพได้กว่า 20%

กราฟการทดสอบประสิทธิภาพรันไทม์
กราฟรันไทม์ของการทดสอบประสิทธิภาพหลายรายการที่มีประสิทธิภาพลดลงประมาณ 20%

การค้นหาและแก้ไขปัญหาด้านประสิทธิภาพการเลื่อน

ถัดไป เราลองดูว่าประสิทธิภาพจะดีขึ้นอย่างไรเมื่อปิดการแปลงค่าพิกัดสี่เหลี่ยมผืนผ้าเป็นอนุกรม กล่องขอบคือตําแหน่งและขนาดขององค์ประกอบในหน้าเว็บ รวมถึงรายละเอียดต่างๆ เช่น ความกว้าง ความสูง และตําแหน่งที่สัมพันธ์กับองค์ประกอบหลัก

ในการทดสอบนี้ เรานําโค้ดที่จัดการกล่องขอบเขตออกชั่วคราวและทําการทดสอบประสิทธิภาพเพื่อดูผลลัพธ์ การทดสอบ 1 รายการคือ focus-links.html แสดงให้เห็นถึงการปรับปรุงอย่างมากประมาณ 1618% การค้นพบนี้กลายเป็นรากฐานสําหรับการดําเนินการต่อๆ ไป

กำลังตรวจสอบการทดสอบที่ช้า

เราเริ่มตรวจสอบสาเหตุที่การทดสอบนั้นช้าเมื่อใช้กล่องขอบเขต การทดสอบทั้งหมดที่ทําคือวางโฟกัสในลิงก์หลายรายการตามลําดับ ดังนั้น ปัญหาหลักต้องเกี่ยวข้องกับการโฟกัสองค์ประกอบหรือการเลื่อนที่เกิดขึ้นพร้อมกับการดำเนินการโฟกัส ในการทดสอบนี้ เราได้เพิ่ม {preventScroll: true} ลงในการเรียกใช้ focus() ในการทดสอบประสิทธิภาพ ซึ่งจะหยุดการเลื่อน

เมื่อปิดใช้การเลื่อน เวลาทดสอบลดลงเหลือ 1.2 มิลลิวินาทีเมื่อการคำนวณกล่องขอบเขตทำงานอยู่ ซึ่งแสดงให้เห็นว่าการเลื่อนเป็นปัญหาจริง

ผลการทดสอบเมื่อปิดใช้การเลื่อน
รันไทม์การทดสอบการโฟกัสลิงก์ลดลงจาก 20 มิลลิวินาทีเป็น 1.1 มิลลิวินาทีเมื่อปิดใช้การเลื่อนหรือนำการจัดรูปแบบกล่องขอบเขตออก

เราได้สร้างการทดสอบใหม่ชื่อ scroll-in-page.html เพื่อจำลองการทดสอบ focus-links แต่จะใช้ scrollIntoView() ในการเลื่อนดูองค์ประกอบแทนการใช้โฟกัส เราทดสอบทั้งการเลื่อนที่ราบรื่นและทันที โดยมีและไม่มีการคำนวณกรอบล้อมรอบ

ผลการทดสอบใหม่
เวลาที่ใช้ในการประมวลผลการเลื่อนในการเลื่อนทันทีคือ 65 มิลลิวินาที ส่วนการเลื่อนอย่างราบรื่นใช้เวลา 123 มิลลิวินาที

ผลลัพธ์แสดงให้เห็นว่าเมื่อใช้การเลื่อนทันทีและกล่องขอบเขต กระบวนการจะใช้เวลาประมาณ 66 มิลลิวินาที การเลื่อนอย่างราบรื่นช้ากว่าที่ประมาณ 124 มิลลิวินาที เมื่อเราปิดกล่องขอบ การดำเนินการนี้ใช้เวลาเพียงเสี้ยววินาทีเนื่องจากไม่มีการทริกเกอร์เหตุการณ์ใดๆ

เราทราบกรณีนี้ แต่ทำไมกรณีนี้จึงเกิดขึ้น

ตอนนี้เราทราบแล้วว่าการเลื่อนเป็นสาเหตุที่ทำให้การแปลงเป็นรูปแบบที่เข้ากันได้กับมาตรฐานการช่วยเหลือพิเศษช้าลง แต่เรายังต้องหาสาเหตุ ในการวิเคราะห์เรื่องนี้ เราใช้เครื่องมือ 2 อย่างที่เรียกว่า perf และ pprof เพื่อแจกแจงงานที่ดำเนินการในกระบวนการของเบราว์เซอร์ เครื่องมือเหล่านี้มักใช้ใน C++ สําหรับการจัดเก็บข้อมูล กราฟต่อไปนี้แสดงตัวอย่างของส่วนที่น่าสนใจ

กราฟการทดสอบการเลื่อนที่บันทึกไว้
กราฟที่สร้างขึ้นจากการทดสอบการเลื่อน แสดงให้เห็นว่าเวลาส่วนใหญ่ใช้ในการเรียกใช้ฟังก์ชันที่ชื่อ Unserialize และอีกฟังก์ชันที่ชื่อ IsChildOfLeaf

หลังจากตรวจสอบแล้ว เราพบว่าปัญหาไม่ได้เกิดจากโค้ดการแปลงข้อมูลย้อนกลับ แต่เป็นความถี่ในการเรียกใช้ ในการทําความเข้าใจเรื่องนี้ เราจําเป็นต้องดูวิธีทํางานของการอัปเดตการช่วยเหลือพิเศษใน Chromium ระบบจะไม่ส่งการอัปเดตทีละรายการ แต่จะมีตำแหน่งส่วนกลางที่เรียกว่า AXObjectCache ซึ่งจัดเก็บพร็อพเพอร์ตี้ทั้งหมด เมื่อโหนดมีการเปลี่ยนแปลง วิธีการต่างๆ จะแจ้งให้แคชทำเครื่องหมายโหนดนั้นว่าไม่ถูกต้องสำหรับการแปลงเป็นอนุกรมในภายหลัง จากนั้นระบบจะจัดรูปแบบและส่งพร็อพเพอร์ตี้ทั้งหมดของโน้ตที่ยังไม่บันทึก รวมถึงพร็อพเพอร์ตี้ที่ไม่มีการเปลี่ยนแปลงไปยังเบราว์เซอร์ แม้ว่าการออกแบบนี้จะลดความซับซ้อนของโค้ดและลดความซับซ้อนด้วยเส้นทางการอัปเดตเดียว แต่ก็จะทําให้ช้าลงเมื่อมีเหตุการณ์ "ทําเครื่องหมายว่าไม่ถูกต้อง" อย่างรวดเร็ว เช่น เหตุการณ์จากการเลื่อน สิ่งเดียวที่เปลี่ยนแปลงคือค่า scrollX และ scrollY แต่เราจะจัดรูปแบบพร็อพเพอร์ตี้ที่เหลือด้วยค่าเหล่านี้ทุกครั้ง อัตราการอัปเดตที่นี่สูงถึง 20 ครั้งต่อวินาที

การจัดรูปแบบกล่องขอบเขตช่วยแก้ปัญหานี้ได้โดยใช้เส้นทางการจัดรูปแบบที่เร็วขึ้นซึ่งส่งเฉพาะรายละเอียดกล่องขอบเขตเท่านั้น ซึ่งช่วยให้อัปเดตได้อย่างรวดเร็วโดยไม่ส่งผลต่อพร็อพเพอร์ตี้อื่นๆ วิธีนี้จัดการการเปลี่ยนแปลงกล่องขอบเขตได้อย่างมีประสิทธิภาพ

การแก้ไขการเลื่อน

วิธีการแก้ปัญหานี้ชัดเจนมาก นั่นคือ รวมการเลื่อนปัจจุบันที่เลื่อนออกด้วยกล่องขอบเขตกับการแปลงเป็นอนุกรม วิธีนี้ช่วยให้มั่นใจได้ว่าการอัปเดตการเลื่อนจะประมวลผลผ่านเส้นทางที่เร็ว ซึ่งจะช่วยเพิ่มประสิทธิภาพโดยไม่เกิดความล่าช้าที่ไม่จำเป็น การรวมออฟเซตการเลื่อนกับข้อมูลขอบเขตช่วยให้เราเพิ่มประสิทธิภาพกระบวนการเพื่ออัปเดตที่ราบรื่นและมีประสิทธิภาพมากขึ้น ซึ่งทำให้ผู้ใช้ที่เปิดการช่วยเหลือพิเศษได้รับประสบการณ์การใช้งานที่ราบรื่นขึ้น การปรับปรุงหลังจากติดตั้งใช้งานการแก้ไขคือสูงสุด 825% ในการทดสอบการเลื่อน

การลดความซับซ้อนของโค้ด

ในช่วงนี้ เรามุ่งเน้นที่คุณภาพโค้ดในโปรเจ็กต์ชื่อ Onion Soup ซึ่งทำให้โค้ดเรียบง่ายขึ้นด้วยการลดหรือนำโค้ดที่กระจายอยู่ในเลเยอร์ต่างๆ ออกโดยไม่จำเป็น

โปรเจ็กต์แรกมีจุดมุ่งหมายเพื่อปรับปรุงวิธีจัดรูปแบบข้อมูลการช่วยเหลือพิเศษจากโปรแกรมแสดงผลไปยังเบราว์เซอร์ ก่อนหน้านี้ ข้อมูลต้องผ่านเลเยอร์เพิ่มเติมก่อนที่จะถึงปลายทาง ซึ่งเพิ่มความซับซ้อนโดยไม่จำเป็น เราได้ลดความซับซ้อนของกระบวนการนี้ด้วยการอนุญาตให้ส่งข้อมูลได้โดยตรงโดยไม่ต้องผ่านสื่อกลาง

นอกจากนี้ เรายังระบุและนำเหตุการณ์ที่ล้าสมัยบางรายการออกด้วย ซึ่งทำให้เกิดงานที่ไม่จำเป็นในระบบ เช่น เหตุการณ์ที่ทริกเกอร์เมื่อเลย์เอาต์เสร็จสมบูรณ์ เราแทนที่ฟีเจอร์เหล่านี้ด้วยโซลูชันที่มีประสิทธิภาพมากขึ้น

รวมถึงมีการปรับปรุงเล็กๆ น้อยๆ อื่นๆ ด้วย ขออภัยที่เราไม่ได้บันทึกการปรับปรุงประสิทธิภาพสำหรับรายการเหล่านี้ แต่เรายินดีที่จะแจ้งให้ทราบว่าโค้ดมีความชัดเจนและอธิบายตนเองได้มากขึ้นกว่าเดิมมาก ซึ่งช่วยปูทางสำหรับการปรับปรุงประสิทธิภาพในอนาคตได้อย่างมาก คุณตรวจสอบการเปลี่ยนแปลงจริงได้ ในโปรไฟล์ Gerrit ของฉัน

บทสรุป

การทํางานร่วมกับทีมการช่วยเหลือพิเศษของ Chromium เป็นเส้นทางที่คุ้มค่า การแก้ปัญหาที่หลากหลาย ตั้งแต่การเพิ่มประสิทธิภาพการเลื่อนไปจนถึงการลดความซับซ้อนของโค้ดเบส ทำให้ฉันได้เข้าใจการพัฒนาในโปรเจ็กต์ขนาดใหญ่เช่นนี้มากขึ้น รวมถึงได้เรียนรู้เครื่องมือสําคัญสําหรับการโปรไฟล์ นอกจากนี้ เรายังได้เรียนรู้ว่าความสำคัญของการช่วยเหลือพิเศษต่อการสร้างเว็บที่ครอบคลุมสำหรับทุกคน การปรับปรุงที่เราทำไม่เพียงช่วยปรับปรุงประสบการณ์ของผู้ใช้ที่ใช้เทคโนโลยีความช่วยเหลือพิเศษเท่านั้น แต่ยังช่วยเพิ่มประสิทธิภาพโดยรวมและเสถียรภาพของเบราว์เซอร์ด้วย

ผลลัพธ์ด้านประสิทธิภาพน่าประทับใจ ตัวอย่างเช่น การเปลี่ยนไปใช้ลิสต์สำหรับกำหนดเวลางานช่วยปรับปรุงประสิทธิภาพได้มากกว่า 20% นอกจากนี้ การแก้ไขการเลื่อนของเรายังส่งผลให้การทดสอบการเลื่อนลดลงถึง 825% การเปลี่ยนแปลงการลดความซับซ้อนของโค้ดไม่เพียงทำให้โค้ดชัดเจนขึ้นและดูแลรักษาได้ง่ายขึ้นเท่านั้น แต่ยังปูทางสำหรับการปรับปรุงในอนาคตด้วย

ฉันอยากแสดงความขอบคุณ Stefan Zager, Chris Harrelson และ Mason Freed ที่ให้การสนับสนุนและคำแนะนำตลอดทั้งปี และโดยเฉพาะ Aaron Leventhal ที่ช่วยให้ฉันได้รับโอกาสนี้ และขอขอบคุณ Tab Atkins-Bittner และทีม GSoC ที่ให้การสนับสนุน

เราขอแนะนำอย่างยิ่งให้เข้าร่วม Chromium สำหรับผู้ที่อยากมีส่วนร่วมในโปรเจ็กต์ที่มีความหมายและพัฒนาทักษะของตนเอง ซึ่งถือเป็นวิธีที่ดีในการเรียนรู้ และโปรแกรมอย่าง Google Summer of Code เป็นจุดเริ่มต้นที่ยอดเยี่ยมสำหรับเส้นทางของคุณ