Năm cách AirSHIFT cải thiện hiệu suất thời gian chạy của ứng dụng React

Nghiên cứu điển hình thực tế về việc tối ưu hoá hiệu suất của React SPA.

Kento Tsuji
Kento Tsuji
Satoshi Arai
Satoshi Arai
Yusuke Utsunomiya
Yusuke Utsunomiya
Yosuke Furukawa
Yosuke Furukawa

Hiệu suất trang web không chỉ là thời gian tải. Điều quan trọng là phải mang đến cho người dùng trải nghiệm nhanh và nhạy, đặc biệt là đối với các ứng dụng máy tính giúp nâng cao năng suất mà mọi người sử dụng hằng ngày. Nhóm kỹ sư tại Recruit Technologies đã thực hiện một dự án tái cấu trúc để cải thiện một trong các ứng dụng web của họ, AirSHIFT, nhằm cải thiện hiệu suất đầu vào của người dùng. Sau đây là cách họ thực hiện.

Phản hồi chậm, hiệu suất thấp

AirSHIFT là một ứng dụng web dành cho máy tính giúp chủ cửa hàng (như nhà hàng và quán cà phê) quản lý ca làm việc của nhân viên. Được xây dựng bằng React, ứng dụng một trang này cung cấp các tính năng phong phú cho ứng dụng khách, bao gồm nhiều bảng lưới về lịch làm việc theo ngày, tuần, tháng và nhiều thông tin khác.

Ảnh chụp màn hình ứng dụng web AirSHIFT.

Khi nhóm kỹ sư của Recruit Technologies thêm các tính năng mới vào ứng dụng AirSHIFT, họ bắt đầu nhận được nhiều ý kiến phản hồi hơn về hiệu suất chậm. Yosuke Furukawa, Giám đốc kỹ thuật của AirSHIFT, cho biết:

Trong một nghiên cứu về người dùng, chúng tôi đã rất ngạc nhiên khi một trong những chủ cửa hàng nói rằng cô sẽ rời khỏi chỗ ngồi để pha cà phê sau khi nhấp vào một nút, chỉ để giết thời gian chờ bảng ca làm việc tải.

Sau khi nghiên cứu, nhóm kỹ sư nhận thấy nhiều người dùng đang cố gắng tải các bảng thay đổi lớn trên máy tính có thông số kỹ thuật thấp, chẳng hạn như máy tính xách tay Celeron M 1 GHz từ 10 năm trước.

Vòng quay vô hạn trên các thiết bị cấp thấp.

Ứng dụng AirSHIFT đang chặn luồng chính bằng các tập lệnh tốn kém, nhưng nhóm kỹ sư không nhận ra các tập lệnh tốn kém như thế nào vì chúng đang phát triển và thử nghiệm trên các máy tính có thông số kỹ thuật phong phú với kết nối Wi-Fi nhanh.

Biểu đồ cho thấy hoạt động trong thời gian chạy của ứng dụng.
Khi tải bảng ca, khoảng 80% thời gian tải đã bị tiêu thụ bởi các tập lệnh đang chạy.

Sau khi phân tích hiệu suất trong Công cụ của Chrome cho nhà phát triển với CPU và tính năng điều tiết mạng được bật, họ nhận thấy rõ ràng rằng cần phải tối ưu hoá hiệu suất. AirSHIFT đã thành lập một nhóm chuyên trách để giải quyết vấn đề này. Sau đây là 5 điều mà họ tập trung vào để giúp ứng dụng phản hồi tốt hơn với hoạt động đầu vào của người dùng.

1. Ảo hoá các bảng lớn

Việc hiển thị bảng ca làm việc đòi hỏi nhiều bước tốn kém: tạo DOM ảo và hiển thị trên màn hình theo tỷ lệ với số lượng nhân viên và khung giờ. Ví dụ: nếu một nhà hàng có 50 nhân viên làm việc và muốn kiểm tra lịch làm việc hằng tháng của họ, thì đó sẽ là một bảng gồm 50 (nhân viên) nhân với 30 (ngày), dẫn đến 1.500 thành phần ô để hiển thị. Đây là một thao tác rất tốn kém, đặc biệt là đối với các thiết bị có thông số kỹ thuật thấp. Thực tế thì mọi thứ còn tệ hơn. Qua nghiên cứu, họ nhận thấy có những cửa hàng quản lý 200 nhân viên, cần khoảng 6.000 thành phần ô trong một bảng hằng tháng.

Để giảm chi phí của thao tác này, AirSHIFT đã ảo hoá bảng ca làm việc. Ứng dụng hiện chỉ gắn các thành phần trong khung nhìn và tháo các thành phần ngoài màn hình.

Ảnh chụp màn hình có chú thích minh hoạ rằng AirSHIFT dùng để hiển thị nội dung bên ngoài khung nhìn.
Trước: Hiển thị tất cả các ô trong bảng di chuyển.
Ảnh chụp màn hình có chú thích minh hoạ rằng AirSHIFT hiện chỉ hiển thị nội dung xuất hiện trong khung nhìn.
Sau: Chỉ hiển thị các ô trong khung nhìn.

Trong trường hợp này, AirSHIFT đã sử dụng react-virtualized vì có các yêu cầu về việc bật bảng lưới hai chiều phức tạp. Họ cũng đang khám phá các cách chuyển đổi cách triển khai để sử dụng react-window gọn nhẹ trong tương lai.

Kết quả

Chỉ riêng việc ảo hoá bảng đã giúp giảm thời gian viết tập lệnh xuống 6 giây (trong tốc độ làm chậm CPU gấp 4 lần + môi trường Macbook Pro được điều tiết 3G nhanh). Đây là điểm cải thiện hiệu suất có tác động mạnh mẽ nhất trong dự án tái cấu trúc.

Ảnh chụp màn hình có chú thích của bản ghi bảng điều khiển Hiệu suất Công cụ của Chrome cho nhà phát triển.
Trước: Khoảng 10 giây tập lệnh sau khi người dùng nhập.
Một ảnh chụp màn hình khác có chú thích về bản ghi của bảng điều khiển Hiệu suất trong Chrome DevTools.
Sau: 4 giây lập trình sau khi người dùng nhập.

2. Kiểm tra bằng API Thời gian của người dùng

Tiếp theo, nhóm AirSHIFT đã tái cấu trúc các tập lệnh chạy trên dữ liệu đầu vào của người dùng. Biểu đồ hình ngọn lửa của Công cụ của Chrome cho nhà phát triển cho phép bạn phân tích những gì đang thực sự diễn ra trong luồng chính. Tuy nhiên, nhóm AirSHIFT nhận thấy việc phân tích hoạt động của ứng dụng dựa trên vòng đời của React sẽ dễ dàng hơn.

React 16 cung cấp dấu vết hiệu suất thông qua User Timing API (API Thời gian của người dùng). Bạn có thể trực quan hoá dấu vết này trong mục Thời gian của Công cụ của Chrome cho nhà phát triển. AirSHIFT đã sử dụng phần Timings (Thời gian) để tìm logic không cần thiết đang chạy trong các sự kiện trong vòng đời của React.

Mục Thời gian của bảng điều khiển Hiệu suất trong Công cụ của Chrome cho nhà phát triển.
Sự kiện Thời gian của người dùng trong React.

Kết quả

Đội ngũ AirSHIFT phát hiện ra rằng quá trình Điều chỉnh cây phản ứng một cách không cần thiết đã diễn ra ngay trước mỗi lần điều hướng tuyến đường. Điều này có nghĩa là React đang cập nhật bảng ca làm việc một cách không cần thiết trước khi điều hướng. Việc cập nhật trạng thái Redux không cần thiết đã gây ra sự cố này. Việc khắc phục lỗi này đã tiết kiệm khoảng 750 mili giây thời gian tập lệnh. AirSHIFT cũng thực hiện các hoạt động tối ưu hoá vi mô khác, giúp giảm tổng thời gian viết tập lệnh 1 giây.

3. Tải từng phần các thành phần và di chuyển logic tốn kém sang trình thực thi web

AirSHIFT có một ứng dụng trò chuyện tích hợp. Nhiều chủ cửa hàng giao tiếp với nhân viên của họ qua tính năng trò chuyện trong khi xem bảng ca làm việc. Điều này có nghĩa là người dùng có thể đang nhập tin nhắn trong khi bảng đang tải. Nếu luồng chính đang chứa các tập lệnh đang hiển thị bảng, thì hoạt động đầu vào của người dùng có thể bị giật.

Để cải thiện trải nghiệm này, AirSHIFT hiện sử dụng React.lazy và Suspense để hiển thị phần giữ chỗ cho nội dung bảng trong khi tải từng phần các thành phần thực tế.

Nhóm AirSHIFT cũng đã di chuyển một số logic nghiệp vụ tốn kém trong các thành phần tải lười sang worker web. Cách này đã giải quyết được vấn đề giật do hoạt động đầu vào của người dùng bằng cách giải phóng luồng chính để có thể tập trung vào việc phản hồi hoạt động đầu vào của người dùng.

Thông thường, nhà phát triển gặp phải sự phức tạp khi sử dụng worker, nhưng lần này Comlink đã giúp họ giải quyết vấn đề này. Dưới đây là mã giả về cách AirSHIFT tạo worker cho một trong những hoạt động tốn kém nhất mà họ từng thực hiện: tính tổng chi phí nhân công.

Trong App.js, hãy sử dụng React.lazy và Suspense để hiển thị nội dung dự phòng trong khi tải

/** 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>
  )
}

Trong thành phần Chi phí, hãy sử dụng đường liên kết comlink để thực thi logic 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>;
}

Triển khai logic tính toán chạy trong worker và hiển thị logic đó bằng liên kết 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);

Kết quả

Mặc dù lượng logic mà họ đã thử nghiệm có giới hạn, nhưng AirSHIFT đã chuyển khoảng 100 mili giây JavaScript từ luồng chính sang luồng worker (mô phỏng bằng cách điều tiết CPU gấp 4 lần).

Ảnh chụp màn hình bản ghi của bảng điều khiển Hiệu suất trong Chrome DevTools cho thấy rằng quá trình tập lệnh hiện đang diễn ra trên một worker web thay vì luồng chính.

AirSHIFT hiện đang tìm hiểu xem liệu có thể tải lười các thành phần khác và giảm tải thêm logic cho trình chạy web để giảm thiểu hiện tượng giật hay không.

4. Đặt ngân sách hiệu suất

Sau khi triển khai tất cả các biện pháp tối ưu hoá này, điều quan trọng là phải đảm bảo rằng ứng dụng vẫn hoạt động hiệu quả theo thời gian. AirSHIFT hiện sử dụng bundlesize để không vượt quá kích thước tệp JavaScript và CSS hiện tại. Ngoài việc thiết lập các ngân sách cơ bản này, họ còn tạo một trang tổng quan để hiển thị nhiều phần trăm thời gian tải bảng ca làm việc nhằm kiểm tra xem ứng dụng có hoạt động hiệu quả ngay cả trong điều kiện không lý tưởng hay không.

  • Thời gian hoàn thành tập lệnh cho mỗi sự kiện Redux hiện được đo lường
  • Dữ liệu hiệu suất được thu thập trong Elasticsearch
  • Hiệu suất phân vị thứ 10, 25, 50 và 75 của mỗi sự kiện được hiển thị bằng Kibana

AirSHIFT hiện đang theo dõi sự kiện tải bảng ca để đảm bảo sự kiện này hoàn tất trong 3 giây đối với người dùng ở phân vị thứ 75. Hiện tại, đây là mức ngân sách chưa được thực thi, nhưng họ đang xem xét việc nhận thông báo tự động thông qua Elasticsearch khi họ vượt quá ngân sách.

Biểu đồ cho thấy bách phân vị thứ 75 hoàn tất trong khoảng 2500 mili giây, bách phân vị thứ 50 hoàn tất trong khoảng 1250 mili giây, bách phân vị thứ 25 hoàn tất trong khoảng 750 mili giây và bách phân vị thứ 10 hoàn tất trong khoảng 500 mili giây.
Trang tổng quan Kibana cho thấy dữ liệu hiệu suất hằng ngày theo phân vị.

Kết quả

Từ biểu đồ trên, bạn có thể thấy rằng AirSHIFT hiện chủ yếu đạt ngân sách 3 giây cho người dùng phân vị thứ 75 và cũng tải bảng ca trong vòng một giây cho người dùng phân vị thứ 25. Bằng cách thu thập dữ liệu hiệu suất RUM từ nhiều điều kiện và thiết bị, giờ đây, AirSHIFT có thể kiểm tra xem bản phát hành tính năng mới có thực sự ảnh hưởng đến hiệu suất của ứng dụng hay không.

5. Sự kiện hackathon về hiệu suất

Mặc dù tất cả những nỗ lực tối ưu hoá hiệu suất này đều quan trọng và có tác động, nhưng không phải lúc nào các nhóm kỹ thuật và kinh doanh cũng dễ dàng ưu tiên phát triển không chức năng. Một phần thách thức là không thể lập kế hoạch cho một số hoạt động tối ưu hoá hiệu suất này. Bạn cần thử nghiệm và có tư duy thử-sai-lặp lại.

AirSHIFT hiện đang tổ chức các cuộc thi lập trình kéo dài 1 ngày về hiệu suất nội bộ để các kỹ sư chỉ tập trung vào công việc liên quan đến hiệu suất. Trong các cuộc thi lập trình này, họ loại bỏ mọi quy tắc ràng buộc và tôn trọng sự sáng tạo của các kỹ sư, tức là mọi phương pháp triển khai góp phần tăng tốc độ đều đáng được cân nhắc. Để đẩy nhanh quá trình tổ chức hackathon, AirSHIFT chia nhóm thành các nhóm nhỏ và mỗi nhóm sẽ cạnh tranh để xem nhóm nào có thể cải thiện điểm hiệu suất Lighthouse nhiều nhất. Các đội sẽ cạnh tranh rất quyết liệt! 🔥

Ảnh chụp sự kiện hackathon.

Kết quả

Phương pháp hackathon mang lại hiệu quả cho họ.

  • Bạn có thể dễ dàng phát hiện điểm tắc nghẽn về hiệu suất bằng cách thực sự thử nhiều phương pháp trong suốt cuộc thi hackathon và đo lường từng phương pháp bằng Lighthouse.
  • Sau cuộc thi lập trình, tôi dễ dàng thuyết phục được nhóm về việc họ nên ưu tiên tối ưu hoá nào cho bản phát hành chính thức.
  • Đây cũng là một cách hiệu quả để ủng hộ tầm quan trọng của tốc độ. Mọi người tham gia đều có thể hiểu được mối tương quan giữa cách bạn lập trình và hiệu suất của mã.

Một tác dụng phụ tốt là nhiều nhóm kỹ sư khác trong Recruit quan tâm đến phương pháp thiết thực này và đội ngũ AirSHIFT hiện đang tổ chức nhiều cuộc thi hackathon nhanh trong công ty.

Tóm tắt

Đây chắc chắn không phải là hành trình dễ dàng nhất để AirSHIFT thực hiện những tối ưu hóa này, nhưng chắc chắn nó đã được đền đáp. Giờ đây, AirSHIFT đang tải bàn chuyển trong vòng 1,5 giây với trung vị, tức là cải thiện gấp 6 lần so với hiệu suất của họ trước dự án.

Sau khi các tính năng tối ưu hoá hiệu suất ra mắt, một người dùng đã nói:

Cảm ơn bạn rất nhiều vì đã giúp bảng ca làm việc tải nhanh. Giờ đây, việc sắp xếp công việc theo ca đã trở nên hiệu quả hơn nhiều.