15 промтов для оптимизации производительности кода: от профилирования до узких мест

15 промтов для оптимизации производительности кода: от профилирования до узких мест

Введение

Каждый разработчик сталкивался с ситуацией, когда код работает, но медленно. Особенно это критично в продакшене: пользователи уходят, если страница грузится дольше 3 секунд (данные Google PageSpeed Insights). Оптимизация производительности — это не просто «ускорение», а системный процесс поиска узких мест и их устранения. Я, как практикующий разработчик, ежедневно использую AI-промты для решения таких задач. В этой подборке — 15 проверенных промтов, которые помогут вам быстрее профилировать, находить баги производительности и рефакторить код. Каждый промт сопровождается реальным примером и кодом.

1. Промт для профилирования времени выполнения функции

Промт: «Напиши код для профилирования времени выполнения функции processData в Python с использованием time.perf_counter. Выведи среднее время выполнения за 1000 запусков, минимальное и максимальное значение, а также количество вызовов в секунду. Пример вывода: "Среднее: 0.0023 с, мин: 0.0018 с, макс: 0.0031 с, вызовов/с: 434"».

Пример использования:

import time

def process_data(n):
    return sum(i**2 for i in range(n))

# Профилирование
def profile(func, *args, n_runs=1000):
    times = []
    for _ in range(n_runs):
        start = time.perf_counter()
        func(*args)
        end = time.perf_counter()
        times.append(end - start)
    avg = sum(times) / len(times)
    min_t = min(times)
    max_t = max(times)
    calls_per_sec = 1 / avg if avg > 0 else float('inf')
    print(f"Среднее: {avg:.4f} с, мин: {min_t:.4f} с, макс: {max_t:.4f} с, вызовов/с: {calls_per_sec:.0f}")

profile(process_data, 10000)

Пояснение: Этот промт даёт готовый инструмент для быстрого замера производительности любой функции. Используйте его на этапе тестирования, чтобы сравнить разные реализации.

2. Промт для поиска узких мест с помощью cProfile

Промт: «Напиши скрипт на Python, который использует модуль cProfile для профилирования функции compute_heavy. Выведи топ-10 функций по времени выполнения, отсортированных по cumtime. Пример вывода: "{ncalls: 1000, tottime: 0.5, cumtime: 2.1, filename: script.py:10}"».

Пример использования:

import cProfile
import pstats

def compute_heavy():
    total = 0
    for i in range(100000):
        total += i ** 0.5
    return total

cProfile.run('compute_heavy()', 'profile_stats')
p = pstats.Stats('profile_stats')
p.sort_stats('cumtime').print_stats(10)

Пояснение: cProfile — стандартный инструмент Python для профилирования. Промт помогает сразу получить читаемый отчёт, экономя время на настройку.

3. Промт для оптимизации SQL-запросов

Промт: «Дан SQL-запрос: "SELECT * FROM orders WHERE status = 'pending' ORDER BY created_at DESC;". Предложи оптимизации: добавь индексы, измени структуру запроса, объясни, как проверить план выполнения через EXPLAIN. Напиши пример с индексом и без».

Пример использования:

-- Исходный запрос
EXPLAIN ANALYZE SELECT * FROM orders WHERE status = 'pending' ORDER BY created_at DESC;

-- Оптимизированный: составной индекс
CREATE INDEX idx_orders_status_created ON orders(status, created_at DESC);

-- Повторный EXPLAIN
EXPLAIN ANALYZE SELECT * FROM orders WHERE status = 'pending' ORDER BY created_at DESC;

Пояснение: Без индекса база данных сканирует всю таблицу (Seq Scan), что медленно на больших объёмах. Составной индекс ускоряет фильтрацию и сортировку. Используйте EXPLAIN ANALYZE для проверки.

4. Промт для анализа памяти в Python

Промт: «Напиши скрипт для профилирования памяти функции load_large_data с помощью модуля memory_profiler. Выведи потребление памяти в МБ до и после вызова, а также прирост. Пример вывода: "До: 50.2 МБ, После: 120.8 МБ, Прирост: 70.6 МБ"».

Пример использования:

from memory_profiler import memory_usage

def load_large_data():
    data = [i for i in range(1000000)]
    return sum(data)

mem_before = memory_usage(-1, interval=0.1, timeout=1)[0]
load_large_data()
mem_after = memory_usage(-1, interval=0.1, timeout=1)[0]
print(f"До: {mem_before:.1f} МБ, После: {mem_after:.1f} МБ, Прирост: {mem_after - mem_before:.1f} МБ")

Пояснение: Утечки памяти — частая проблема. Этот промт позволяет быстро оценить, сколько памяти съедает функция.

5. Промт для оптимизации алгоритма сортировки

Промт: «У меня есть список из 10⁶ чисел. Нужно отсортировать его по убыванию. Сравни время выполнения встроенной sorted() и ручной реализации быстрой сортировки. Напиши код с замером времени и выводом разницы. Пример вывода: "sorted(): 0.12 с, quicksort: 2.34 с, разница в 19.5 раз"».

Пример использования:

import random
import time

# Быстрая сортировка (ручная реализация)
def quicksort(arr):
    if len(arr) <= 1:
        return arr
    pivot = arr[len(arr) // 2]
    left = [x for x in arr if x > pivot]
    middle = [x for x in arr if x == pivot]
    right = [x for x in arr if x < pivot]
    return quicksort(left) + middle + quicksort(right)

# Тест
data = [random.randint(0, 1000000) for _ in range(100000)]

start = time.time()
sorted_data = sorted(data, reverse=True)
time_sorted = time.time() - start

start = time.time()
qs_data = quicksort(data)
time_qs = time.time() - start

print(f"sorted(): {time_sorted:.4f} с, quicksort: {time_qs:.4f} с, разница в {time_qs/time_sorted:.1f} раз")

Пояснение: Встроенная сортировка в Python (Timsort) оптимизирована на C, поэтому она значительно быстрее ручных реализаций. Промт учит не изобретать велосипед.

6. Промт для кэширования результатов функций

Промт: «Напиши декоратор для кэширования результатов функции с помощью lru_cache из functools. Покажи пример для рекурсивного вычисления чисел Фибоначчи. Сравни время выполнения с кэшем и без. Выведи ускорение в разах».

Пример использования:

from functools import lru_cache
import time

def fib_no_cache(n):
    if n < 2:
        return n
    return fib_no_cache(n-1) + fib_no_cache(n-2)

@lru_cache(maxsize=128)
def fib_cache(n):
    if n < 2:
        return n
    return fib_cache(n-1) + fib_cache(n-2)

n = 35
start = time.time()
fib_no_cache(n)
time_no = time.time() - start

start = time.time()
fib_cache(n)
time_yes = time.time() - start

print(f"Без кэша: {time_no:.4f} с, С кэшем: {time_yes:.4f} с, ускорение в {time_no/time_yes:.0f} раз")

Пояснение: Кэширование — один из самых эффективных способов ускорения рекурсивных алгоритмов. lru_cache автоматически хранит результаты последних вызовов.

7. Промт для параллельного выполнения задач

Промт: «Напиши код на Python, который загружает 10 веб-страниц параллельно с использованием concurrent.futures.ThreadPoolExecutor. Сравни время последовательного и параллельного выполнения. Пример вывода: "Последовательно: 5.2 с, Параллельно: 1.1 с, ускорение в 4.7 раз"».

Пример использования:

import requests
import time
from concurrent.futures import ThreadPoolExecutor

urls = ['https://httpbin.org/delay/1'] * 10

def fetch(url):
    return requests.get(url).status_code

# Последовательно
start = time.time()
results_seq = [fetch(url) for url in urls]
time_seq = time.time() - start

# Параллельно
start = time.time()
with ThreadPoolExecutor(max_workers=5) as executor:
    results_par = list(executor.map(fetch, urls))
time_par = time.time() - start

print(f"Последовательно: {time_seq:.2f} с, Параллельно: {time_par:.2f} с, ускорение в {time_seq/time_par:.1f} раз")

Пояснение: Для I/O-bound задач (запросы к API, чтение файлов) параллелизм с потоками даёт значительное ускорение.

8. Промт для оптимизации циклов с помощью NumPy

Промт: «Замени цикл на Python для вычисления суммы квадратов элементов массива на векторную операцию NumPy. Сравни время выполнения для массива из 10⁷ элементов. Пример вывода: "Цикл: 3.2 с, NumPy: 0.04 с, ускорение в 80 раз"».

Пример использования:

import numpy as np
import time

n = 10000000
arr = np.random.rand(n)

# Цикл
def sum_sq_loop(arr):
    total = 0
    for x in arr:
        total += x**2
    return total

start = time.time()
sum_sq_loop(arr)
time_loop = time.time() - start

# NumPy
start = time.time()
np.sum(arr**2)
time_np = time.time() - start

print(f"Цикл: {time_loop:.4f} с, NumPy: {time_np:.4f} с, ускорение в {time_loop/time_np:.0f} раз")

Пояснение: NumPy использует векторизованные операции на C, что даёт многократное ускорение для численных расчётов.

9. Промт для дебага медленных запросов к API

Промт: «Напиши функцию для замера времени HTTP-запроса с помощью requests и time. Выведи URL, статус код, время ответа и размер ответа в байтах. Пример вывода: "GET https://api.example.com/data -> 200, 0.234 с, 12345 байт"».

Пример использования:

import requests
import time

def time_request(url):
    start = time.time()
    response = requests.get(url)
    elapsed = time.time() - start
    size = len(response.content)
    print(f"GET {url} -> {response.status_code}, {elapsed:.3f} с, {size} байт")
    return elapsed

time_request('https://jsonplaceholder.typicode.com/posts/1')

Пояснение: Этот промт помогает быстро выявить медленные эндпоинты. Добавьте его в тесты или мониторинг.

10. Промт для анализа времени загрузки страницы в браузере

Промт: «Напиши JavaScript-код для измерения времени загрузки страницы с помощью Performance API. Выведи время до DOMContentLoaded, load и общее время. Пример вывода: "DOMContentLoaded: 1.2 с, Load: 2.5 с, Total: 3.0 с"».

Пример использования:

window.addEventListener('load', () => {
    const perfData = window.performance.timing;
    const domContentLoaded = perfData.domContentLoadedEventEnd - perfData.navigationStart;
    const loadTime = perfData.loadEventEnd - perfData.navigationStart;
    console.log(`DOMContentLoaded: ${(domContentLoaded / 1000).toFixed(2)} с`);
    console.log(`Load: ${(loadTime / 1000).toFixed(2)} с`);
});

Пояснение: Performance API — стандартный инструмент для фронтенд-оптимизации. Промт помогает измерить ключевые метрики.

11. Промт для поиска дублирующихся вычислений

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

def compute(x):
    a = x ** 2 + 2 * x + 1
    b = x ** 2 + 2 * x + 1
    return a + b

Напиши оптимизированную версию с замером времени».

Пример использования:

# Исходный код
def compute_original(x):
    a = x ** 2 + 2 * x + 1
    b = x ** 2 + 2 * x + 1
    return a + b

# Оптимизированный
def compute_optimized(x):
    cached = x ** 2 + 2 * x + 1
    return cached * 2

# Замер
import time
x = 1000000
start = time.time()
for _ in range(100000):
    compute_original(x)
time_orig = time.time() - start

start = time.time()
for _ in range(100000):
    compute_optimized(x)
time_opt = time.time() - start

print(f"Исходный: {time_orig:.4f} с, Оптимизированный: {time_opt:.4f} с, ускорение в {time_orig/time_opt:.1f} раз")

Пояснение: Дублирование вычислений — частая ошибка. Промт учит выносить повторяющиеся операции в переменные.

12. Промт для оптимизации работы с файлами

Промт: «Напиши код для чтения большого текстового файла (100 МБ) построчно с помощью генератора. Сравни время чтения всего файла в память и построчного чтения. Выведи разницу в секундах и потребление памяти».

Пример использования:

import time
import sys

# Создаём тестовый файл
with open('test.txt', 'w') as f:
    for i in range(10000000):
        f.write(f"Line {i}\n")

# Чтение всего файла в память
start = time.time()
with open('test.txt', 'r') as f:
    data = f.read()
time_all = time.time() - start

# Построчное чтение
start = time.time()
with open('test.txt', 'r') as f:
    for line in f:
        pass
time_line = time.time() - start

print(f"Всё в память: {time_all:.4f} с, Построчно: {time_line:.4f} с")

Пояснение: Построчное чтение не загружает весь файл в RAM, что критично при ограниченной памяти.

13. Промт для сравнения времени работы разных структур данных

Промт: «Сравни время выполнения операций поиска, вставки и удаления для списка, множества и словаря в Python для 10⁵ элементов. Напиши код с замерами».

Пример использования:

import time

n = 100000
data_list = list(range(n))
data_set = set(range(n))
data_dict = {i: i for i in range(n)}

# Поиск элемента
start = time.time()
for _ in range(1000):
    n-1 in data_list
time_list = time.time() - start

start = time.time()
for _ in range(1000):
    n-1 in data_set
time_set = time.time() - start

print(f"Поиск в списке: {time_list:.4f} с, в множестве: {time_set:.4f} с, ускорение в {time_list/time_set:.0f} раз")

Пояснение: Множества и словари используют хеш-таблицы, что даёт O(1) для поиска, в отличие от O(n) для списка.

14. Промт для профилирования времени загрузки страницы в Node.js

Промт: «Напиши middleware для Express.js, который логирует время обработки каждого запроса. Выведи URL, метод, статус, время в миллисекундах. Пример вывода: "GET /api/users 200 45ms"».

Пример использования:

const express = require('express');
const app = express();

app.use((req, res, next) => {
    const start = Date.now();
    res.on('finish', () => {
        const duration = Date.now() - start;
        console.log(`${req.method} ${req.url} ${res.statusCode} ${duration}ms`);
    });
    next();
});

app.get('/', (req, res) => {
    setTimeout(() => res.send('Hello'), 100);
});

app.listen(3000);

Пояснение: Middleware-промт для мониторинга производительности API в реальном времени.

15. Промт для оптимизации рендеринга React-компонентов

Промт: «Напиши React-компонент, который использует useMemo и useCallback для предотвращения лишних ререндеров. Покажи пример с профилированием через React.memo и Profiler API».

Пример использования:

import React, { useMemo, useState, Profiler } from 'react';

const ExpensiveComponent = React.memo(({ data }) => {
    const processed = useMemo(() => {
        return data.map(x => x * 2);
    }, [data]);
    return <div>{processed.join(',')}</div>;
});

function App() {
    const [count, setCount] = useState(0);
    const data = useMemo(() => [1, 2, 3], []);

    const onRender = (id, phase, actualDuration) => {
        console.log(`Компонент {id} отрендерен за {actualDuration} мс`);
    };

    return (
        <Profiler id="App" onRender={onRender}>
            <ExpensiveComponent data={data} />
            <button onClick={() => setCount(c => c + 1)}>{count}</button>
        </Profiler>
    );
}

Пояснение: useMemo и React.memo предотвращают повторные вычисления и ререндеры, что критично для сложных UI.

Заключение

Эти 15 промтов покрывают основные сценарии оптимизации: профилирование, кэширование, параллелизм, работа с памятью и структурами данных. Используйте их как шпаргалку при ревью кода или дебаге. Начните с профилирования (промты 1-2) — это покажет, где реальные узкие места. Помните: преждевременная оптимизация — зло, но системный подход с инструментами AI ускорит вашу работу. Попробуйте применить хотя бы один промт сегодня — и вы увидите разницу.

← Все статьи

Комментарии