5 วิธีที่ AirSHIFT ได้ปรับปรุงประสิทธิภาพรันไทม์ของแอป React

กรณีศึกษาที่เกิดขึ้นจริงเกี่ยวกับการเพิ่มประสิทธิภาพ SPA ของ React

Kento Tsuji
Kento Tsuji
Satoshi Arai
Satoshi Arai
Yosuke Furukawa
Yosuke Furukawa

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

ตอบสนองช้า ประสิทธิภาพการทำงานลดลง

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

ภาพหน้าจอของเว็บแอป AirSHIFT

เมื่อทีมวิศวกรของ Recruit Technologies เพิ่มฟีเจอร์ใหม่ในแอป AirSHIFT พวกเขาก็เริ่มได้รับความคิดเห็นเพิ่มเติมเกี่ยวกับประสิทธิภาพที่ช้า Yosuke Furukawa ผู้จัดการฝ่ายวิศวกรรมของ AirSHIFT กล่าวว่า

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

หลังจากทำการวิจัยแล้ว ทีมวิศวกรก็พบว่าผู้ใช้จำนวนมากพยายามโหลดตาราง Shift ขนาดใหญ่ในคอมพิวเตอร์ที่มีสเปกต่ำ เช่น แล็ปท็อป 1 GHz Celeron M จากเมื่อ 10 ปีที่แล้ว

ไอคอนหมุนไม่รู้จบบนอุปกรณ์ระดับโลว์เอนด์

แอป AirSHIFT บล็อกเทรดหลักด้วยสคริปต์ราคาแพง แต่ทีมวิศวกรไม่รู้ว่าสคริปต์เหล่านั้นมีราคาแพงขนาดไหนเพราะสคริปต์เหล่านั้นกำลังพัฒนาและทดสอบคอมพิวเตอร์สเปคสูงขนาดใหญ่ที่มีการเชื่อมต่อ Wi-Fi ที่รวดเร็ว

แผนภูมิที่แสดงกิจกรรมรันไทม์ของแอป
การเรียกใช้สคริปต์ใช้เวลาในการโหลดตาราง Shift ประมาณ 80%

หลังจากโปรไฟล์ประสิทธิภาพใน Chrome DevTools โดยใช้การควบคุม CPU และเครือข่าย เป็นที่แน่ชัดว่าจำเป็นต้องเพิ่มประสิทธิภาพ AirSHIFT จัดตั้งหน่วยปฏิบัติการเพื่อแก้ไขปัญหานี้ ต่อไปนี้คือ 5 สิ่งที่ทีมมุ่งเน้นเพื่อให้แอปตอบสนองต่อข้อมูลจากผู้ใช้ได้มากขึ้น

1. จำลองตารางขนาดใหญ่

การแสดงตาราง Shift ต้องใช้ขั้นตอนที่มีราคาสูงหลายขั้นตอน เช่น การสร้าง DOM เสมือนและแสดงผลบนหน้าจอตามสัดส่วนจำนวนเจ้าหน้าที่และช่วงเวลา เช่น หากร้านอาหารมีสมาชิกที่ทำงานอยู่ 50 คนและต้องการตรวจสอบกำหนดกะการทำงานรายเดือน ตารางดังกล่าวจะเป็นตารางที่มี 50 (สมาชิก) คูณด้วย 30 (วัน) ซึ่งจะทำให้เกิดคอมโพเนนต์เซลล์ 1,500 เซลล์ในการแสดงผล มีการดำเนินการที่มีราคาแพงมาก โดยเฉพาะสำหรับอุปกรณ์ที่มีสเปคต่ำ ในความเป็นจริง สิ่งต่างๆ แย่ลง จากการวิจัยพบว่า มีร้านค้าที่ต้องจัดการพนักงาน 200 คน ซึ่งต้องใช้ส่วนประกอบเซลล์ประมาณ 6,000 ชิ้นในตารางเดียวต่อเดือน

AirSHIFT ได้จัดตาราง Shift ให้อยู่ในรูปแบบเสมือนจริงเพื่อลดต้นทุนในการดำเนินการนี้ ตอนนี้แอปจะต่อเชื่อมเฉพาะคอมโพเนนต์ภายในวิวพอร์ตและยกเลิกการต่อเชื่อมคอมโพเนนต์ที่ออกนอกหน้าจอ

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

ในกรณีนี้ AirSHIFT ใช้ react-virtualized เนื่องจากมีข้อกำหนดในการเปิดใช้ตารางตารางกริด 2 มิติที่ซับซ้อน นอกจากนี้ ยังสำรวจวิธีเปลี่ยนรูปแบบการใช้งานดังกล่าวให้ใช้กรอบเวลารีแอ็กชันแบบเบาๆ ในอนาคตด้วย

ผลลัพธ์

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

ภาพหน้าจอที่มีคำอธิบายประกอบของการบันทึกแผงประสิทธิภาพของเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome
ก่อน: ประมาณ 10 วินาทีของการเขียนสคริปต์หลังจากที่ผู้ใช้ป้อนข้อมูล
ภาพหน้าจออื่นที่มีคำอธิบายประกอบของการบันทึกแผงประสิทธิภาพของเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome
หลังจาก: 4 วินาทีของการเขียนสคริปต์หลังจากที่ผู้ใช้ป้อนข้อมูล

2. ตรวจสอบด้วย User Timing API

ต่อมา ทีม AirSHIFT ได้เปลี่ยนโครงสร้างสคริปต์ที่ทำงานตามข้อมูลของผู้ใช้ แผนภูมิ Flame ของเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome ช่วยให้วิเคราะห์สิ่งที่เกิดขึ้นจริงในเทรดหลักได้ แต่ทีม AirSHIFT พบว่าการวิเคราะห์กิจกรรมแอปพลิเคชันตามวงจรของ React นั้นทำได้ง่ายขึ้น

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

ส่วนการจับเวลาของแผงประสิทธิภาพของเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome
เหตุการณ์ระยะเวลาของผู้ใช้ของ React

ผลลัพธ์

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

3. โหลดคอมโพเนนต์แบบ Lazy Loading และย้ายตรรกะราคาแพงไปให้ผู้ปฏิบัติงานที่ทำงานบนเว็บ

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

เพื่อปรับปรุงประสบการณ์การใช้งานนี้ ตอนนี้ AirSHIFT ใช้ React.lazy และ Suspense เพื่อแสดงตัวยึดตำแหน่งสำหรับสารบัญพร้อมกับโหลดคอมโพเนนต์จริงแบบ Lazy Loading

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

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

ใน 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 ในคอมโพเนนต์ต้นทุนเพื่อเรียกใช้ตรรกะ Calc

/** 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 เท่า)

ภาพหน้าจอของแผงประสิทธิภาพของเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome ที่แสดงให้เห็นว่าขณะนี้การเขียนสคริปต์เกิดขึ้นใน Web Worker ไม่ใช่เทรดหลัก

ขณะนี้ AirSHIFT กำลังศึกษาว่าจะสามารถโหลดคอมโพเนนต์อื่นๆ แบบ Lazy Loading ได้ไหม และนำตรรกะออกไปใช้งาน Web Worker ได้มากขึ้นเพื่อลดการติดขัด

4. การตั้งงบประมาณด้านประสิทธิภาพ

เมื่อมีการนำการเพิ่มประสิทธิภาพเหล่านี้ไปใช้ ก็เป็นเรื่องสำคัญอย่างยิ่งที่จะดูแลให้แอปยังคงมีประสิทธิภาพต่อไปเมื่อเวลาผ่านไป ตอนนี้ AirSHIFT ใช้ bundlesize เพื่อไม่ให้เกินขนาดไฟล์ JavaScript และ CSS ปัจจุบัน นอกจากการตั้งงบประมาณพื้นฐานเหล่านี้แล้ว องค์กรยังสร้างแดชบอร์ดเพื่อแสดงเปอร์เซ็นต์ไทล์ต่างๆ ของเวลาที่ใช้ในการโหลดตาราง Shift เพื่อตรวจสอบว่าแอปพลิเคชันนั้นมีประสิทธิภาพแม้ในเงื่อนไขที่ไม่ดีที่สุดหรือไม่

  • ตอนนี้ระบบจะวัดเวลาเสร็จสมบูรณ์ของสคริปต์สำหรับทุกเหตุการณ์ Redux แล้ว
  • ระบบจะรวบรวมข้อมูลประสิทธิภาพไว้ใน Elasticsearch
  • ประสิทธิภาพเปอร์เซ็นไทล์ที่ 10, 25, 50 และ 75 ของแต่ละเหตุการณ์จะแสดงด้วย Kibana

ตอนนี้ AirSHIFT กำลังตรวจสอบเหตุการณ์การโหลดตาราง Shift เพื่อให้แน่ใจว่าดำเนินการเสร็จสิ้นภายใน 3 วินาทีสำหรับผู้ใช้เปอร์เซ็นไทล์ที่ 75 งบประมาณนี้เป็นงบประมาณที่ไม่ได้บังคับใช้ในขณะนี้ แต่ทางบริษัทกำลังพิจารณาที่จะส่งการแจ้งเตือนอัตโนมัติผ่าน Elasticsearch เมื่อใช้จ่ายเกินงบประมาณ

แผนภูมิแสดงว่าเปอร์เซ็นไทล์ที่ 75 เสร็จสมบูรณ์ภายในเวลาประมาณ 2,500 มิลลิวินาที, เปอร์เซ็นไทล์ที่ 50 ในช่วงประมาณ 1,250 มิลลิวินาที, เปอร์เซ็นไทล์ที่ 25 ในช่วงประมาณ 750 มิลลิวินาที และเปอร์เซ็นไทล์ที่ 10 ในเวลาประมาณ 500 มิลลิวินาที
แดชบอร์ด Kibana แสดงข้อมูลประสิทธิภาพรายวันตามเปอร์เซ็นไทล์

ผลลัพธ์

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

5. แฮ็กกาธอนด้านประสิทธิภาพ

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

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

รูปภาพแฮ็กกาธอน

ผลลัพธ์

วิธีแฮ็กกาธอนใช้ได้ผลกับองค์กรเหล่านี้เป็นอย่างดี

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

ผลข้างเคียงที่ดีคือทีมวิศวกรอื่นๆ มากมายภายใน Recruit สนใจแนวทางการลงมือปฏิบัตินี้ และทีม AirSHIFT ก็กำลังอำนวยความสะดวกให้กับแฮ็กกาธอนหลายความเร็วภายในบริษัท

สรุป

วิธีนี้ไม่ใช่เส้นทางที่ง่ายที่สุดในการที่ AirSHIFT ในการเพิ่มประสิทธิภาพเหล่านี้ แต่ก็ให้ผลตอบแทนที่คุ้มค่าอย่างแน่นอน ตอนนี้ AirSHIFT จะโหลดตาราง Shift ภายใน 1.5 วินาทีตามค่ามัธยฐาน ซึ่งนับว่ามีประสิทธิภาพมากขึ้น 6 เท่าจากช่วงก่อนดำเนินโปรเจ็กต์

หลังจากเปิดตัวการเพิ่มประสิทธิภาพ ผู้ใช้คนหนึ่งกล่าวว่า

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