Các bước cơ bản xác định và fix performance bugs

Trong lập trình đôi khi chúng ta gặp phải các bug liên quan đến performance, chiếm nhiều tài nguyên CPU, thời gian sử lý chậm, gây lag v.v... Chúng thường là các bug khó chịu và tốn nhiều thời gian để sử lý. Khi gặp những lỗi như vậy đòi hỏi chúng ta cần có một chiến lược hợp lý để có thể có những bước tiến rõ ràng xác định vị trí bug, nguyên nhân, cô lập vấn đề và giải quyết chúng. Theo mình, các bước hợp lý mà chúng ta nên làm theo là như sau:

1. Tạo môi trường test, chuẩn bị test case.

Đa số chúng ta thường không có thói quen chuẩn bị bước thứ nhất này, lý do cơ bản nhất là do chủ quan, không lường trước được sự khó khăn và phức tạp gặp phải khi đối mặt với các lỗi performance và lý do thứ hai là tâm lý muốn nhanh chóng fix xong bug, nghĩ rằng chỉ cần xử lý case hiện tại là đủ. Nhưng thực tế thường ngược lại, chúng ta sẽ mất rất nhiều thời gian chạy đi chạy lại đoạn code để xem xét và tìm kiếm vấn đề. Nếu chúng ta lờ đi bước thứ nhất thì việc chuẩn bị môi trường sẽ tốn rất nhiều thời gian dẫn tới mỗi ngày chúng ta chỉ chạy được vài lần chương trình để xác định và tìm kiếm nguyên nhân.

Chuẩn bị data test, chúng ta cần cân nhắc đến thời gian cần thiết để xử lý hết một chu kỳ của logic, lượng data cần hợp lý, để thời gian chờ không phải quá dài và cũng không có qúa ít data để logic có thể phản ánh chính xác tình trạng lỗi.

Nếu có file data dump chúng ta cũng cần phải phân tích file data dump từ đó lấy được mẫu dữ liệu và tình trạng objects chạy trong hệ thống lúc bug xảy ra.

2. Kiểm tra lại cách thức log và bổ sung các thông tin log cần thiết

Thêm một bước ngoài chuẩn bị ngoài rìa nữa, nhưng bước này sẽ không bao giờ là thừa, hãy đọc file log và chèn thêm đoạn code để log các thông tin mà bạn nghĩ là thiếu, vì trong khi hệ thống chạy thông qua break point chúng ta sẽ bỏ xót những thông tin cần thiết. Và còn một khả năng nữa là với những lỗi performance không phải lúc nào chúng ta cũng có thể tái hiện được trên môi trường dev. Vì vậy thông tin log là thật sự cần thiết và hãy kiểm tra lại file log để có cái nhìn tổng quan hơn, việc đọc file log cũng sẽ đơn giản hơn là việc theo dõi giá trị của object thông qua break point.

3. Xác định vị trí đoạn code dẫn đến lỗi

Bug liên quan đến thời gian sử lý chậm, tốn nhiều thời gian: Ở đây chúng ta cần xác định các yếu tố thời gian sử lý của từng method code, thời gian tổng chương trình sử dụng sử lý đoạn code đó, tần xuất gọi method v.v...

Khi xác định được các thông tin trên (có thể thông qua các tool hỗ trợ sẵn có, hoặc thông qua thông tin file logs), xác định tỉ lệ sử lý như vậy là bất thường hay không. Ví dụ hàm connect đến database được gọi quá nhiều và chiếm một khoảng thời gian lớn, hay hàm logic xử lý group data chiếm gần 50% thời gian của chương trình v.v... -> xác định được vị trí vấn đề xảy ra.

Tương tự với bug liên quan đến hệ thống chạy gốn quá nhiều bộ nhớ, chúng ta cần log và quan tâm đến trước và sau mỗi method thì hiện tại bộ nhớ tăng và giảm như thế nào, trong bộ nhớ còn những object nào đang hoạt động. Bạn có thể xem một ví dụ ở đây: Tool performance report

4. Cô lập bug

Mục tiêu là tác rời chức năng hoặc đoạn code có vấn đề ra khỏi chương trình, thực thi riêng đoạn code đó, để chắc chắn rằng vấn đề thực sự nằm ở đâu, cố gắng tách rời và đơn giản hóa vấn đề hết mức có thể.

5. Xử lý bug

  • Xác định số lần gọi đến các method là hợp lý hay chưa, ví dụ checkAuthen, thông thường chúng ta chỉ cần check 1 lần với một logic, nếu chúng ta thấy nó gọi đến 2 lần -> cần check lại logic condition, logic cho điều kiện gọi đến đoạn code đấy. Bỏ những lần gọi không cần thiết đi.
  • Xác định lại logic của đoạn mã xem có thể làm gắn gọn và loại bỏ những xử lý dư thừa khác hay không. Ở đây điều quan trọng chúng ta cần cân nhắc là chia nhỏ method phức tạp ra thành các sub-method và đảm bảo logic sử lý của từng method riêng biệt là cơ bản nhất và đảm bảo các chức năng khác nhau.
  • Xác định các đoạn code try-catch exception được sử dụng đã hợp lý hay chưa.
  • Trong điều kiện đoạn code đã là tối ưu rồi, nhưng vẫn gặp vấn đề về thời gian xử lý, thì cần chú ý đến việc bố trí chương trình sử dụng các kĩ thuật lazy load, hoặc sử lý và log trước kết quả vào file lưu trữ sẵn, v.v...

6. Bug vẫn không được sử lý thì làm sao

Sau khi qua 5 steps trên, hiện tại vấn đề đã hiện rõ nguyên hình, bài toán đưa ra được mô tả tương đối gọn nhẹ và dễ hiểu -> Trong trường hợp này thì việc tiếp theo chúng ta cần phải làm là trình bày vấn đề để tìm kiếm sự giúp đỡ về thuật toán và nếu thuật toán vẫn bó tay thì cơ bản là phải có sự thay đổi từ phần cứng, môi trường, lúc này trọng tâm là nêu chính xác vấn đề và giải thích đơn giản nhất để có được trợ giúp từ bên ngoài (nếu có) hoặc ít nhất mọi người hiểu được chính xác rắc rối ở đây là gì.

Trường hợp xấu nhất là người trợ giúp sẽ kế thừa được các bước 1, 2, 3 và 4. Tất nhiên mình hy vọng chúng ta sẽ không bao giờ phải rơi vào step 6 này. Chúc các bạn may mắn và thành công và nhớ chia sẻ với các thiếu sót của bài viết nhé. Thank you very much.