Tối ưu mã nguồn C/C++
Trong lập trình C/C++ hay bất cứ ngôn ngữ nào khác, việctối ưu mã lệnh để tăng tốc độ xử lý là rất quan trọng. Đặc biệt ứng dụng C/C++ thực hiện ở tầng dưới như tầng service hay tầng driver.
Dưới đây là một số thủ thật giúp tối ưu mã lệnh trong C/C++.
Contents
Công thức tính mức độ tối ưu
Khi lập trình tối ưu bạn cần nhớ công thức tính sau của Ahmdal (Ahmdal’s Law):

Trong đó:
- funccost: Tỉ lệ % thời gian thực thi của chương trình sử dụng hàm func.
- funcspeedup: Hệ số tăng tốc độ xử lý cho hàm,
Ví dụ: Bạn tối ưu hàm TriangleIntersect(). Hàm này chiếm 40% của chương trình. Sau khi bạn tối ưu, hàm này chạy nhanh gấp đôi. Áp dụng công thức tính như sau:

=> Như vậy ứng dụng của bạn sẽ chạy nhanh thêm 25%.
Theo như công thức này nên tập trung vào tối ưu các hàm/lớp có tần suất sử dụng nhiều
Lập trình đúng rồi mới tối ưu
Đầu tiên bạn phải cài đặt/lập trình đúng trước đã. Sau đó khi bạn biết hàm nào có tần suất gọi nhiều, bạn mới thực hiện tối ưu. Khi tối ưu code bạn cần tìm ra các nút thắt (bottlenecks) để loại bỏ nó. Có nhiều cách để thực hiện việc này:
- Tối ưu mã lệnh
- Cải tiến thuật toán
Nhiều lập trình viên tốt đã nói rằng: Họ thường phải mất ít lần tối ưu trong khi viết code.
Các lệnh rẽ nhánh (Branches), các bước nhảy (Jumps) là rất đắt. Tối ưu sử dụng chúng bất cứ khi nào có thể
- Các lời gọi hàm yêu cầu hai bước nhảy, thêm vào đó là các thao tác với ngăn xếp
- Ưu tiên phép lặp hơn là đệ quy
- Nên sử dụng các hàm inline cho các các hàm ngắn.
- Nên dịch chuyển các vòng lặp vào trong hàm. Ví dụ đoạn code dưới:
for(i=0;i<100;i++) DoSomething();
Nên thay bằng:
DoSomething() { for(i=0;i<100;i++) { ... } } - Với lệnh có chuỗi if...else if...else if...else if... dài yêu cầu rất nhiều bước nhảy cho trường hợp ở cuối chuỗi. Trường hợp này nếu có thể hãy chuyển sang câu lệnh switch, trình dịch sẽ tối ưu hóa bằng một bước nhảy thông qua kỹ thuật tra bảng. Trường hợp không thể chuyển sang lệnh switch, hãy để các điều kiện hay xảy ra ở đầu của chuỗi.
Hãy để ý thứ tự các chỉ số của mảng
- Mảng hai hay nhiều chiều được lưu trữ trong vùng nhớ một chiều. Điều này có nghĩa (Với C/C++), phần tử array[i][j] và array[i][j+1] nằm sát nhau, trong khi phần tử array[i][j] và array[i+1][j] thì ở xa nhau.
- Truy cập dữ liệu theo cách tuần tự trên bộ nhớ giúp đoạn mã của bạn chạy nhanh hơn.
- Với dòng CPU mới, khi tải dữ liệu từ bộ nhớ chính vào bộ nhớ cache, chúng sẽ không chỉ lấy đúng dữ liệu yêu cầu mà sẽ lấy khối dữ liệu chứa dữ liệu yêu cầu (Gọi là một cache line). Điều này có nghĩa là sau khi phần tử array[i][j] nằm trong bộ nhớ cache của CPU, thì phần tử array[i][j+1] có xác xuất ở trong bộ nhớ cache rất cao, trong khi phần tử array[i+ 1][j] thì có thể vẫn đang trong bộ nhớ chính.
Tránh/Giảm số lượng các biến cục bộ
Các biến cục bộ thường được lưu trữ trong stack. Tuy nhiên, nếu các biến cục bộ đủ ít, chúng có thể được lưu trữ trong các thanh ghi. Trong trường hợp này, các hàm thực thi nhanh hơn do tốc độ truy cập nhanh và hạn chế sử dụng stack.
Tối ưu các tham số của hàm
- Giảm số lượng tham số của hàm: Các tham số trong hàm cũng được lưu trữ trong stack. Vì thế giảm số lượng tham số của hàm sẽ giúp giảm thao tác lưu trữ trong vùng stack.
- Truyền cấu trúc dữ liệu thông qua tham chiếu, chứ không sử dụng giá trị: Việc này sẽ giúp giảm chi phí sao chép dữ liệu và cũng đồng thời giảm dữ liệu lưu trữ vùng stack.