chèn bài giảng sớm-thảo luận-là-gốc-của-tất cả-ác
Điều đó nói rằng, đây là một số thói quen tôi đã mắc phải để tránh hiệu quả không cần thiết, và trong một số trường hợp, làm cho mã của tôi đơn giản hơn và chính xác hơn.
Đây không phải là một cuộc thảo luận về các nguyên tắc chung, nhưng về một số điều cần lưu ý để tránh đưa sự thiếu hiệu quả không cần thiết vào mã.
Biết ông lớn của bạn
Điều này có lẽ nên được hợp nhất vào các cuộc thảo luận dài ở trên. Có một ý nghĩa khá phổ biến là một vòng lặp bên trong một vòng lặp, trong đó vòng lặp bên trong lặp lại một phép tính, sẽ chậm hơn. Ví dụ:
for (i = 0; i < strlen(str); i++) {
...
}
Điều này sẽ mất một lượng thời gian khủng khiếp nếu chuỗi thực sự dài, bởi vì độ dài đang được tính toán lại trên mỗi lần lặp của vòng lặp. Lưu ý rằng GCC thực sự tối ưu hóa trường hợp này vì strlen()
được đánh dấu là một hàm thuần túy.
Khi sắp xếp một triệu số nguyên 32 bit, sắp xếp bong bóng sẽ là cách sai . Nói chung, việc sắp xếp có thể được thực hiện trong thời gian O (n * log n) (hoặc tốt hơn, trong trường hợp sắp xếp cơ số), vì vậy trừ khi bạn biết dữ liệu của mình sẽ nhỏ, hãy tìm một thuật toán ít nhất là O (n * đăng nhập n).
Tương tự như vậy, khi làm việc với cơ sở dữ liệu, hãy chú ý đến các chỉ mục. Nếu bạn SELECT * FROM people WHERE age = 20
và bạn không có chỉ số về người (tuổi), thì nó sẽ yêu cầu quét tuần tự O (n) thay vì quét chỉ mục O (log n) nhanh hơn nhiều.
Phân cấp số nguyên
Khi lập trình bằng C, hãy nhớ rằng một số phép toán số học đắt hơn các phép toán khác. Đối với số nguyên, hệ thống phân cấp có dạng như thế này (ít tốn kém nhất trước tiên):
Cấp, trình biên dịch sẽ điều thường tối ưu hóa như n / 2
để n >> 1
tự động nếu bạn đang nhắm mục tiêu một máy tính chủ đạo, nhưng nếu bạn đang nhắm mục tiêu một thiết bị nhúng, bạn có thể không nhận được sang trọng.
Ngoài ra, % 2
và & 1
có ngữ nghĩa khác nhau. Phân chia và mô đun thường làm tròn về 0, nhưng nó được xác định. Tốt ol ' >>
và &
luôn luôn hướng về vô cực tiêu cực, mà (theo ý kiến của tôi) có ý nghĩa hơn rất nhiều. Chẳng hạn, trên máy tính của tôi:
printf("%d\n", -1 % 2); // -1 (maybe)
printf("%d\n", -1 & 1); // 1
Do đó, sử dụng những gì có ý nghĩa. Đừng nghĩ rằng bạn là một cậu bé tốt bằng cách sử dụng % 2
khi ban đầu bạn sẽ viết & 1
.
Hoạt động điểm nổi tốn kém
Tránh các hoạt động dấu phẩy động nặng như pow()
và log()
trong mã không thực sự cần chúng, đặc biệt là khi giao dịch với số nguyên. Lấy ví dụ, đọc một số:
int parseInt(const char *str)
{
const char *p;
int digits;
int number;
int position;
// Count the number of digits
for (p = str; isdigit(*p); p++)
{}
digits = p - str;
// Sum the digits, multiplying them by their respective power of 10.
number = 0;
position = digits - 1;
for (p = str; isdigit(*p); p++, position--)
number += (*p - '0') * pow(10, position);
return number;
}
Việc sử dụng pow()
(và int
<-> double
chuyển đổi cần thiết để sử dụng nó) khá tốn kém, nhưng nó tạo ra cơ hội cho việc mất độ chính xác (tình cờ, mã ở trên không có vấn đề chính xác). Đó là lý do tại sao tôi nhăn nhó khi thấy loại hàm này được sử dụng trong bối cảnh phi toán học.
Ngoài ra, hãy chú ý cách thuật toán "thông minh" bên dưới, nhân với 10 trên mỗi lần lặp, thực sự ngắn gọn hơn mã ở trên:
int parseInt(const char *str)
{
const char *p;
int number;
number = 0;
for (p = str; isdigit(*p); p++) {
number *= 10;
number += *p - '0';
}
return number;
}