Представьте: вы на code review, с четвёртой чашкой кофе за утро в руках, и тут кто-то бросает такую фразу: «Зачем здесь использовать списковое включение? Поиск по словарю выполняется за O(1)!» Между тем рассматриваемый метод обрабатывает максимум три элемента. Поздравляем — вы только что стали свидетелем преждевременной оптимизации в естественной среде обитания.
Высокая цена ранней оптимизации
Начнём с истории, которую вы, возможно, узнаете:
# «Оптимизированный» подход
results = []
for i in range(0, len(data), 1):
temp = process(data[i])
results.append(temp)
# против «неэффективного» спискового включения
[process(x) for x in data]
Недавно я видел, как разработчик 20 минут доказывал, что первый подход «более эффективно использует память». Для скрипта, который запускается раз в месяц. Для набора данных меньше, чем ваши личные сообщения в Twitter. Ирония? Они были технически правы — и совершенно неправы.
Это не только вопрос чистого кода — это вопрос экономики выживания. Каждая минута, потраченная на микрооптимизацию, — это минута, не потраченная:
- На написание тестов (вы же пишете тесты, правда?).
- На обработку граничных случаев.
- На предотвращение следующей «катастрофы 404» в продакшне.
Когда хорошие оптимизации идут не так
Давайте разберём несколько классических «оптимизаций», которые я находил в PR:
Эпидемия раскрутки циклов
// До
for (let i = 0; i < 4; i++) {
process(i);
}
// После «оптимизации»
process(0);
process(1);
process(2);
process(3);
Ведь очевидно, что экономия трёх итераций цикла оправдывает увеличение кода в 4 раза! 🎉
Культ кэша
Я однажды видел, как разработчик реализовал собственный кэш LFU для… URL-адресов аватаров пользователей. Ирония? Приложение уже имело заголовки HTTP-кэширования.
Если-оператор за миллиард долларов
Бесконечные дебаты о:
if x is None: ...
# против
if not x: ...
Тем временем в бизнес-логике было достаточно «дыр», чтобы процедить макароны.
Руководство по выживанию при оптимизации
Вот мой проверенный на практике чек-лист перед рассмотрением оптимизаций:
- Тест на 3 часа ночи: если этот код сломается в 3 часа ночи, будет ли мне не всё равно?
- Тест на масштабируемость: справляется ли он с трафиком в 10 раз больше текущего?
- Тест на деньги: может ли это сэкономить реальные доллары? (Спойлер: счета AWS > денег на кофе)
- Тест на пятницу: усложнит ли это пятничный деплой?
Когда вам всё-таки нужно оптимизировать, вот как не испортить это:
# Шаг 1: напишите просто
def calculate_stats(data):
return {
'avg': sum(data)/len(data),
'max': max(data)
}
# Шаг 2: профилируйте как профи
# $ python -m cProfile your_script.py
# Шаг 3: оптимизируйте ТОЛЬКО горячие точки
def calculate_stats(data):
total = 0
maximum = data
for num in data:
total += num
if num > maximum:
maximum = num
return {'avg': total/len(data), 'max': maximum}
Заметьте, мы не трогали код, пока не получили доказательства, что он медленный? Волшебно.
Золотая середина оптимизации
Закончим любимой историей об оптимизации: команда потратила недели на оптимизацию сжатия изображений, только чтобы обнаружить, что 60% времени загрузки приходилось на… подождите… неоптимизированную доставку CSS. Урок? Вы не можете оптимизировать то, что не измеряете.
В следующий раз, когда почувствуете зуд оптимизации, спросите себя: решаю ли я реальную проблему или просто демонстрирую свой диплом по CS? Ваше будущее «я» (и раздражённые товарищи по команде) скажут вам спасибо.
А теперь, если вы меня извините, мне нужно переписать эту статью на ассемблере. Шутка. (Или нет?) 🔥