Skip to content

Commit

Permalink
Merge pull request #585 from realpython/python-313
Browse files Browse the repository at this point in the history
Add materials for Python 3.13 tutorials
  • Loading branch information
brendaweles authored Sep 24, 2024
2 parents fe6ef2f + 69982e1 commit 4f9996d
Show file tree
Hide file tree
Showing 18 changed files with 1,469 additions and 0 deletions.
45 changes: 45 additions & 0 deletions python-313/README.md
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.
48 changes: 48 additions & 0 deletions python-313/free-threading-jit/Dockerfile
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/*
218 changes: 218 additions & 0 deletions python-313/free-threading-jit/README.md
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
```
72 changes: 72 additions & 0 deletions python-313/free-threading-jit/benchmarks/gil.py
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())
Loading

0 comments on commit 4f9996d

Please sign in to comment.