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

Введение: Почему 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% общего прироста. Поэтому всегда начинайте с профилирования.

Ссылки и дополнительные ресурсы

ASI Biont поддерживает подключение к базам данных через API для автоматизации профилирования — подробнее на asibiont.com/courses

Автор: технический писатель и автор курсов по AI. Специализация — оптимизация производительности и системное программирование.

← Все статьи

Комментарии

Читайте также

Squarespace и AI: как автоматизировать магазин без кода с ASI Biont и сэкономить 15 часов в неделю

7 июля 2026

Чёрная дыра как commit: что общего у космоса и контроля версий

7 июля 2026

Локальный TTS на CPU: Kokoro — революция в синтезе речи без облачных затрат

7 июля 2026

Claude Cowork выходит на мобильные и веб: Новая эра Vibe Coding для разработчиков

7 июля 2026

Почему 72% инженеров ИИ отдают предпочтение тонкой настройке перед предварительным обучением в 2026 году: Курс по тонкой настройке LLM на Asibiont.com

7 июля 2026

Почему AI-инноваторы выбирают NVIDIA Vera: решающая роль одного потока в масштабе

7 июля 2026

Как AI-агент ASI Biont автоматизирует мониторинг энергопотребления через интеграцию с Energy Meters: пошаговый гайд

7 июля 2026

Облачные технологии в 2026 году: почему курс «Облачные технологии — микросервисы, Kubernetes и облачные технологии» на Asibiont.com — ваш быстрый путь к мастерству

7 июля 2026

Ethernet (W5500, ENC28J60) + ASI Biont: Как превратить проводной датчик в AI-управляемый узел за 5 минут

7 июля 2026