Производительность программы против читаемости и простоты кода: в пользу чего стоит делать выбор?

Рассказывает Arne Mertz 


Одной из сильных сторон C++ является возможность написания очень производительного кода. Но значит ли это, что мы должны постоянно беспокоиться о производительности и писать весь код настолько производительно, насколько это возможно? Должны ли мы отказаться от простоты ради этого? А стоит ли? 

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

Производительность ≠ эффективность

В первую очередь нужно научиться различать производительность и эффективность. В чем разница? Если коротко: производительность влияет на то, как быстро вы что-то делаете, а эффективность — на то, сколько это занимает у вас времени.

Вроде бы одно и то же, скажете вы, и будете неправы. Представьте, что вы перемещаетесь из пункта А в пункт Б. Эффективно будет пойти кратчайшим путем. Производительно будет, если вы побежите. То есть, если вы на всех парах промчитесь по всему району, чтобы попасть к соседу, это будет производительно, но не эффективно.

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

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

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

Производительность нужна не всегда

Это очевидно, но многие программисты, особенно начинающие, не всегда понимают это. На форумах висят сотни вопросов об оптимизации кода. При этом, если задается вопрос о том, является ли этот участок кода «бутылочным горлышком» для общей производительности, ответ на него чаще всего дается отрицательный.

Говорят, что 80% времени своей работы программа занимается выполнением 20% кода. Но точные числа здесь не важны. Главным является тот факт, что программа тратит большую часть времени на исполнение меньшей части кода.

Иными словами, это означает, что почти весь код мало влияет на время выполнения, и его оптимизация прироста скорости не даст.

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

На самом деле мы не знаем, как правильно оптимизировать

О да, как я посмел 🙂 Дело в том, что огромное влияние на время выполнения программы имеет количество инструкций, исполняемых процессором. А их задаем не мы, а компилятор и его оптимизатор.

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

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

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

Конечно же, вышесказанное не значит, что стоит писать непроизводительный код. Если есть два способа написать одинаково читаемый код, выбирайте более производительный. Например, используйте ++iter вместо iter++, если вам не нужно сохранять результат выражения, и так далее.

Производительность и простота — не взаимоисключающие признаки

Серьезный вклад во время выполнения программы также вносит структура и расположение данных в памяти. Об этом можно прочитать хорошую статью, я не буду заострять свое внимание на этом вопросе.

Главное — если вы плохо структуризовали данные в памяти, это точно приведет к ухудшению производительности.

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

Есть еще один способ написания производительного и простого кода: используйте проверенные библиотеки. Их авторы, как правило, умные ребята, и разбираются в производительности. Поэтому, если вы будете использовать библиотеки, а не свои решения, код будет не только проще, но и производительней.

Используйте библиотеки, но только если профайлер не жалуется на их плохую производительность.

Заключение

Изначально пишите простой и читаемый код. Если же вы нашли проблему в производительности, исправить ее можно, не превращая код в быстрый, но непонятный беспорядок. Жертвуйте простотой ради производительности только в крайнем случае, и всегда используйте профайлер.


А что вы думаете по этому поводу? Делитесь мнениями в комментариях, и да начнётся холивар 🙂

Перевод статьи «Simple and Clean Code vs. Performance»