Tối ưu sớm là gốc rễ của mọi tội lỗi? Hiểu đúng về premature optimization

  • author-image

    Nguyễn Đình Cường

  • blog-tag
  • blog-comment 0 comment
  • created-date 09 Nov, 2026
blog-thumbnail

Câu nói nổi tiếng "premature optimization is the root of all evil" (tối ưu sớm là gốc rễ của mọi điều xấu) được gắn với nhà khoa học máy tính Donald Knuth. Nhưng nhiều người trích dẫn nó thiếu ngữ cảnh. Bài này giúp bạn hiểu ý nghĩa thật sự: khi nào nên tối ưu, khi nào nên chờ.

Tối ưu sớm là gì?

Đó là việc bỏ công làm code nhanh hơn hoặc tiết kiệm bộ nhớ hơn trước khi bạn biết nó có phải vấn đề thật hay không. Biểu hiện thường gặp:

  • Viết cấu trúc dữ liệu phức tạp "phòng khi dữ liệu lớn" trong khi hiện chỉ có vài chục bản ghi.
  • Dùng thủ thuật bit hay vi tối ưu làm code khó đọc để tiết kiệm vài nano giây.
  • Thêm lớp cache cho một truy vấn chỉ chạy một lần mỗi ngày.
  • Chia nhỏ hệ thống thành vi dịch vụ khi dự án chưa có người dùng.

Vì sao nó có hại?

  1. Làm code khó hiểu: code tối ưu thường phức tạp, khó đọc, khó sửa, dễ sinh lỗi.
  2. Tốn thời gian vào chỗ không cần: phần lớn thời gian chạy dồn ở một phần nhỏ của chương trình. Tối ưu nơi khác không cải thiện được gì đáng kể.
  3. Đoán sai nút thắt: trực giác về cái gì chậm thường sai. Chỉ đo đạc mới cho câu trả lời.
  4. Cản trở thay đổi: yêu cầu hay đổi, một thiết kế quá chuyên biệt có thể trở thành gánh nặng khi hướng đi thay đổi.
  5. Trì hoãn việc giao sản phẩm: bạn có thể mất cả tuần cho một cải thiện mà người dùng không để ý.

Nhưng đừng hiểu thành "không cần quan tâm hiệu năng"

Trích dẫn đầy đủ của Knuth nói rằng nên quên đi những cải thiện nhỏ nhặt trong khoảng 97% thời gian, nhưng không được bỏ qua cơ hội trong 3% còn lại, những phần thật sự quan trọng. Nghĩa là: đừng tối ưu bừa, nhưng cũng đừng viết code chậm một cách thiếu suy nghĩ.

Có những quyết định hiệu năng nên cân nhắc ngay từ đầu vì khó sửa sau này:

  • Chọn thuật toán và cấu trúc dữ liệu hợp lý: tìm kiếm tuần tự trong danh sách lớn so với dùng bảng băm có thể chênh nhau hàng nghìn lần.
  • Thiết kế database: chỉ mục (index), tránh truy vấn N+1.
  • Kiến trúc tổng thể: giao tiếp mạng, cách chia nhỏ hệ thống.
  • Hợp đồng API: phân trang thay vì trả cả triệu bản ghi.

Ví dụ: code dễ đọc trước, đo sau

// Phiên bản dễ hiểufunction getActiveUserNames(users) {return users.filter(user => user.isActive).map(user => user.name);}// Phiên bản "tối ưu" một lần duyệtfunction getActiveUserNames(users) {const names = [];for (let i = 0; i < users.length; i++) {if (users[i].isActive) names.push(users[i].name);}return names;}

Với vài nghìn người dùng, sự khác biệt thường không đáng kể và phiên bản đầu dễ đọc hơn. Chỉ khi đo thấy đây là điểm nghẽn thật sự thì mới đáng đổi.

Vòng lặp tối ưu đúng cách

  1. Làm cho nó chạy đúng: ưu tiên đúng và dễ hiểu, có test.
  2. Đặt mục tiêu hiệu năng cụ thể: "trang tải dưới 2 giây", "API trả lời dưới 300ms ở 95% yêu cầu".
  3. Đo đạc: dùng profiler, log thời gian, công cụ giám sát để tìm đúng nút thắt.
  4. Tối ưu chỗ đo được: thay đổi một thứ mỗi lần.
  5. Đo lại: xác nhận có cải thiện thật, không làm hỏng đúng đắn (chạy lại test).
  6. Dừng khi đạt mục tiêu. Cải thiện thêm tốn kém và ít giá trị.

Các câu hỏi tự kiểm tra trước khi tối ưu

  • Có số liệu chứng minh đây là nút thắt không?
  • Người dùng hoặc hệ thống có thực sự bị ảnh hưởng không?
  • Cải thiện kỳ vọng là bao nhiêu và đáng đánh đổi độ phức tạp không?
  • Có cách đơn giản hơn không, như cache, thêm chỉ mục, giảm số lần gọi?
  • Code sau khi tối ưu có còn được đồng đội hiểu và bảo trì không?

Một số tối ưu "rẻ" nên làm sớm

  • Tránh làm việc thừa trong vòng lặp (ví dụ gọi database mỗi vòng).
  • Dùng cấu trúc dữ liệu phù hợp (tập hợp để kiểm tra tồn tại thay vì duyệt mảng).
  • Thêm chỉ mục cho cột thường tìm kiếm hay sắp xếp.
  • Phân trang và giới hạn dữ liệu trả về.
  • Nén tài nguyên tĩnh, bật cache của trình duyệt cho web.

Những điều này gần như không làm code phức tạp thêm mà mang lại lợi ích rõ ràng.

Kết luận

Hãy viết code đúng, rõ ràng trước; lựa chọn thuật toán và thiết kế hợp lý ngay từ đầu; còn vi tối ưu thì chờ số liệu đo đạc quyết định. Như thế bạn vừa tránh được code phức tạp không cần thiết, vừa không bỏ lỡ những điểm đáng tối ưu thật.

Muốn hiểu sâu về thuật toán và hiệu năng, bạn có thể xem các khóa học liên quan tại codebem.com.

author_photo
Nguyễn Đình Cường

0 comment