8. Không tối ưu hóa một cách hấp tấp

Estimated reading: 8 minutes 291 views

Tóm tắt

Ngựa tốt không cần roi (tục ngữ Latin): Tối ưu hóa sớm không chỉ gây nghiện mà còn không hiệu quả. Quy tắc đầu tiên của tối ưu hóa: Không làm. Quy tắc thứ hai của tối ưu hóa (chỉ dành cho chuyên gia): Không làm vội. Hãy đo lường hai lần, tối ưu hóa một lần.

Thảo luận

Như [Stroustrup00] §6 đã trích dẫn một cách hào hứng:
“Tối ưu hóa sớm là gốc rễ của mọi điều xấu.” —Donald Knuth [trích lời Hoare]
“Tuy nhiên, chúng ta không thể bỏ qua hiệu suất.” —Jon Bentley

Cả Hoare, Knuth và Bentley đều đúng (xem thêm Mục 6 và 9).

Tối ưu hóa sớm là khi ta làm thiết kế hoặc mã nguồn trở nên phức tạp hơn, khó đọc hơn, chỉ để cải thiện hiệu suất mà không có nhu cầu hiệu suất thực sự được chứng minh (dựa trên đo lường thực tế và so sánh với mục tiêu). Việc tối ưu hóa này thường không mang lại giá trị thực tế và thậm chí thường không làm chương trình nhanh hơn.

Hãy luôn nhớ:

“Làm cho chương trình chạy đúng rồi mới làm nó nhanh sẽ dễ hơn rất nhiều so với việc làm chương trình nhanh rồi mới làm cho nó chạy đúng.”

Do đó, theo mặc định, đừng cố code mã nhanh ngay từ đầu. Hãy tập trung vào việc làm mã rõ ràng và dễ đọc nhất có thể (xem Mục 6). Mã rõ ràng sẽ dễ viết đúng hơn, dễ hiểu hơn, dễ tái cấu trúc hơn và dễ tối ưu hóa hơn sau này. Những việc phức tạp, bao gồm cả tối ưu hóa, có thể thêm vào sau và chỉ khi thực sự cần thiết.

Có 2 lý do mà tối ưu hóa sớm thường không làm chương trình nhanh hơn:

  • Lập trình viên thường đánh giá sai mã code như thế nào để gọn và nhanh hơn và đâu mới chính xác là điểm thắt cổ chai của mã. Điều này đúng với cả tác giả và cả với bạn!
    • Hệ thống máy tính hiện đại rất phức tạp, từ CPU với các đơn vị xử lý song song, bộ nhớ đệm, thực thi dự đoán…Trên hết những phần cứng này, trình biên dịch sẽ “phỏng đoán” để chuyển những mã code của bạn thành mã máy sao cho tốt nhất cho phần cứng.
    • Trên hết thảy những yếu tố phần cứng và trình biên dịch trên đó chính là sự “phỏng đoán của” lập trình viên. Nếu tối ưu hóa chỉ dựa trên phỏng đoán, kết quả thường không cải thiện nhiều. Vì vậy, tối ưu hóa cần được đo lường thực tế, và đo lường phải dựa trên mục tiêu tối ưu rõ ràng. Vì vậy chỉ khi được yêu cầu, hãy tập trung vào ưu tiên số 1 – viết mã code cho con người. (Nếu ai đó yêu cầu bạn tối ưu hoá, hãy yêu cầu bằng chứng.)
  • Mã code đươc thực thi bởi CPU nhưng nhiều chương trình hiện đại không bị giới hạn bởi CPU!
    • Trong các chương trình hiện đại, ngày càng có nhiều các chương trình bị giới hạn bởi bộ nhớ, mạng, ổ cứng hoặc dịch vụ bên ngoài (ví dụ: cơ sở dữ liệu, API). Tối ưu hóa mã trong các trường hợp này không giải quyết được vấn đề thực sự, điều đó cũng có nghĩa là lập trình viên đã lãng phí thời gian quý báu để cải thiện những gì không cần cải thiện thay vì tăng thêm giá trị bằng cách cải thiện những điểm trọng yếu khác.

Tất nhiên trong trường hợp bắt buộc phải tối ưu hoá, hãy tìm cách cải thiện thuật toán (xem Mục 7), đóng gói tối ưu hóa (ví dụ: trong hàm hoặc lớp, xem Mục 511), và thêm chú thích giải thích lý do cũng như thuật toán được sử dụng.

Một sai lầm của người mới là thường viết mã mới với sự ám ảnh về hiệu suất tối ưu, dẫn đến mã phức tạp và khó đọc (thường là kiểu spaghetti code). Ngay cả khi mã ban đầu đúng, nó vẫn sẽ rất khó bảo trì (xem Mục 6).

Một số thói quen, như truyền tham chiếu (xem Mục 25), sử dụng tiền tố ++ và — (xem Mục 28), là những thực hành tự nhiên và không nên bị xem là tối ưu hóa sớm. Chúng đơn giản là tránh những suy giảm hiệu suất không cần thiết (xem Mục 9).

Ví dụ

Nhiều lập trình viên sử dụng inline theo mặc định, cho rằng điều này giúp tối ưu hóa. Thực tế là:

  • Bộ đếm của trình phân tích hiệu suất (Profilers) có thể cho biết hàm nào nên được đánh dấu inline mà vẫn chưa được đánh dấu.
  • Nhưng Profilers rất tệ trong việc cho bạn biết những hàm nào bạn đã đánh dấu là inline nhưng không nên đánh dấu inline.
    Quá nhiều lập trình viên “inline theo mặc định” dưới danh nghĩa tối ưu hóa, gần như luôn luôn tăng độ phụ thuộc (coupling) để đổi lại một lợi ích không rõ ràng. (Thậm chí còn làm cho bạn nghĩ là việc viết inline có lợi cho trình biên dịch.)

Ngoại lệ

Trong lập trình thư viện: Khó dự đoán được các hoạt động nào sẽ được sử dụng trong mã code nhạy cảm với hiệu suất. Tuy nhiên, ngay cả tác giả thư viện cũng cần kiểm tra hiệu suất trên nhiều loại mã khách hàng trước khi cam kết tối ưu hóa.

Tham Khảo

[Bentley00] §6 • [Cline99] §13.01-09 • [Kernighan99] §7 • [Lakos96] §9.1.14 • [Meyers97] §33• [Murray93] §9.9-10, §9.13 • [StroustrupOO] §6 introduction • [Sutter00] §30, §46 • [Sutter02]
§12 • [Sutter04] §25

Link 

Leave a Comment

Chia sẻ:

8. Không tối ưu hóa một cách hấp tấp

Or copy link

CONTENTS