กรณีศึกษาในชีวิตจริงเกี่ยวกับการเพิ่มประสิทธิภาพ React SPA
ประสิทธิภาพของเว็บไซต์ไม่ได้วัดแค่เวลาในการโหลด การให้ประสบการณ์การใช้งานที่รวดเร็วและตอบสนองต่อผู้ใช้เป็นสิ่งสำคัญอย่างยิ่ง โดยเฉพาะสำหรับแอปเดสก์ท็อปที่เน้นประสิทธิภาพการทำงานซึ่งผู้ใช้ใช้ทุกวัน ทีมวิศวกรของ Recruit Technologies ได้ทำโปรเจ็กต์การจัดโครงสร้างใหม่เพื่อปรับปรุงเว็บแอป AirSHIFT หนึ่งในแอปของตนให้มีประสิทธิภาพในการป้อนข้อมูลของผู้ใช้ที่ดียิ่งขึ้น มาดูว่าพวกเขาทําได้อย่างไร
ตอบสนองช้า ประสิทธิภาพการทำงานลดลง
AirSHIFT เป็นเว็บแอปพลิเคชันบนเดสก์ท็อปที่ช่วยให้เจ้าของร้านค้า เช่น ร้านอาหารและคาเฟ่ จัดการงานกะของพนักงานได้ แอปพลิเคชันหน้าเว็บเดียวที่สร้างด้วย React มีฟีเจอร์สำหรับไคลเอ็นต์ที่หลากหลาย รวมถึงตารางตารางกริดต่างๆ ของตารางกะที่จัดเรียงตามวัน สัปดาห์ เดือน และอื่นๆ
เมื่อทีมวิศวกรของ Recruit Technologies เพิ่มฟีเจอร์ใหม่ๆ ลงในแอป AirSHIFT ก็เริ่มได้รับความคิดเห็นเกี่ยวกับประสิทธิภาพที่ช้ามากขึ้น Yosuke Furukawa ผู้จัดการฝ่ายวิศวกรของ AirSHIFT กล่าวว่า
ในการศึกษาวิจัยผู้ใช้ เราตกใจเมื่อเจ้าของร้านรายหนึ่งบอกว่าเธอจะลุกออกจากที่นั่งไปชงกาแฟหลังจากคลิกปุ่ม เพื่อฆ่าเวลารอให้ตารางกะโหลด
หลังจากทําการวิจัยแล้ว ทีมวิศวกรพบว่าผู้ใช้จํานวนมากพยายามโหลดตารางกะขนาดใหญ่ในคอมพิวเตอร์สเปคต่ำ เช่น แล็ปท็อป Celeron M 1 GHz จากเมื่อ 10 ปีก่อน
แอป AirSHIFT บล็อกเธรดหลักด้วยสคริปต์ที่มีราคาแพง แต่ทีมวิศวกรไม่ทราบว่าสคริปต์มีราคาแพงเพียงใดเนื่องจากกำลังพัฒนาและทดสอบบนคอมพิวเตอร์ที่มีสเปคสูงและมีการเชื่อมต่อ Wi-Fi ความเร็วสูง

หลังจากสร้างโปรไฟล์ประสิทธิภาพในเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome โดยเปิดใช้การจำกัด CPU และเครือข่ายแล้ว เห็นได้ชัดว่าจำเป็นต้องเพิ่มประสิทธิภาพ AirSHIFT ได้จัดตั้งกลุ่มทำงานเพื่อจัดการปัญหานี้ ต่อไปนี้คือ 5 สิ่งที่ทีมมุ่งเน้นเพื่อให้แอปตอบสนองต่ออินพุตของผู้ใช้ได้ดียิ่งขึ้น
1. จำลองตารางขนาดใหญ่
การแสดงตารางกะงานต้องใช้ขั้นตอนหลายขั้นตอนที่สิ้นเปลืองทรัพยากร เช่น การสร้าง DOM เสมือนและแสดงผลบนหน้าจอตามสัดส่วนจำนวนเจ้าหน้าที่และช่วงเวลา ตัวอย่างเช่น หากร้านอาหารมีพนักงานที่ทำงาน 50 คนและต้องการตรวจสอบตารางกะงานรายเดือน ตารางดังกล่าวจะเป็นตาราง 50 (สมาชิก) คูณด้วย 30 (วัน) ซึ่งจะทำให้ต้องแสดงผลคอมโพเนนต์เซลล์ 1,500 รายการ ซึ่งการดำเนินการนี้ค่อนข้างมีค่าใช้จ่ายสูง โดยเฉพาะสำหรับอุปกรณ์ที่มีสเปคต่ำ แต่สถานการณ์จริงกลับแย่กว่านั้น จากการศึกษา พบว่ามีร้านค้าที่จัดการพนักงาน 200 คน ซึ่งต้องใช้คอมโพเนนต์เซลล์ประมาณ 6,000 รายการในตารางรายเดือนตารางเดียว
AirSHIFT ได้ทำให้ตารางการเปลี่ยนเป็นเวอร์ชวลเพื่อลดต้นทุนของการดำเนินการนี้ ตอนนี้แอปจะเมานต์เฉพาะคอมโพเนนต์ภายในวิวพอร์ตและยกเลิกเมานต์คอมโพเนนต์นอกหน้าจอเท่านั้น


ในกรณีนี้ AirSHIFT ใช้ react-virtualized เนื่องจากมีข้อกำหนดเกี่ยวกับการเปิดใช้ตารางตารางกริด 2 มิติที่ซับซ้อน นอกจากนี้ ยังกําลังหาวิธีแปลงการติดตั้งใช้งานเพื่อใช้ react-window เวอร์ชันน้ำหนักเบาในอนาคตด้วย
ผลลัพธ์
การทำให้เป็นเสมือนของตารางเพียงอย่างเดียวทำให้เวลาในการเขียนสคริปต์ลดลง 6 วินาที (ในสภาพแวดล้อม Macbook Pro ที่ชะลอความเร็ว CPU ลง 4 เท่าและ 3G ที่เร็ว) นี่เป็นการเพิ่มประสิทธิภาพที่ส่งผลมากที่สุดในโปรเจ็กต์การจัดระเบียบใหม่


2. ตรวจสอบด้วย User Timing API
ถัดไป ทีม AirSHIFT ได้ปรับโครงสร้างสคริปต์ที่ทำงานกับอินพุตของผู้ใช้ แผนภูมิเปลวไฟของ เครื่องมือสำหรับนักพัฒนาเว็บใน Chrome ช่วยให้คุณวิเคราะห์สิ่งที่เกิดขึ้นจริงในเธรดหลักได้ แต่ทีม AirSHIFT พบว่าการวิเคราะห์กิจกรรมของแอปพลิเคชันตามวงจรชีวิตของ React นั้นง่ายกว่า
React 16 แสดงการติดตามประสิทธิภาพผ่าน User Timing API ซึ่งคุณดูภาพได้จากส่วนการวัดเวลาของเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome AirSHIFT ใช้ส่วน "ช่วงเวลา" เพื่อค้นหาตรรกะที่ไม่จําเป็นซึ่งทํางานในเหตุการณ์วงจรชีวิตของ React

ผลลัพธ์
ทีม AirSHIFT พบว่ามีการปรับยอด React Tree ที่ไม่จำเป็นเกิดขึ้นก่อนการไปยังส่วนต่างๆ ของเส้นทางทุกครั้ง ซึ่งหมายความว่า React อัปเดตตารางกะโดยไม่จำเป็นก่อนการไปยังส่วนต่างๆ การอัปเดตสถานะ Redux ที่ไม่จำเป็นทำให้เกิดปัญหานี้ การแก้ไขนี้ช่วยประหยัดเวลาในการเขียนสคริปต์ได้ประมาณ 750 มิลลิวินาที AirSHIFT ได้ทําการเพิ่มประสิทธิภาพเล็กๆ น้อยๆ อื่นๆ ด้วย ซึ่งสุดท้ายแล้วส่งผลให้เวลาในการเขียนสคริปต์ลดลงทั้งหมด 1 วินาที
3. โหลดคอมโพเนนต์แบบ Lazy Loading และย้ายตรรกะที่มีค่าใช้จ่ายสูงไปยัง Web Worker
AirSHIFT มีแอปพลิเคชันแชทในตัว เจ้าของร้านค้าหลายรายสื่อสารกับพนักงานผ่านแชทขณะดูตารางกะ ซึ่งหมายความว่าผู้ใช้อาจพิมพ์ข้อความขณะที่ตารางกำลังโหลด หากเทรดหลักไม่ว่างเนื่องจากสคริปต์ที่แสดงผลตาราง อินพุตของผู้ใช้อาจกระตุก
ตอนนี้ AirSHIFT ใช้ React.lazy และ Suspense เพื่อแสดงตัวยึดตําแหน่งสำหรับเนื้อหาตารางขณะโหลดคอมโพเนนต์จริงแบบ Lazy เพื่อปรับปรุงประสบการณ์นี้
ทีม AirSHIFT ยังย้ายข้อมูลตรรกะทางธุรกิจที่มีราคาแพงบางส่วนภายในคอมโพเนนต์ที่โหลดแบบเลื่อนเวลาไปไว้ที่Web Worker ด้วย วิธีนี้ช่วยแก้ปัญหาการกระตุกของอินพุตของผู้ใช้ด้วยการทำให้เธรดหลักว่างเปล่าเพื่อให้มุ่งเน้นที่การตอบสนองต่ออินพุตของผู้ใช้
โดยปกติแล้วนักพัฒนาแอปต้องพบกับความซับซ้อนในการใช้แรงงาน แต่ครั้งนี้ Comlink เป็นผู้ดำเนินการที่ยากลำบากแทน ด้านล่างนี้คือซอร์สโค้ดจำลองของวิธีที่ AirSHIFT นำการทำงานแบบ WFM ไปใช้กับการดำเนินการที่แพงที่สุดอย่างหนึ่ง ซึ่งก็คือการคำนวณต้นทุนแรงงานทั้งหมด
ใน App.js ให้ใช้ React.lazy และ Suspense เพื่อแสดงเนื้อหาสำรองขณะโหลด
/** App.js */
import React, { lazy, Suspense } from 'react'
// Lazily loading the Cost component with React.lazy
const Hello = lazy(() => import('./Cost'))
const Loading = () => (
<div>Some fallback content to show while loading</div>
)
// Showing the fallback content while loading the Cost component by Suspense
export default function App({ userInfo }) {
return (
<div>
<Suspense fallback={<Loading />}>
<Cost />
</Suspense>
</div>
)
}
ในคอมโพเนนต์ต้นทุน ให้ใช้ comlink เพื่อเรียกใช้ตรรกะการคํานวณ
/** Cost.js */
import React from 'react';
import { proxy } from 'comlink';
// import the workerlized calc function with comlink
const WorkerlizedCostCalc = proxy(new Worker('./WorkerlizedCostCalc.js'));
export default async function Cost({ userInfo }) {
// execute the calculation in the worker
const instance = await new WorkerlizedCostCalc();
const cost = await instance.calc(userInfo);
return <p>{cost}</p>;
}
ใช้ตรรกะการคำนวณที่ทำงานในเวิร์กเกอร์และแสดงด้วย comlink
// WorkerlizedCostCalc.js
import { expose } from 'comlink'
import { someExpensiveCalculation } from './CostCalc.js'
// Expose the new workerlized calc function with comlink
expose({
calc(userInfo) {
// run existing (expensive) function in the worker
return someExpensiveCalculation(userInfo);
}
}, self);
ผลลัพธ์
แม้ว่าจะมีตรรกะแบบจำกัดที่เปลี่ยนเป็นเวิร์กเกอร์เป็นการทดลอง แต่ AirSHIFT ก็ย้าย JavaScript ประมาณ 100 มิลลิวินาทีจากชุดข้อความหลักไปยังชุดข้อความของผู้ปฏิบัติงาน (จำลองด้วยการจำกัด CPU 4 เท่า)
ปัจจุบัน AirSHIFT กำลังสำรวจว่าสามารถโหลดคอมโพเนนต์อื่นๆ แบบ Lazy Load ได้หรือไม่ และสามารถส่งผ่านตรรกะเพิ่มเติมไปยัง Web Worker เพื่อลดการกระตุกได้หรือไม่
4. การตั้งงบประมาณด้านประสิทธิภาพ
เมื่อใช้การเพิ่มประสิทธิภาพทั้งหมดเหล่านี้แล้ว สิ่งสำคัญคือต้องตรวจสอบว่าแอปยังคงมีประสิทธิภาพอยู่เสมอ ตอนนี้ AirSHIFT ใช้ bundlesize เพื่อไม่ให้มีขนาดไฟล์ JavaScript และ CSS เกินขนาดปัจจุบัน นอกเหนือจากการตั้งงบประมาณพื้นฐานเหล่านี้แล้ว ทีมยังได้สร้างแดชบอร์ดเพื่อแสดงเปอร์เซ็นต์ต่างๆ ของเวลาในการโหลดตารางการเปลี่ยนเพื่อตรวจสอบว่าแอปพลิเคชันมีประสิทธิภาพหรือไม่แม้ในสภาพที่ไม่เหมาะสม
- ตอนนี้ระบบจะวัดเวลาในการดำเนินการสคริปต์เสร็จสมบูรณ์สำหรับเหตุการณ์ Redux แต่ละรายการ
- ระบบจะรวบรวมข้อมูลประสิทธิภาพใน Elasticsearch
- ประสิทธิภาพของเหตุการณ์แต่ละรายการในเปอร์เซ็นต์ไทล์ที่ 10, 25, 50 และ 75 จะแสดงเป็นภาพด้วย Kibana
ตอนนี้ AirSHIFT จะตรวจสอบเหตุการณ์การโหลดตารางกะเพื่อให้แน่ใจว่าโหลดเสร็จสมบูรณ์ใน 3 วินาทีสำหรับผู้ใช้ในเปอร์เซ็นไทล์ 75 ขณะนี้งบประมาณนี้ยังไม่บังคับใช้ แต่ทางทีมกำลังพิจารณาการแจ้งเตือนอัตโนมัติผ่าน Elasticsearch เมื่อใช้จ่ายเกินงบประมาณ

ผลลัพธ์
จากกราฟด้านบน คุณจะเห็นว่าตอนนี้ AirSHIFT ส่วนใหญ่ใช้งบประมาณ 3 วินาทีสําหรับผู้ใช้ในเปอร์เซ็นไทล์ 75 และโหลดตารางการเปลี่ยนภายใน 1 วินาทีสําหรับผู้ใช้ในเปอร์เซ็นไทล์ 25 การเก็บรวบรวมข้อมูลประสิทธิภาพ RUM จากเงื่อนไขและอุปกรณ์ต่างๆ ช่วยให้ AirSHIFT ตรวจสอบได้ว่าการเปิดตัวฟีเจอร์ใหม่ส่งผลต่อประสิทธิภาพของแอปพลิเคชันจริงหรือไม่
5. แฮ็กกาธอนด้านประสิทธิภาพ
แม้ว่าความพยายามทั้งหมดในการเพิ่มประสิทธิภาพเหล่านี้จะสำคัญและส่งผลดี แต่การทำให้ทีมวิศวกรและทีมธุรกิจให้ความสำคัญกับการพัฒนาที่ไม่เกี่ยวข้องกับฟังก์ชันการทำงานก็ไม่ใช่เรื่องง่ายเสมอไป ปัญหาส่วนหนึ่งคือการเพิ่มประสิทธิภาพบางอย่างเหล่านี้ไม่สามารถวางแผนได้ นวัตกรรมเหล่านี้ต้องอาศัยการทดลองและแนวคิดแบบลองผิดลองถูก
ตอนนี้ AirSHIFT กำลังจัดแฮ็กแฮทเกี่ยวกับประสิทธิภาพ 1 วันภายในองค์กรเพื่อให้วิศวกรมุ่งเน้นที่งานที่เกี่ยวข้องกับประสิทธิภาพเท่านั้น ในแฮ็กกาธอนเหล่านี้ ทีมจะนําข้อจํากัดทั้งหมดออกและเคารพความคิดสร้างสรรค์ของวิศวกร ซึ่งหมายความว่าการใช้งานที่ส่งผลต่อความเร็วควรได้รับการพิจารณา AirSHIFT ได้แบ่งกลุ่มออกเป็นทีมเล็กๆ เพื่อให้การแฮ็กคอลตันสามารถดำเนินการได้เร็วขึ้น และแต่ละทีมจะแข่งขันกันเพื่อดูว่าใครจะปรับปรุงคะแนนประสิทธิภาพ Lighthouse ได้มากที่สุด แต่ละทีมแข่งขันกันอย่างดุเดือด 🔥
ผลลัพธ์
แนวทางแฮ็กกาธอนได้ผลดีกับพวกเขา
- คุณตรวจหาจุดคอขวดด้านประสิทธิภาพได้ง่ายๆ ด้วยการลองใช้แนวทางต่างๆ จริงในระหว่างแฮ็กแฮตทอนและวัดผลแต่ละแนวทางด้วย Lighthouse
- หลังจากการแฮ็กคาธอน เราโน้มน้าวให้ทีมเห็นด้วยว่าควรให้ความสำคัญกับการเพิ่มประสิทธิภาพใดสำหรับเวอร์ชันที่ใช้งานจริง
- และยังเป็นการแสดงให้เห็นถึงความสำคัญของความเร็วอย่างมีประสิทธิภาพ ผู้เข้าร่วมทุกคนจะเข้าใจความสัมพันธ์ระหว่างการเขียนโค้ดกับผลลัพธ์ด้านประสิทธิภาพ
ผลพลอยได้ที่ดีคือทีมวิศวกรคนอื่นๆ จำนวนมากภายใน Recruit สนใจแนวทางปฏิบัติจริงนี้ และตอนนี้ทีม AirSHIFT กำลังจัดแฮ็กแฮทแบบเร่งด่วนหลายรายการภายในบริษัท
สรุป
เส้นทางนี้ไม่ใช่เส้นทางที่ง่ายที่สุดสำหรับ AirSHIFT ในการทําการเพิ่มประสิทธิภาพเหล่านี้ แต่คุ้มค่าแน่นอน ตอนนี้ AirSHIFT โหลดตารางการเปลี่ยนภายใน 1.5 วินาทีเป็นค่ามัธยฐาน ซึ่งปรับปรุงประสิทธิภาพได้ 6 เท่าจากก่อนเริ่มโปรเจ็กต์
หลังจากเปิดตัวการเพิ่มประสิทธิภาพแล้ว ผู้ใช้รายหนึ่งกล่าวว่า
ขอขอบคุณอย่างยิ่งที่ทำให้ตารางกะทำงานโหลดเร็ว ตอนนี้การจัดตารางงานเป็นกะมีประสิทธิภาพมากขึ้น