กรณีศึกษาในชีวิตจริงเกี่ยวกับการเพิ่มประสิทธิภาพ 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 มิติที่ซับซ้อน นอกจากนี้ พวกเขายังสำรวจวิธีแปลงการติดตั้งใช้งานเป็นหน้าต่างรีแอคทีฟขนาดเล็กในอนาคตด้วย
ผลลัพธ์
การทำให้เป็นเสมือนของตารางเพียงอย่างเดียวทำให้เวลาในการเขียนสคริปต์ลดลง 6 วินาที (ในสภาพแวดล้อม Macbook Pro ที่ชะลอความเร็ว CPU ลง 4 เท่าและ 3G ที่เร็ว) นี่เป็นการเพิ่มประสิทธิภาพที่ส่งผลมากที่สุดในโปรเจ็กต์การจัดระเบียบใหม่
2. การตรวจสอบโดยใช้ User Timing API
ต่อมา ทีม AirSHIFT ได้เปลี่ยนโครงสร้างภายในสคริปต์ที่ทำงานตามอินพุตของผู้ใช้ แผนภูมิเปลวไฟของเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome ช่วยให้วิเคราะห์สิ่งที่เกิดขึ้นจริงในเทรดหลักได้ แต่ทีม AirSHIFT พบว่าการวิเคราะห์กิจกรรมของแอปพลิเคชันตามวงจรชีวิตของ React ทำได้ง่ายกว่า
React 16 มีการติดตามประสิทธิภาพผ่าน User Timing API ซึ่งดูได้จากส่วนการจับเวลา ของ Chrome DevTools 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 เท่าจากก่อนเริ่มโปรเจ็กต์
หลังจากเปิดตัวการเพิ่มประสิทธิภาพแล้ว ผู้ใช้รายหนึ่งกล่าวว่า
ขอขอบคุณอย่างยิ่งที่ทำให้ตารางกะทำงานโหลดเร็ว ตอนนี้การจัดตารางงานกะมีประสิทธิภาพมากขึ้น