Введение: Почему 90% разработчиков неправильно ищут узкие места
Каждый разработчик хотя бы раз сталкивался с ситуацией, когда код работает, но медленно. В 2026 году, когда средний веб-сервис обрабатывает миллионы запросов в день, даже микросекунда задержки может стоить компании тысячи долларов. Исследование Google (Web Vitals Report, 2024) показало: 53% пользователей покидают сайт, если загрузка длится дольше 3 секунд. Однако, по данным опроса Stack Overflow (2025), 68% разработчиков тратят до 40% времени на оптимизацию, не используя системный подход.
Проблема в том, что интуитивная оптимизация часто приводит к усложнению кода без реального прироста производительности. Вместо того чтобы гадать, где «тормозит», нужно использовать промты — структурированные запросы к AI-ассистентам (например, Claude 4, GPT-5 или Copilot X), которые помогают находить узкие места системно. В этой статье я собрал 10 промтов, разделённых на три уровня сложности: от базовых (для новичков) до экспертных (для senior-инженеров). Каждый промт сопровождается задачей, примером кода и объяснением, как он работает.
Базовые промты (уровень 1–3)
Промт 1: «Найди аномалии в цикле»
Задача: Определить, есть ли в цикле операции, которые можно вынести или оптимизировать.
Промт:
Проанализируй следующий код на Python. Найди операции внутри цикла, которые можно вынести за его пределы, и предложи оптимизированную версию. Объясни каждое изменение.
[вставьте код]
Пример результата:
Исходный код:
import math
def process_data(items):
results = []
for item in items:
factor = math.sqrt(len(items)) # вычисляется на каждой итерации
processed = item * factor
results.append(processed)
return results
Оптимизированная версия:
import math
def process_data(items):
factor = math.sqrt(len(items)) # вынесено за цикл
return [item * factor for item in items] # list comprehension быстрее
Объяснение: Вычисление math.sqrt(len(items)) не зависит от item, поэтому его можно выполнить один раз. Кроме того, list comprehension работает быстрее ручного append, так как использует внутренние оптимизации CPython (согласно документации Python 3.12, разница может достигать 15–20%).
Промт 2: «Оцени сложность алгоритма»
Задача: Понять, как алгоритм масштабируется при росте данных.
Промт:
Определи временную и пространственную сложность этого алгоритма в нотации Big O. Предложи альтернативу с лучшей сложностью, если это возможно. Приведи пример данных, на которых текущая версия будет работать неприемлемо долго.
[вставьте код]
Пример результата:
Исходный код:
def find_duplicates(arr):
duplicates = []
for i in range(len(arr)):
for j in range(i+1, len(arr)):
if arr[i] == arr[j] and arr[i] not in duplicates:
duplicates.append(arr[i])
return duplicates
Анализ:
- Временная сложность: O(n²) — вложенные циклы.
- Пространственная сложность: O(k) — хранение дубликатов.
- На массиве из 100 000 элементов (n=100 000) потребуется ~10¹⁰ операций, что займёт около 10 секунд (на CPU 3 ГГц).
Оптимизированная версия:
def find_duplicates(arr):
seen = set()
duplicates = set()
for item in arr:
if item in seen:
duplicates.add(item)
else:
seen.add(item)
return list(duplicates)
Объяснение: Использование set (хэш-таблицы) снижает сложность до O(n), так как проверка in для set выполняется за O(1) в среднем. Разница будет заметна уже на массивах от 10 000 элементов.
Промт 3: «Профилируй время выполнения»
Задача: Измерить, сколько времени занимает каждая часть кода.
Промт:
Добавь в этот код профилирование с помощью time.perf_counter() или cProfile. Выведи топ-3 самых медленных функций с указанием времени и процента от общего времени выполнения. Предложи, что можно оптимизировать в первую очередь.
[вставьте код]
Пример результата:
Добавленный код:
import cProfile
import pstats
def run_profile():
# ваш код
pass
if __name__ == "__main__":
profiler = cProfile.Profile()
profiler.enable()
run_profile()
profiler.disable()
stats = pstats.Stats(profiler)
stats.sort_stats('cumtime').print_stats(10)
Вывод:
- Функция parse_data занимает 45% времени — содержит неэффективный regex.
- Функция load_file занимает 30% — возможно, стоит использовать буферизацию.
- Рекомендация: заменить regex на split() и увеличить buffer size до 64 KB.
Продвинутые промты (уровень 4–6)
Промт 4: «Оптимизируй запросы к базе данных»
Задача: Уменьшить количество запросов или ускорить их выполнение.
Промт:
Проанализируй этот SQL-запрос (или ORM-код) и предложи оптимизацию. Учти: наличие индексов, план выполнения (EXPLAIN ANALYZE), возможность денормализации. Если используешь ORM (например, Django ORM), укажи, как избежать N+1 запросов.
[вставьте код]
Пример результата:
Исходный код (Django ORM):
orders = Order.objects.filter(status='pending')
for order in orders:
print(order.customer.name) # N+1 проблема
Оптимизация:
orders = Order.objects.filter(status='pending').select_related('customer')
for order in orders:
print(order.customer.name) # один JOIN-запрос
Объяснение: Без select_related Django делает отдельный запрос для каждого заказа (N+1). С select_related — один запрос с JOIN. Согласно документации Django 5.0, это может сократить время выполнения с O(N) до O(1) запросов.
Промт 5: «Уменьши потребление памяти»
Задача: Найти объекты, которые занимают избыточную память.
Промт:
Проанализируй этот код на утечки памяти и избыточное потребление. Используй sys.getsizeof() или tracemalloc. Предложи изменения, чтобы снизить peak memory usage.
[вставьте код]
Пример результата:
Исходный код:
def load_large_file(filename):
with open(filename, 'r') as f:
data = f.read() # загружает весь файл в память
lines = data.split('\n')
return lines
Оптимизация:
def load_large_file(filename):
with open(filename, 'r') as f:
for line in f: # читает построчно
yield line.strip()
Объяснение: Файл размером 1 ГБ будет загружен в память целиком в первом варианте. Использование генератора снижает потребление до O(1) — только одна строка в памяти. Согласно тестам на Python 3.12, разница в peak memory может быть в 1000 раз.
Промт 6: «Распараллель вычисления»
Задача: Использовать многопоточность или многопроцессность для ускорения.
Промт:
Предложи, как распараллелить этот код с помощью concurrent.futures или multiprocessing. Учти GIL (Global Interpreter Lock) в Python: если задачи CPU-bound, используй multiprocessing; если I/O-bound — threading. Оцени ожидаемое ускорение.
[вставьте код]
Пример результата:
Исходный код (CPU-bound):
def compute_factorials(numbers):
results = []
for n in numbers:
results.append(math.factorial(n))
return results
Оптимизация:
from concurrent.futures import ProcessPoolExecutor
import math
def compute_factorials(numbers):
with ProcessPoolExecutor() as executor:
results = list(executor.map(math.factorial, numbers))
return results
Объяснение: ProcessPoolExecutor обходит GIL, используя отдельные процессы. На 4-ядерном процессоре ускорение может быть до 3.5x (с учётом накладных расходов на IPC). Для I/O-bound задач (например, HTTP-запросы) лучше использовать ThreadPoolExecutor.
Экспертные промты (уровень 7–10)
Промт 7: «Примени JIT-компиляцию»
Задача: Ускорить численные вычисления с помощью Numba или PyPy.
Промт:
Перепиши эту функцию с использованием декоратора @jit из Numba. Укажи, какие типы данных нужно указать для ускорения (nopython mode). Сравни производительность с исходной версией.
[вставьте код]
Пример результата:
Исходный код:
def sum_of_squares(n):
total = 0
for i in range(n):
total += i * i
return total
Оптимизация:
from numba import jit
@jit(nopython=True)
def sum_of_squares_fast(n):
total = 0
for i in range(n):
total += i * i
return total
Результат: На n=10⁷ исходная версия выполняется ~0.5 сек, версия с Numba — ~0.02 сек (ускорение в 25 раз). Numba компилирует Python-код в машинный код через LLVM, что особенно эффективно для циклов и численных операций (источник: документация Numba 0.60).
Промт 8: «Оптимизируй работу с кэшем CPU»
Задача: Изменить структуру данных для улучшения локальности ссылок.
Промт:
Проанализируй этот код с точки зрения кэш-промахов (cache misses). Предложи изменить layout данных (например, перейти от списка объектов к массиву структур) для улучшения пространственной локальности. Используй perf или valgrind --tool=cachegrind для проверки.
[вставьте код]
Пример результата:
Исходный код:
class Particle:
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
particles = [Particle(random.random(), random.random(), random.random()) for _ in range(1000000)]
total = sum(p.x for p in particles)
Оптимизация:
import array
x = array.array('d', [random.random() for _ in range(1000000)])
y = array.array('d', [random.random() for _ in range(1000000)])
z = array.array('d', [random.random() for _ in range(1000000)])
total = sum(x)
Объяснение: Объекты Python разбросаны в памяти, что вызывает кэш-промахи. Массивы хранят данные последовательно, что улучшает локальность. Согласно тестам (Intel VTune), разница в производительности может быть до 5x для больших массивов.
Промт 9: «Используй SIMD-инструкции»
Задача: Векторизовать вычисления для использования SIMD (Single Instruction Multiple Data).
Промт:
Перепиши этот цикл с использованием NumPy или явных SIMD-инструкций (через Cython или Rust). Покажи, как изменится производительность на массиве из 10⁶ элементов.
[вставьте код]
Пример результата:
Исходный код:
def add_arrays(a, b):
result = []
for i in range(len(a)):
result.append(a[i] + b[i])
return result
Оптимизация (NumPy):
import numpy as np
def add_arrays_fast(a, b):
return np.add(a, b) # использует SIMD через BLAS
Результат: Для массивов из 10⁶ элементов: исходный код — ~0.3 сек, NumPy — ~0.005 сек (ускорение в 60 раз). NumPy вызывает оптимизированные C-функции, которые автоматически используют AVX2/AVX-512 инструкции на современных CPU (документация NumPy 2.0).
Промт 10: «Профилируй асинхронный код»
Задача: Найти узкие места в asyncio-приложениях.
Промт:
Проанализируй этот асинхронный код на наличие блокирующих операций, которые нарушают event loop. Используй asyncio.run() с отладкой или aiomonitor. Предложи заменить блокирующие вызовы на асинхронные.
[вставьте код]
Пример результата:
Исходный код:
import asyncio
def fetch_data(url):
import requests
return requests.get(url).text # блокирующий вызов
async def main():
result = await asyncio.to_thread(fetch_data, "https://example.com")
print(result)
Оптимизация:
import asyncio
import aiohttp
async def fetch_data(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
return await response.text()
async def main():
result = await fetch_data("https://example.com")
print(result)
Объяснение: requests.get() блокирует event loop, что снижает пропускную способность. aiohttp работает асинхронно, позволяя обрабатывать тысячи параллельных запросов. Согласно тестам (aiohttp docs), разница в throughput может быть до 100x при большом количестве соединений.
Таблица сравнения промтов
| Уровень | Номер промта | Метрика улучшения | Типичное ускорение | Сложность внедрения |
|---|---|---|---|---|
| Базовый | 1 (циклы) | Время выполнения | 15–20% | Низкая |
| Базовый | 2 (алгоритмы) | Big O | O(n²) → O(n) | Средняя |
| Продвинутый | 4 (БД) | Количество запросов | O(N) → O(1) | Средняя |
| Продвинутый | 5 (память) | Peak memory | 1000x | Низкая |
| Экспертный | 7 (JIT) | Время выполнения | 10–50x | Средняя |
| Экспертный | 9 (SIMD) | Время выполнения | 10–100x | Высокая |
Заключение: Как выбрать правильный промт
Оптимизация кода — это не магия, а системный процесс. Используя промты из этой подборки, вы можете:
1. Начинать с профилирования (промт 3) — чтобы не оптимизировать то, что не является узким местом.
2. Применять базовые промты (1–3) для быстрых побед — они требуют минимальных изменений.
3. Переходить к продвинутым (4–6) для работы с базами данных и памятью.
4. Использовать экспертные (7–10) только если действительно нужен прирост в 10–100x, и вы готовы к сложным изменениям.
Помните: согласно закону Амдала, ускорение ограничено долей времени, которую занимает оптимизируемая часть. Если функция занимает 5% времени, даже ускорение в 100 раз даст лишь 5% общего прироста. Поэтому всегда начинайте с профилирования.
Ссылки и дополнительные ресурсы
- Официальная документация Python по профилированию: docs.python.org/3/library/profile.html
- Документация Numba: numba.pydata.org
- Статья Google Web Vitals: web.dev/vitals
- Исследование Stack Overflow (2025): stackoverflow.com/survey/2025
ASI Biont поддерживает подключение к базам данных через API для автоматизации профилирования — подробнее на asibiont.com/courses
Автор: технический писатель и автор курсов по AI. Специализация — оптимизация производительности и системное программирование.
Комментарии