Пять правил Роба Пайка, которые спасут ваш код от преждевременной оптимизации
Знаешь, что меня бесит больше всего в нашем деле? Когда junior-разработчик (или даже senior — неважно) приходит и говорит: «Тут квадратный корень извлекается в цикле, давайте заменим на табличный поиск, это же быстрее». А потом выясняется, что функция вызывается три раза при старте приложения, а реальное узкое место — это запрос к базе данных на 200ms, который вообще никто не оптимизировал.
Именно поэтому правила Пайка полезны не как историческая цитата, а как практический фильтр для любой команды, которая хочет ускорять продукт без самообмана.
Роб Пайк и его правила
Роб Пайк — это не просто какой-то там программист. Это человек, который вместе с Кеном Томпсоном создал язык Go, работал над Plan 9 и Unix в Bell Labs. Когда такой человек говорит что-то про программирование — имеет смысл послушать. В конце 80-х он сформулировал пять правил, которые до сих пор актуальны. Давайте разберём каждое.
Правило первое: не угадывай узкие места
Первое правило Пайка говорит о том, что невозможно предсказать, где программа проведёт большую часть времени. Узкие места возникают в самых неожиданных местах, поэтому не стоит пытаться угадать и пихать «оптимизации» до тех пор, пока ты не доказал, что именно там проблема.
Это напоминает известный афоризм Тони Хоара: «Преждевременная оптимизация — корень всех зол». И знаешь что? Хоар сказал это в 1974 году. С тех пор ничего не изменилось. Мы как писали тормозной код в надежде, что «авось прокатит», так и продолжаем.
Типичный пример из жизни: разработчик потратил три дня на замену string concatenation на StringBuilder (в Java это реально быстрее при большом количестве операций), а потом оказалось, что основное время съедают HTTP-запросы к внешнему API, которые можно было просто закэшировать.
Правило второе: измеряй, прежде чем оптимизировать
Второе правило — логическое продолжение первого. Измеряй производительность, и только потом оптимизируй. И то только в том случае, если один участок кода явно «убивает» всё остальное.
Это звучит очевидно, но сколько раз ты видел, как команда месяцами оптимизирует какой-то модуль, а потом выясняется, что пользователи ждут не его, а совсем другой функционал?
В современном мире есть масса инструментов для профилирования: в Python это cProfile, в Java — VisualVM, в JavaScript — Chrome DevTools. Да что угодно. Главное — использовать эти инструменты, а не гадать на кофейной гуще.
ОПТИМИЗАЦИЯ БЕЗ ИЗМЕРЕНИЙ
──────────────────────────
Догадка Реальность
▼ ▼
┌───────┐ ┌───────┐
│"Тут │ │"Тут │
│ мед- │ │ мед- │
│ленно"│ │ленно" │
└───────┘ └───────┘
│ │
▼ ▼
Тратим Тратим время
время в правильном
не там месте ✓
Правило третье: простота важнее хитрости при маленьких объёмах
Третье правило Пайка — отдельная философия. Сложные алгоритмы медленны, когда n мало, а n обычно мало. У сложных алгоритмов большие константы.
Что это значит на практике? Допустим, у тебя есть алгоритм бинарного поиска — O (log n) против линейного O (n). Звучит круто, правда? Но если у тебя массив из 10 элементов, линейный поиск просто пробежит по 10 элементам, а бинарный должен сначала отсортировать (если не отсортировано), потом делить, делить, делить… При маленьких n разница может быть вообще незаметна, а константы у сложных алгоритмов часто зашкаливают.
Кен Томпсон переформулировал правила 3 и 4 очень кратко: «Когда сомневаешься — используй грубую силу». Это не значит, что нужно писать код вида while(true) {} (хотя и такое видели). Это значит: не усложняй без необходимости.
Правило четвёртое: сложные алгоритмы — это сложные баги
Четвёртое правило вытекает из третьего. Сложные алгоритмы сложнее в реализации и содержат больше багов. Используй простые алгоритмы и простые структуры данных.
Это философия KISS — «Keep It Simple, Stupid». Знаешь, сколько проектов утонуло в попытках внедрить «красивое» решение? Сколько раз я видел код, где автор использовал красно-чёрное дерево там, где хватило бы обычного массива? А потом выяснялось, что этот код никто не может поддерживать, потому что «автор — единственный, кто понимает, как это работает».
Простые алгоритмы легче понять, легче отладить, легче исправить. А ещё простой код обычно работает быстрее просто потому, что в нём меньше накладных расходов.
Правило пятое: данные важнее алгоритмов
И самое интересное — пятое правило. Если ты выбрал правильные структуры данных и организовал их хорошо, алгоритмы почти всегда будут очевидными. Структуры данных, а не алгоритмы, являются центральными в программировании.
Эту же мысль ранее выразил Фред Брукс в книге «Мифический человеко-месяц». Пайк часто сокращает это до «пиши тупой код, который использует умные объекты».
Что это означает? Допустим, тебе нужно найти человека по имени в списке из миллиона записей. Можно написать супер-оптимизированный алгоритм поиска… А можно просто отсортировать данные по имени и использовать бинарный поиск. Или вообще использовать хэш-таблицу (dictionary, map — называй как хочешь), где поиск — это O (1).
Выбор правильной структуры данных решает проблему ещё до того, как ты начал писать алгоритмы. Это как выбрать правильный инструмент: молотком гвозди забивать легко, а отвёрткой — можно, но зачем?
ДАННЫЕ vs АЛГОРИТМЫ
───────────────────
Неправильно: Правильно:
────────────── ────────────
Алгоритм Данные
│ │
▼ ▼
"Как искать?" → "Как хранить?"
O(n) поиск O(1) через хэш
в массиве таблицу
Сложно! Просто!
Выводы
Первое: оптимизация без измерений почти всегда лечит не ту проблему.
Второе: простые алгоритмы и простые структуры данных выигрывают чаще, чем хочется признать, особенно на реальных рабочих объёмах.
Третье: если система тормозит, начинай не с микроулучшений, а с вопроса, правильно ли организованы данные и действительно ли найдено узкое место.
Ссылки
- Оригинальная статья с правилами Роба Пайка
- Фред Брукс «Мифический человеко-месяц» — классика про управление проектами и роль структур данных
- KISS принцип — философия простоты в разработке