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

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

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

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

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

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

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

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

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

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

สปินเนอร์ทำงานไม่หยุดในอุปกรณ์ระดับล่าง

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

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

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

1. สร้างเสมือนตารางขนาดใหญ่

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

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

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

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

ผลลัพธ์

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

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

2. การตรวจสอบโดยใช้ User Timing API

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

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

ส่วนการกําหนดเวลาของแผงประสิทธิภาพในเครื่องมือสําหรับนักพัฒนาเว็บใน Chrome
เหตุการณ์เกี่ยวกับเวลาของผู้ใช้ 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 เท่า)

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

ปัจจุบัน AirSHIFT กำลังสำรวจว่าสามารถโหลดคอมโพเนนต์อื่นๆ แบบ Lazy Load ได้หรือไม่ และสามารถส่งผ่านตรรกะเพิ่มเติมไปยัง Web Worker เพื่อลดการกระตุกได้หรือไม่

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

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

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

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

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

ผลลัพธ์

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

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

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

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

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

ผลลัพธ์

แนวทางแฮ็กกาธอนได้ผลดีกับพวกเขา

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

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

สรุป

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

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

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