-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #585 from realpython/python-313
Add materials for Python 3.13 tutorials
- Loading branch information
Showing
18 changed files
with
1,469 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
# Python 3.13 Demos | ||
|
||
This repository contains sample code and data files that demonstrate some of the new features in Python 3.13. | ||
|
||
## Introduction | ||
|
||
You need Python 3.13 installed to run these examples. See the following tutorial instructions: | ||
|
||
- [How Can You Install a Pre-Release Version of Python](https://realpython.com/python-pre-release/) | ||
|
||
Note that for testing the free-threading and JIT features, you'll need to build Python from source code with additional compiler flags enabled, as [explained in the tutorial](https://realpython.com/python313-free-threading-jit/#get-your-hands-on-the-new-features). Alternatively, you can run benchmarks using Docker containers as [explained below](#free-threading-and-jit). | ||
|
||
You can learn more about Python 3.13's new features in the following Real Python tutorials: | ||
|
||
<!-- - [Python 3.13: Cool New Features for You to Try](https://realpython.com/python313-new-features/) --> | ||
- [Python 3.13 Preview: Free Threading and a JIT Compiler](https://realpython.com/python313-free-threading-jit/) | ||
- [Python 3.13 Preview: A Modern REPL](https://realpython.com/python313-repl) | ||
|
||
You'll find examples from these tutorials in this repository. | ||
|
||
## Examples | ||
|
||
This section only contains brief instructions on how you can run the examples. See the tutorials for technical details. | ||
|
||
### REPL | ||
|
||
The following examples are used to demonstrate different features of the new REPL: | ||
|
||
- [`tab_completion.py`](repl/tab_completion.py) | ||
- [`multiline_editing.py`](repl/multiline_editing.py) | ||
- [`power_factory.py](repl/power_factory.py) | ||
- [`guessing_game.py](repl/guessing_game.py) | ||
|
||
### Free-Threading and JIT | ||
|
||
You need to enable a few build options to try out the free-threading and JIT features in Python 3.13. You can find more information in the dedicated [README file](free-threading-jit/README.md). | ||
|
||
## Authors | ||
|
||
- **Bartosz Zaczyński**, E-mail: [[email protected]]([email protected]) | ||
- **Geir Arne Hjelle**, E-mail: [[email protected]]([email protected]) | ||
|
||
## License | ||
|
||
Distributed under the MIT license. See [`LICENSE`](../LICENSE) for more information. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
FROM ubuntu:latest AS free-threaded-builder | ||
|
||
# Install dependencies | ||
RUN apt update && \ | ||
apt upgrade -y && \ | ||
DEBIAN_FRONTEND=noninteractive apt install -y \ | ||
wget unzip build-essential pkg-config zlib1g-dev python3 clang | ||
|
||
# Download Python 3.13 source code | ||
ARG BASE_URL=https://github.com/python/cpython/archive/refs/tags | ||
ARG ZIP_FILE=v3.13.0rc1.zip | ||
RUN wget -P /tmp $BASE_URL/$ZIP_FILE && \ | ||
unzip -d /tmp /tmp/$ZIP_FILE | ||
|
||
# Build free-threaded Python 3.13 | ||
WORKDIR /tmp/cpython-3.13.0rc1/ | ||
RUN ./configure --disable-gil --enable-experimental-jit --enable-optimizations && \ | ||
make -j$(nproc) && \ | ||
make altinstall | ||
|
||
FROM free-threaded-builder AS jit-builder | ||
|
||
# Build Python 3.13 with JIT only | ||
WORKDIR /tmp/cpython-3.13.0rc1/ | ||
RUN make clean && \ | ||
./configure --enable-experimental-jit --enable-optimizations --prefix=/python3.13-jit && \ | ||
make -j$(nproc) && \ | ||
make install && \ | ||
ln -s /python3.13-jit/bin/python3.13 /usr/local/bin/python3.13j | ||
|
||
FROM jit-builder AS stock-builder | ||
|
||
# Build stock Python 3.13 | ||
WORKDIR /tmp/cpython-3.13.0rc1/ | ||
RUN make clean && \ | ||
./configure --enable-optimizations && \ | ||
make -j$(nproc) && \ | ||
make install | ||
|
||
FROM ubuntu:latest AS final | ||
|
||
COPY --from=jit-builder /python3.13-jit /python3.13-jit | ||
COPY --from=stock-builder /usr/local /usr/local | ||
|
||
# Install Python provided by the system | ||
RUN apt update && \ | ||
apt install -y python3 && \ | ||
apt clean && rm -rf /var/lib/apt/lists/* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,218 @@ | ||
# Python 3.13 Preview: Free Threading and a JIT Compiler | ||
|
||
This repository contains sample code and data files for the [Python 3.13 Preview: Free Threading and a JIT Compiler](https://realpython.com/python313-free-threading-jit/) tutorial on Real Python. | ||
|
||
## Build the Docker Image | ||
|
||
Build Python 3.13 from source code using Docker: | ||
|
||
```sh | ||
$ docker image build --tag pythons . | ||
``` | ||
|
||
**Note:** If you want to compile your C extension modules, then you'll need to keep the intermediate stage with the required build tools: | ||
|
||
```sh | ||
$ docker build --target stock-builder --tag builder . | ||
$ docker image build --tag pythons . | ||
``` | ||
|
||
## Run Performance Benchmarks Using Docker | ||
|
||
Change directory to the `benchmarks/` folder: | ||
|
||
```sh | ||
$ cd benchmarks/ | ||
``` | ||
|
||
### Free Threading | ||
|
||
Run the benchmark against Python 3.12 shipped with the system: | ||
|
||
```sh | ||
$ docker run --rm -v "$(pwd)":/app -w /app pythons python3.12 gil.py --threads=4 | ||
====================================================== | ||
💻 Linux 64bit with 4x CPU cores (x86_64 Little Endian) | ||
🐍 CPython 3.12.3 /usr/bin/python3.12 | ||
Free Threading: unsupported | ||
JIT Compiler: unsupported | ||
====================================================== | ||
Running 4 threads: 7.33s | ||
``` | ||
|
||
Run the benchmark against stock Python 3.13: | ||
|
||
```sh | ||
$ docker run --rm -v "$(pwd)":/app -w /app pythons python3.13 gil.py --threads=4 | ||
====================================================== | ||
💻 Linux 64bit with 4x CPU cores (x86_64 Little Endian) | ||
🐍 CPython 3.13.0rc1 /usr/local/bin/python3.13 | ||
Free Threading: unsupported | ||
JIT Compiler: unsupported | ||
====================================================== | ||
Running 4 threads: 7.51s | ||
``` | ||
|
||
Run the benchmark against the free-threaded Python 3.13 build with the GIL and JIT disabled: | ||
|
||
```sh | ||
$ docker run --rm -v "$(pwd)":/app -w /app -e PYTHON_JIT=0 pythons python3.13t gil.py --threads=4 | ||
====================================================== | ||
💻 Linux 64bit with 4x CPU cores (x86_64 Little Endian) | ||
🐍 CPython 3.13.0rc1 /usr/local/bin/python3.13t | ||
Free Threading: enabled ✨ | ||
JIT Compiler: disabled | ||
====================================================== | ||
Running 4 threads: 3.25s | ||
``` | ||
|
||
Run the benchmark against the free-threaded Python 3.13 build with the GIL enabled and JIT disabled: | ||
|
||
```sh | ||
$ docker run --rm -v "$(pwd)":/app -w /app -e PYTHON_JIT=0 pythons python3.13t -X gil=1 gil.py --threads=4 | ||
====================================================== | ||
💻 Linux 64bit with 4x CPU cores (x86_64 Little Endian) | ||
🐍 CPython 3.13.0rc1 /usr/local/bin/python3.13t | ||
Free Threading: disabled | ||
JIT Compiler: disabled | ||
====================================================== | ||
Running 4 threads: 13.72s | ||
``` | ||
|
||
### Just-In-Time (JIT) Compiler | ||
|
||
Run the benchmark against Python 3.12 shipped with the system: | ||
|
||
```sh | ||
$ docker run --rm -v "$(pwd)":/app -w /app pythons python3.12 jit.py -n 10000 | ||
====================================================== | ||
💻 Linux 64bit with 4x CPU cores (x86_64 Little Endian) | ||
🐍 CPython 3.12.3 /usr/bin/python3.12 | ||
Free Threading: unsupported | ||
JIT Compiler: unsupported | ||
====================================================== | ||
Running fib() 10,000 times: 6.06s | ||
``` | ||
|
||
Run the benchmark against stock Python 3.13: | ||
|
||
```sh | ||
$ docker run --rm -v "$(pwd)":/app -w /app pythons python3.13 jit.py -n 10000 | ||
====================================================== | ||
💻 Linux 64bit with 4x CPU cores (x86_64 Little Endian) | ||
🐍 CPython 3.13.0rc1 /usr/local/bin/python3.13 | ||
Free Threading: unsupported | ||
JIT Compiler: unsupported | ||
====================================================== | ||
Running fib() 10,000 times: 5.68s | ||
``` | ||
|
||
Run the benchmark against Python 3.13 with the JIT enabled but GIL unsupported: | ||
|
||
```sh | ||
$ docker run --rm -v "$(pwd)":/app -w /app pythons python3.13j jit.py -n 10000 | ||
====================================================== | ||
💻 Linux 64bit with 4x CPU cores (x86_64 Little Endian) | ||
🐍 CPython 3.13.0rc1 /usr/local/bin/python3.13j | ||
Free Threading: unsupported | ||
JIT Compiler: enabled ✨ | ||
====================================================== | ||
Running fib() 10,000 times: 5.27s | ||
``` | ||
|
||
## Disassemble JIT's Micro-Ops | ||
|
||
Reveal mico-ops generated by the JIT compiler: | ||
|
||
```sh | ||
$ cd benchmarks/ | ||
$ docker run --rm -it -v "$(pwd)":/app -w /app pythons python3.13j -i uops.py | ||
====================================================== | ||
💻 Linux 64bit with 4x CPU cores (x86_64 Little Endian) | ||
🐍 CPython 3.13.0rc1 /usr/local/bin/python3.13j | ||
Free Threading: unsupported | ||
JIT Compiler: enabled ✨ | ||
====================================================== | ||
>>> def fib(n): | ||
... a, b = 0, 1 | ||
... for _ in range(n): | ||
... a, b = b, a + b | ||
... return a | ||
... | ||
|
||
>>> fib(10) | ||
55 | ||
|
||
>>> reveal_code(fib) | ||
Micro-ops unavailable | ||
|
||
>>> fib(10) | ||
55 | ||
|
||
>>> reveal_code(fib) | ||
_START_EXECUTOR | ||
_TIER2_RESUME_CHECK | ||
_ITER_CHECK_RANGE | ||
_GUARD_NOT_EXHAUSTED_RANGE | ||
_ITER_NEXT_RANGE | ||
_STORE_FAST_3 | ||
_LOAD_FAST_2 | ||
_LOAD_FAST_1 | ||
_LOAD_FAST_2 | ||
_GUARD_BOTH_INT | ||
_BINARY_OP_ADD_INT | ||
_STORE_FAST_2 | ||
_STORE_FAST_1 | ||
_JUMP_TO_TOP | ||
_DEOPT | ||
_EXIT_TRACE | ||
_EXIT_TRACE | ||
_ERROR_POP_N | ||
_EXIT_TRACE | ||
_ERROR_POP_N | ||
``` | ||
## Compile the C Extension Module | ||
Build an intermediate stage with the necessary build tools if you haven't before: | ||
```shell | ||
$ docker build --target stock-builder --tag builder . | ||
``` | ||
Change directory to the `c_extension/` folder: | ||
```sh | ||
$ cd c_extension/ | ||
``` | ||
Start a Docker container from the builder stage and set common compiler flags: | ||
```sh | ||
$ docker run --rm -it -v "$(pwd)":/app -w /app builder | ||
root@8312f61fbb6d:/app# CFLAGS='-shared -fPIC -O3' | ||
``` | ||
Compile the `greeter` module for stock Python 3.13: | ||
```sh | ||
root@8312f61fbb6d:/app# gcc greeter.c $CFLAGS \ | ||
$(python3.13-config --includes) \ | ||
-o greeter_stock.so | ||
``` | ||
Compile the `greeter` module for the free-threaded Python 3.13: | ||
```sh | ||
root@8312f61fbb6d:/app# gcc greeter.c $CFLAGS \ | ||
$(python3.13t-config --includes) \ | ||
-o greeter_threaded_v1.so | ||
``` | ||
Compile the `greeter_threaded` module for the free-threaded Python 3.13: | ||
```sh | ||
root@8312f61fbb6d:/app# gcc greeter_threaded.c $CFLAGS \ | ||
$(python3.13t-config --includes) \ | ||
-o greeter_threaded_v2.so | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
from argparse import ArgumentParser | ||
from concurrent.futures import ThreadPoolExecutor | ||
from csv import DictWriter | ||
from functools import wraps | ||
from os import cpu_count | ||
from pathlib import Path | ||
from time import perf_counter | ||
from typing import NamedTuple | ||
|
||
from pyinfo import print_details, python_short | ||
|
||
CSV_PATH = Path(__file__).with_suffix(".csv") | ||
DEFAULT_N = 35 | ||
|
||
|
||
class Record(NamedTuple): | ||
python: str | ||
threads: int | ||
seconds: float | ||
|
||
def save(self): | ||
empty = not CSV_PATH.exists() | ||
with CSV_PATH.open(mode="a", encoding="utf-8", newline="") as file: | ||
writer = DictWriter(file, Record._fields) | ||
if empty: | ||
writer.writeheader() | ||
writer.writerow(self._asdict()) | ||
|
||
|
||
def parse_args(): | ||
parser = ArgumentParser() | ||
parser.add_argument("-t", "--threads", type=int, default=cpu_count()) | ||
parser.add_argument("-n", type=int, default=DEFAULT_N) | ||
return parser.parse_args() | ||
|
||
|
||
def main(args): | ||
print_details() | ||
benchmark(args.threads, args.n) | ||
|
||
|
||
def timed(function): | ||
@wraps(function) | ||
def wrapper(num_threads, n): | ||
t1 = perf_counter() | ||
result = function(num_threads, n) | ||
t2 = perf_counter() | ||
duration = t2 - t1 | ||
print(f"\b\b\b: {duration:.2f}s") | ||
Record(python_short(), num_threads, duration).save() | ||
return result | ||
|
||
return wrapper | ||
|
||
|
||
@timed | ||
def benchmark(num_threads, n): | ||
with ThreadPoolExecutor(max_workers=num_threads) as executor: | ||
for _ in range(num_threads): | ||
executor.submit(fib, n) | ||
if num_threads > 1: | ||
print(f"Running {num_threads} threads...", end="", flush=True) | ||
else: | ||
print("Running 1 thread...", end="", flush=True) | ||
|
||
|
||
def fib(n): | ||
return n if n < 2 else fib(n - 2) + fib(n - 1) | ||
|
||
|
||
if __name__ == "__main__": | ||
main(parse_args()) |
Oops, something went wrong.