Какие простые приемы вы используете для повышения производительности?

Какие простые приемы вы используете для повышения производительности?
Какие простые приемы вы используете для повышения производительности? - asiaculturecenter @ Unsplash

Я говорю о том, как мы пишем простые рутины, чтобы улучшить производительность, не делая код более трудным для чтения... например, вот типичный для нас прием:

for(int i = 0; i < collection.length(); i++ ){
   // stuff here
}

Но, я обычно делаю это, когда foreach не применимо:

for(int i = 0, j = collection.length(); i < j; i++ ){
   // stuff here
}

Я думаю, что это лучший подход, поскольку он будет вызывать метод length только один раз... моя девушка говорит, что это криптовалюта. Есть ли еще какой-нибудь простой трюк, который вы используете в своих разработках?

вставьте лекцию "преждевременное обсуждение - корень всех зол".

Тем не менее, вот некоторые привычки, которые я приобрел, чтобы избежать ненужной эффективности, а в некоторых случаях сделать свой код более простым и правильным.

Это не обсуждение общих принципов, а некоторые вещи, о которых следует помнить, чтобы избежать введения ненужной неэффективности в код.

Знай свое большое-О

Возможно, это следует объединить с длинным обсуждением выше. Вполне логично, что цикл внутри цикла, где внутренний цикл повторяет вычисления, будет медленнее. Например:

for (i = 0; i < strlen(str); i++) {
    ...
}

Это займет ужасающее количество времени, если строка действительно длинная, потому что длина пересчитывается на каждой итерации цикла. Обратите внимание, что GCC фактически оптимизирует этот случай, потому что strlen() помечен как чистая функция.

При сортировке миллиона 32-битных целых чисел пузырьковая сортировка будет неправильным способом. В целом, сортировка может быть выполнена за время O(n * log n) (или лучше, в случае радиксной сортировки), поэтому, если вы не знаете, что ваши данные будут небольшими, ищите алгоритм, который по крайней мере O(n * log n).

Аналогичным образом, работая с базами данных, помните об индексах. Если у вас SELECT * FROM people WHERE age = 20 нет индекса на people(age), то потребуется последовательное сканирование 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;
}

LetsCodeIt, 20 мая 2023 г., 00:20