вставити передчасну дискусію-це-корінь-все-лихої лекції
З цього приводу, ось деякі звички я вжив, щоб уникнути зайвої ефективності, а в деяких випадках і зробити свій код простішим і правильнішим.
Це не обговорення загальних принципів, а деяких речей, які слід пам’ятати, щоб уникнути зайвих неефективностей у код.
Це, мабуть, має бути об'єднане у тривалу дискусію вище. Це досить здоровий глузд, що цикл всередині циклу, де внутрішня петля повторює обчислення, буде повільніше. Наприклад:
for (i = 0; i < strlen(str); i++) {
...
}
Це займе жахливу кількість часу, якщо рядок дійсно довгий, тому що довжина перераховується під час кожної ітерації циклу. Зауважте, що GCC фактично оптимізує цей випадок, оскільки strlen()
позначений як чиста функція.
При сортуванні мільйона 32-бітних цілих чисел сортування міхурів було б неправильним шляхом . Взагалі, сортування може бути виконано за часом O (n * log n) (або, краще, у разі радіусного сортування), тому, якщо ви не знаєте, що ваші дані будуть невеликими, шукайте алгоритм, який принаймні O (n * журнал n).
Так само, працюючи з базами даних, будьте в курсі індексів. Якщо у вас немає SELECT * FROM people WHERE age = 20
, а у вас немає індексу людей (вік), знадобиться O (n) послідовне сканування, а не набагато швидше O (log n) сканування індексу.
Цілісна арифметична ієрархія
При програмуванні на C майте на увазі, що деякі арифметичні операції дорожчі інших. Для цілих чисел ієрархія має щось подібне (найменш дороге спочатку):
Звичайно, компілятор зазвичай Оптимізувати речі , як n / 2
до n >> 1
автоматично , якщо ви орієнтуєтеся на основний комп'ютер, але якщо ви орієнтуєтеся вбудований пристрій, ви можете не отримати таку розкіш.
Також % 2
і & 1
мають різну семантику. Ділення та модуль зазвичай округляються до нуля, але це визначено реалізацією. Добре >>
і &
завжди кругляє до негативної нескінченності, що (на мій погляд) має набагато більше сенсу. Наприклад, на моєму комп’ютері:
printf("%d\n", -1 % 2); // -1 (maybe)
printf("%d\n", -1 & 1); // 1
Отже, використовуйте те, що має сенс. Не думай, що ти хороший хлопчик, використовуючи, % 2
коли ти спочатку збирався писати & 1
.
Дорогі операції з плаваючою комою
Уникайте великих операцій з плаваючою комою, таких як pow()
і log()
в коді, який їм насправді не потрібен, особливо при роботі з цілими числами. Візьмемо, наприклад, читання числа:
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;
}
Це не тільки використання pow()
(і int
<-> double
перетворення , необхідне для використання його) досить дорого, але це створює можливість для втрати точності (до речі, наведений вище код не має проблем з точністю). Ось чому я хотіла, коли бачу цей тип функції, що використовується в нематематичному контексті.
Також зауважте, як алгоритм "розумного" нижче, який множиться на 10 на кожній ітерації, насправді більш стислий, ніж код, наведений вище:
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;
}