Архив рубрики ~Лента новостей~

Python 3.14 и его новый JIT-компилятор

Python 3.14 и его новый JIT-компилятор
Python 3.14 и его новый JIT-компилятор

Технический обзор и некоторые сравнительные показатели.

Делиться

Изображение создано искусственным интеллектом.

Выпуск Python 3.14 знаменует собой важный этап в эволюции самого популярного в мире языка программирования. Хотя Python давно известен своей читаемостью и обширной экосистемой, скорость его выполнения часто оставалась «слоном в комнате».

С выходом версии 3.14 команда разработчиков ядра CPython представила не одну, а две самые ожидаемые функции за последнее время.

Конец GIL

Я уже писал об этом раньше. Теперь в Python доступна настоящая параллельная обработка, если она вам нужна. Если вы хотите узнать больше о Python без GIL, я оставлю ссылку на свою статью об этом в конце.

Компилятор Just-In-Time (JIT)

Эта экспериментальная функция теперь включена непосредственно в официальные установщики, и именно на ней мы сосредоточимся здесь. Это результат многолетней архитектурной подготовки, проведенной основной командой Python и другими, направленной на то, чтобы сделать Python «быстрее по умолчанию», не нарушая экосистему расширений C, которая лежит в основе всего, от анализа данных до веб-бэкендов.

В этой статье мы заглянем под капот новой системы JIT, рассмотрим, чем она отличается от предыдущих методов оптимизации, и ознакомимся с некоторыми методами бенчмаркинга, которые помогут вам решить, пора ли попробовать JIT на ваших рабочих нагрузках.

Что такое новый JIT-компилятор (Just-In-Time) в Python?

Чтобы понять принцип работы JIT-компилятора версии 3.14, нам нужно разобраться, как традиционно работает Python. Стандартный Python (CPython) — это интерпретируемый язык. При запуске скрипта ваш код компилируется в байт-код, который представляет собой набор инструкций, выполняемых виртуальной машиной CPython.

JIT-компилятор меняет этот процесс. Вместо того чтобы просто построчно интерпретировать байт-код, JIT-компилятор отслеживает, какие части вашего кода выполняются наиболее часто («горячие» пути). Когда функция или цикл считаются «горячими», JIT-компилятор преобразует байт-код в машинный код (инструкции, понятные процессору). Затем, при следующем вызове кода, интерпретация не требуется. Вместо этого он просто выполняется как есть. Это может значительно сэкономить время, как мы увидим позже.

Как JIT-компилятор интегрируется в CPython

JIT-компилятор Python 3.14 не является полной переработкой кода. Он разработан как компонент, который можно включить по желанию и который работает параллельно с существующим интерпретатором. В нем используется метод «копирования и внесения изменений», который позволяет JIT-компилятору быть легковесным и переносимым на различные архитектуры ЦП без необходимости использования громоздкого и сложного бэкенда компилятора, такого как LLVM.

Что изменилось в Python 3.14?

В Python 3.13 существовал базовый экспериментальный JIT-компилятор, но он был отключен по умолчанию. Для его тестирования необходимо было клонировать исходный код CPython и скомпилировать его со специальными экспериментальными флагами, такими как - - enable-experimental-jit .

С выходом Python 3.14 все изменилось. JIT-компилятор стал доступен в официальных установщиках .msi (Windows) и .pkg (macOS). Это также означало, что для использования преимуществ JIT-компиляции больше не требовался компилятор C на вашем компьютере. Хотя это все еще «экспериментальная» функция, ее включение в официальные бинарные файлы свидетельствует о том, что основная команда считает JIT-компилятор достаточно стабильным для широкого тестирования сообществом.

Установка Python 3.14

Перейдите по ссылке https://www.python.org/downloads/, и вы увидите опцию загрузки версии 3.14. Нажмите на неё, а затем следуйте инструкциям.

В качестве альтернативы, если у вас установлен инструмент UV , вы можете ввести следующее.

 PS C: > uv python install 3.14

Включение системы «точно в срок»

По умолчанию JIT-компилятор отключен . Это мера безопасности; поскольку он является экспериментальным, Совет разработчиков Python хочет гарантировать, что пользователи не столкнутся с неожиданными ухудшениями стабильности или снижением использования памяти без явного выбора этой опции.

Для активации JIT-компилятора используется переменная среды. Она указывает среде выполнения CPython инициализировать JIT-компилятор при запуске.

В Windows (PowerShell):

 $env:PYTHON_JIT=1 python my_script.py

В macOS/Linux (Bash/Zsh):

 PYTHON_JIT=1 python my_script.py

После включения CPython не выполняет JIT-компиляцию всего кода сразу. Он использует систему многоуровневого компиляции. По сути, он сначала пытается выполнить код с наименьшими затратами ресурсов, и тратит усилия на компиляцию/оптимизацию только на те части, которые оказываются наиболее ресурсоемкими.

  • Уровень 0: Стандартная интерпретация.
  • Уровень 1: Специализированный байт-код (введен в версии 3.11).
  • Уровень 2 (JIT): Генерация машинного кода для наиболее часто используемых путей.

Оценка эффективности системы «точно в срок»

При тестировании JIT-компилятора нельзя просто использовать ` time.time()` вокруг функции. JIT-компиляторам требуется период прогрева . Первые несколько итераций цикла могут быть медленнее обычного, пока JIT-компилятор профилирует код, но последующие итерации могут быть значительно быстрее.

Набор инструментов для сравнительного анализа производительности

Ниже представлен полный набор тестов, предназначенный для проверки различных аспектов системы JIT (Just-in-Time — «точно в срок»), от сложных математических вычислений до манипуляций со сложными объектами.

Файл 1: workloads.py

Этот файл содержит три различные задачи, интенсивно использующие ресурсы процессора.

1/ Функция Мандельброта итерирует формулу Мандельброта по сетке пикселей и возвращает контрольную сумму количества итераций для каждого пикселя.

2/ Функция Дейкстры строит детерминированный случайный взвешенный граф и запускает алгоритм Дейкстры, начиная с узла 0, возвращая количество завершенных/посещенных узлов.

3/ Функция Левенштейна генерирует N детерминированных случайных пар строк и возвращает сумму их расстояний Левенштейна.

 from __future__ import annotations import random import heapq # Workload 1: Mandelbrot (CPU + math loops) def mandelbrot(width: int = 1000, height: int = 1000, iters: int = 500) -> int: checksum = 0 for y in range(height): cy = (y / height) * 2.4 - 1.2 for x in range(width): cx = (x / width) * 3.2 - 2.2 zx, zy, count = 0.0, 0.0, 0 while zx * zx + zy * zy <= 4.0 and count < iters: zx, zy = zx * zx - zy * zy + cx, 2.0 * zx * zy + cy count += 1 checksum += count return checksum # Workload 2: Dijkstra (heap + list + logic) def dijkstra(n: int = 10000, edges_per_node: int = 50, seed: int = 123) -> int: rng = random.Random(seed) graph = [[] for _ in range(n)] for u in range(n): for _ in range(edges_per_node): v = rng.randrange(n) if v != u: graph[u].append((v, rng.randrange(1, 30))) dist = [10**12] * n dist[0] = 0 pq = [(0, 0)] visited = 0 while pq: d, u = heapq.heappop(pq) if d != dist[u]: continue visited += 1 for v, w in graph[u]: nd = d + w if nd < dist[v]: dist[v] = nd heapq.heappush(pq, (nd, v)) return visited # Workload 3: Levenshtein distance (dynamic programming) def levenshtein(a: str, b: str) -> int: prev = list(range(len(b) + 1)) for i, ca in enumerate(a, 1): cur = [i] for j, cb in enumerate(b, 1): cur.append(min(cur[j - 1] + 1, prev[j] + 1, prev[j - 1] + (ca != cb))) prev = cur return prev[-1] def levenshtein_batch(n: int = 10000, seed: int = 7, k: int = 50) -> int: """ Deterministic batch: fixed RNG seed, fixed alphabet, fixed string length. Returns the sum of distances. """ rng = random.Random(seed) alphabet = "abc" total = 0 for _ in range(n): a = "".join(rng.choices(alphabet, k=k)) b = "".join(rng.choices(alphabet, k=k)) total += levenshtein(a, b) return total

Файл 2: benchmark.py

Этот скрипт автоматизирует сравнение различных рабочих нагрузок с включенной и выключенной функцией JIT-компиляции.

 import os import time import json import subprocess from pathlib import Path PYTHON_EXE = r"C:UsersthomaAppDataLocalProgramsPythonPython314python.exe" PROJECT_DIR = Path(__file__).resolve().parent # Original workloads (statement prints a result for sanity) WORKLOADS = [ ("mandelbrot", 'from workloads import mandelbrot; print(mandelbrot())'), ("dijkstra", 'from workloads import dijkstra; print(dijkstra())'), ("levenshtein_batch", 'from workloads import levenshtein_batch; print(levenshtein_batch())'), ] N_RUNS = 10 # average of ALL runs (set to 6/10/20 as you like) OUTFILE = PROJECT_DIR / "results_avg.json" def run_once(stmt: str, jit_val: int) -> tuple[float, str]: env = os.environ.copy() env["PYTHON_JIT"] = str(jit_val) # Ensure local workloads.py is importable in subprocess env["PYTHONPATH"] = str(PROJECT_DIR) + (os.pathsep + env.get("PYTHONPATH", "")) t0 = time.perf_counter() p = subprocess.run( [PYTHON_EXE, "-c", stmt], env=env, cwd=str(PROJECT_DIR), capture_output=True, text=True, ) t1 = time.perf_counter() if p.returncode != 0: raise RuntimeError( f"Run failed (PYTHON_JIT={jit_val})nn" f"Statement:n{stmt}nn" f"STDOUT:n{p.stdout}nnSTDERR:n{p.stderr}" ) return (t1 - t0, p.stdout.strip()) def summarize(times: list[float]) -> dict: return { "avg": sum(times) / len(times), "min": min(times), "max": max(times), "runs": times, } def bench_workload(name: str, stmt: str) -> dict: results = {} outputs = {} for jit_val in (0, 1): times = [] outs = [] print(f" PYTHON_JIT={jit_val}: running {N_RUNS} times...") for i in range(1, N_RUNS + 1): dt, out = run_once(stmt, jit_val) times.append(dt) outs.append(out) print(f" run {i}/{N_RUNS}: {dt:.6f}s") results[jit_val] = summarize(times) outputs[jit_val] = outs avg0 = results[0]["avg"] avg1 = results[1]["avg"] speedup = avg0 / avg1 if avg1 else float("inf") delta_pct = (avg1 - avg0) / avg0 * 100.0 if avg0 else 0.0 return { "workload": name, "jit0": results[0], "jit1": results[1], "speedup_jit0_over_jit1": speedup, "delta_pct_jit1_vs_jit0": delta_pct, "outputs": outputs, # sanity: should be stable } def main() -> int: all_results = [] print(f"Using Python: {PYTHON_EXE}") print(f"Project dir: {PROJECT_DIR}") print(f"Runs per setting (avg of all runs): {N_RUNS}n") for name, stmt in WORKLOADS: print(f"=== {name} ===") r = bench_workload(name, stmt) all_results.append(r) print(f"n Averages:") print(f" JIT=0 avg: {r['jit0']['avg']:.6f}s (min {r['jit0']['min']:.6f}, max {r['jit0']['max']:.6f})") print(f" JIT=1 avg: {r['jit1']['avg']:.6f}s (min {r['jit1']['min']:.6f}, max {r['jit1']['max']:.6f})") print(f" Speedup (JIT=0 / JIT=1): {r['speedup_jit0_over_jit1']:.3f}× (Δ={r['delta_pct_jit1_vs_jit0']:+.2f}%)n") # Optional: warn if outputs vary across runs (nondeterminism) if len(set(r["outputs"][0])) != 1: print(" !! WARNING: JIT=0 output differs across runs (nondeterministic workload?)") if len(set(r["outputs"][1])) != 1: print(" !! WARNING: JIT=1 output differs across runs (nondeterministic workload?)") OUTFILE.write_text(json.dumps(all_results, indent=2), encoding="utf-8") print(f"Wrote: {OUTFILE}") return 0 if __name__ == "__main__": raise SystemExit(main())

Вот мои результаты.

 C:Usersthomaprojectspython_jit>C:UsersthomaAppDataLocalProgramsPythonPython314python.exe benchmark.py Using Python: C:UsersthomaAppDataLocalProgramsPythonPython314python.exe Project dir: C:Usersthomaprojectspython_jit Runs per setting (avg of all runs): 10 === mandelbrot === PYTHON_JIT=0: running 10 times... run 1/10: 6.890924s run 2/10: 6.950737s run 3/10: 7.265357s run 4/10: 6.947150s run 5/10: 6.932333s run 6/10: 6.939378s run 7/10: 7.194705s run 8/10: 6.995550s run 9/10: 6.902696s run 10/10: 7.256164s PYTHON_JIT=1: running 10 times... run 1/10: 5.216740s run 2/10: 5.241888s run 3/10: 5.350822s run 4/10: 5.246767s run 5/10: 5.294771s run 6/10: 5.273295s run 7/10: 5.272135s run 8/10: 5.617062s run 9/10: 5.251656s run 10/10: 5.239060s Averages: JIT=0 avg: 7.027499s (min 6.890924, max 7.265357) JIT=1 avg: 5.300420s (min 5.216740, max 5.617062) Speedup (JIT=0 / JIT=1): 1.326× (Δ=-24.58%) === dijkstra === PYTHON_JIT=0: running 10 times... run 1/10: 0.235401s run 2/10: 0.227603s run 3/10: 0.244492s run 4/10: 0.232971s run 5/10: 0.249589s run 6/10: 0.232229s run 7/10: 0.229422s run 8/10: 0.238399s run 9/10: 0.230657s run 10/10: 0.235772s PYTHON_JIT=1: running 10 times... run 1/10: 0.238862s run 2/10: 0.239266s run 3/10: 0.240312s run 4/10: 0.231413s run 5/10: 0.232692s run 6/10: 0.233783s run 7/10: 0.230016s run 8/10: 0.237760s run 9/10: 0.240895s run 10/10: 0.246033s Averages: JIT=0 avg: 0.235653s (min 0.227603, max 0.249589) JIT=1 avg: 0.237103s (min 0.230016, max 0.246033) Speedup (JIT=0 / JIT=1): 0.994× (Δ=+0.62%) === levenshtein_batch === PYTHON_JIT=0: running 10 times... run 1/10: 2.176256s run 2/10: 2.171253s run 3/10: 2.171834s run 4/10: 2.170444s run 5/10: 2.149874s run 6/10: 2.162820s run 7/10: 2.171975s run 8/10: 2.199151s run 9/10: 2.168398s run 10/10: 2.167821s PYTHON_JIT=1: running 10 times... run 1/10: 1.575666s run 2/10: 1.612615s run 3/10: 1.571106s run 4/10: 1.584650s run 5/10: 1.579948s run 6/10: 1.582633s run 7/10: 1.593924s run 8/10: 1.573608s run 9/10: 1.581427s run 10/10: 1.578553s Averages: JIT=0 avg: 2.170983s (min 2.149874, max 2.199151) JIT=1 avg: 1.583413s (min 1.571106, max 1.612615) Speedup (JIT=0 / JIT=1): 1.371× (Δ=-27.06%)

Интерпретация результатов

Как видите, результаты неоднозначны. Это нормально для экспериментальной системы «точно в срок».

  • Ускорение на 10–30%: часто встречается в циклах «чистого Python» (например, в тестах Мандельброта или Левенштейна), где JIT-компилятор может избежать накладных расходов цикла диспетчеризации байт-кода.
  • Улучшение на 0%: характерно для задач, интенсивно использующих ввод-вывод, или кода, активно использующего расширения C. Код Дейкстры не ускорился, потому что его время выполнения в основном определяется операциями с кучей/кортежами и ресурсоемкими операциями выделения памяти, которые текущий JIT-компилятор CPython не оптимизирует в значительной степени, поэтому любая экономия ресурсов интерпретатора теряется в общем объеме кода.

Когда использовать JIT-компиляцию в Python 3.14?

Система JIT (точно в срок) — мощный инструмент, но это не «волшебная кнопка». Из моего опыта, систему JIT следует использовать, когда у вас есть…

  • Логика, ресурсоемкая для ЦП: Ваше приложение выполняет сложные вычисления, обработку данных или сложную логику на чистом Python.
  • Длительно выполняющиеся процессы: веб-серверы (Gunicorn/Uvicorn) или фоновые рабочие процессы (Celery), работающие в течение нескольких часов, что дает JIT-компилятору достаточно времени для прогрева и оптимизации ресурсоемких процессов.
  • Экспериментальное тестирование: Вам необходимо подготовить свой код к будущим версиям Python (3.15+), где JIT-компилятор, вероятно, будет работать более агрессивно.

И избегайте этого, если у вас есть…

  • Приложения, сильно зависящие от операций ввода-вывода : если ваше приложение просто ожидает запросов к базе данных или ответов от API, JIT-компилятор не поможет.
  • В средах с ограниченными ресурсами памяти: небольшие лямбда-функции или крошечные контейнеры могут страдать от увеличения объема памяти, занимаемого JIT-кэшем.
  • Инструменты командной строки с коротким сроком службы: скрипту, выполняющемуся менее чем за секунду, не нужен JIT-компилятор.

Перспективы на будущее: за пределами 3.14

Основная команда разработчиков CPython рассматривает версию 3.14 как «год закладки фундамента». Ожидается, что будущие итерации (Python 3.15 и 3.16) будут включать в себя:

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

Краткое содержание

JIT-компилятор в Python 3.14 — это не просто патч для повышения производительности. Это заявление о намерениях. Он показывает, что Python всерьез намерен сократить разрыв в производительности с такими языками, как Java или Go, сохраняя при этом простоту «с батарейками», которая сделала его знаменитым.

Для большинства разработчиков JIT-компилятор — это просто ещё один инструмент, за которым стоит следить. Если производительность важна в ваших проектах, стоит протестировать Python 3.14 на существующих рабочих нагрузках. Несколько тестов на наиболее важных участках кода могут выявить прирост производительности там, где вы его не ожидали.

Вот ссылка на мою предыдущую статью о GIL Fee Python, о которой я упоминал в начале.

Python 3.14 и конец GIL

Томас Рид. Все материалы от Томаса Рида.

Источник: towardsdatascience.com

Оцените материал:

Поделиться
Понравилась статья? Расскажите другим
ВКонтакте
Читайте также
Архив рубрики ~Обо всем~ Apple подтверждает предстоящее повышение цен — во сколько это обойдется вам? Архив рубрики ~Обо всем~ [Перевод] Учёный из Гарварда утверждает, что ваш организм способен победить смерть, вспомнив, как снова стать молодым Архив рубрики ~Обо всем~ ТЕОРИЯ, КОТОРУЮ ФИЗИКИ НЕНАВИДЯТ, А ПРОВЕРИТЬ ПОКА НЕ МОГУТ Новости робототехники Компания Go рассматривает возможность приобретения других компаний в сфере роботакси после крупнейшего IPO в Японии в 2026 году. Вот почему это важно. Архив рубрики ~Обо всем~ У меня был тромб. Диагностика с помощью ИИ, возможно, спасла мне жизнь | Глеб Ципурский Архив рубрики ~Идей копилка~ ИИ для бизнеса: что реально приносит деньги, а что просто шум Архив рубрики ~Обо всем~ Я попытался запланировать выполнение ETL-процесса. Вот чего я не ожидал. Новости робототехники IEEE запускает виртуальный учебный курс по большим языковым моделям. Новости робототехники Он сделал так, чтобы ваш бесплатный видеоплеер работал без сбоев. Теперь он делает то же самое для роботов. Новости робототехники ИИ добрался до коров. И оказался бизнесом на $2 млрд Архив рубрики ~Обо всем~ Раньше, чем ожидалось? Полезная квантовая коррекция ошибок обещана к 2028 году. Архив рубрики ~Обо всем~ Магазин отказал в гарантии покупателю RX 9070 XT, пострадавшей от плавления 12V-2×6 Архив рубрики ~Коротко из Telegram~ AVG Cleaner — Полезная системная утилита, которая позволит интеллектуально анализировать… Архив рубрики ~Коротко из Telegram~ ✅Solid Explorer File Manager ▶️Версия: 3.5.7 💬Solid Explorer File Manager… Архив рубрики ~Обо всем~ Apple подтверждает предстоящее повышение цен — во сколько это обойдется вам? Архив рубрики ~Обо всем~ [Перевод] Учёный из Гарварда утверждает, что ваш организм способен победить смерть, вспомнив, как снова стать молодым Архив рубрики ~Обо всем~ ТЕОРИЯ, КОТОРУЮ ФИЗИКИ НЕНАВИДЯТ, А ПРОВЕРИТЬ ПОКА НЕ МОГУТ Новости робототехники Компания Go рассматривает возможность приобретения других компаний в сфере роботакси после крупнейшего IPO в Японии в 2026 году. Вот почему это важно. Архив рубрики ~Обо всем~ У меня был тромб. Диагностика с помощью ИИ, возможно, спасла мне жизнь | Глеб Ципурский Архив рубрики ~Идей копилка~ ИИ для бизнеса: что реально приносит деньги, а что просто шум Архив рубрики ~Обо всем~ Я попытался запланировать выполнение ETL-процесса. Вот чего я не ожидал. Новости робототехники IEEE запускает виртуальный учебный курс по большим языковым моделям. Новости робототехники Он сделал так, чтобы ваш бесплатный видеоплеер работал без сбоев. Теперь он делает то же самое для роботов. Новости робототехники ИИ добрался до коров. И оказался бизнесом на $2 млрд Архив рубрики ~Обо всем~ Раньше, чем ожидалось? Полезная квантовая коррекция ошибок обещана к 2028 году. Архив рубрики ~Обо всем~ Магазин отказал в гарантии покупателю RX 9070 XT, пострадавшей от плавления 12V-2×6 Архив рубрики ~Коротко из Telegram~ AVG Cleaner — Полезная системная утилита, которая позволит интеллектуально анализировать… Архив рубрики ~Коротко из Telegram~ ✅Solid Explorer File Manager ▶️Версия: 3.5.7 💬Solid Explorer File Manager…

Оставить комментарий