Urgent.News

What's breaking now, across thousands of outlets.

Tech

Making Python code twice as fast with type annotations

Rewriting parts of a Python library in Rust with PyO3 is a common way to speed it up these days. If you still want the speedup but don't want to leave the cozy world of Python, there is mypyc . TL;DR : I'll show how to speed up h11 (a small HTTP/1.1 library with 710k dependents on GitHub) by roughly a factor of two by compiling it with mypyc. Along the way I'll walk through the interesting errors…

Making Python Code Twice as Fast with Type Annotations

Python libraries can often be sped up by rewriting certain parts in Rust using PyO3. However, for those who prefer to remain within the Python ecosystem, mypyc offers a solution. In this article, we will explore how to speed up the h11 HTTP/1.1 library by roughly a factor of two by compiling it with mypyc. We'll also discuss the errors encountered during the process and how they were fixed.

Understanding mypyc

Mypyc is an ahead-of-time compiler that transforms Python code annotated with type hints into CPython C extensions. By leveraging type annotations, mypyc can compile the Python code into .so or .pyd files, which hold C extension code. As a result, the Python code runs as native code, precompiled for the specific platform, leading to improved performance.

Type annotations play a crucial role in mypyc to achieve efficiency gains. They help in selecting more efficient representations such as unboxed integers, enabling early binding of function calls and attribute access, and minimizing dynamic checks and namespace lookups. However, whenever regular Python code calls compiled functions, mypyc inserts explicit type checks that raise TypeError if the passed types do not match.

Fixing Type Annotations

To compile the h11 library using mypyc, we first need to address the type annotation errors reported by mypy. We install mypy, configure it in pyproject.toml, and run the following command:

mypy h11 ...

The initial errors indicate a few redundant # type: ignore comments and a few type mismatches. Considering that the library is quite old and supports older Python versions (back to 3.8), we will focus on fixing the annotations for Python 3.10+ compatibility. We break down the process into three stages:

1. Satisfy mypy and get the code to report no errors in strict mode.

2. Ensure mypyc compiles everything successfully.

3. Verify that the compiled code works correctly at runtime, with imports and tests passing.

One of the issues we encounter is the mismatch between bytearray and bytes in various parts of the codebase. To resolve this, we widen the signatures of specific methods such as ReceiveBuffer.maybe_extract_lines(), _decode_header_lines(), validate(), and the Data() constructor to accept both bytes and bytearray. We introduce a helper type, ByteLike, which is a Union of bytes and bytearray, and use it throughout the codebase to ensure compatibility.

After making these changes and removing unnecessary # type: ignore comments, mypy reports no errors, indicating that our type annotations are now compatible with mypyc. However, compiling the code with mypyc alone is not sufficient. Running mypyc h11 --exclude h11/tests/ produces a series of errors that need to be addressed.

One such error is related to the Sentinel metaclass in h11/_util.py. This metaclass has a custom __new__ method that asserts specific conditions and returns a custom instance. To fix this error, we need to ensure that the Sentinel metaclass is correctly defined and compatible with mypyc's compilation process.

By addressing these errors and making the necessary adjustments to the codebase, we can successfully compile h11 with mypyc. The resulting binary holds native C extension code, which runs faster than the original Python implementation.

In conclusion, by employing type annotations and utilizing mypyc, we can significantly speed up Python libraries without having to switch to Rust or other compiled languages. The process involves fixing type annotations, ensuring compatibility with mypyc's compilation, and verifying the correctness of the compiled code at runtime. With these steps, we were able to achieve a substantial performance improvement in the h11 library while remaining within the familiar Python ecosystem.

Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.

Read the original at dev.to →

More in Tech

More from Saturday 19 September →