What is Codon?
Codon is a high-performance Python compiler that compiles Python code to native machine code without any runtime overhead. Typical speedups over Python are on the order of 100x or more, on a single thread. Codon supports native multithreading which can lead to speedups many times higher still.[1] It uses the LLVM framework to compile to LLVM bytecode and then to specific machine code.[2]
In simple terms, it is Python but compiled.
I first learned about Codon from an episode of C++ Weekly by Jason Turner (Ep 366 - C++ vs Compiled Python (Codon)) where he compares the performance of Python, C++, Python (jit), Python with C++ (cppyy) and Codon along with his Ep 362. He compares the performance by running his version of Conway’s game of life.
Since then, I worked with Codon for the first time during my internship at Viant. I worked on a
POC where I improved the performance of their Python scripts by 30x
on average on a single thread.
Installing Codon
These installation steps would be specific to macOS (arm64) and may not work in every case
- Installing pre-built binaries
# Execute the following command (from https://github.com/exaloop/codon#install)
# Press "y" to add Codon to PATH during installation
/bin/bash -c "$(curl -fsSL https://exaloop.io/install.sh)"
- Setting CODON_PYTHON environment variable
We need to set the CODON_PYTHON
env var to point to the appropriate Python shared library. On macOS with a brew-installed
python, this shared library can be something similar to -
/opt/homebrew/Cellar/python@3.11/3.11.3/Frameworks/Python.framework/Versions/3.11/lib/libpython3.11.dylib
# Run this command to find the base path of Python.framework
brew ls python3
Once found, set the env variable in the shell configuration file (~/.zshrc by default). Add the following line to the end of the file -
export CODON_PYTHON=<path-to-dylib-python-file>
Basics
If you know Python, you already know 99% of Codon. You can start using Codon directly, while keeping some differences in mind -
- In most cases you need to explicitly specify data types. In cases where you do not specify the data type and Codon cannot infer the type, it will treat as a Generic Python Object (PyObj)
def add(x: int, y: int) -> int:
return x + y
- It’s best to keep the collections homogeneous or specifying the types during compile-time
t: tuple[str, int, bool]
t = ("abc", 1, False)
l: list[str] = [] # homogeneous lists
l.append("xyz")
- Once the CODON_PYTHON env var is set, you can import Python libraries using from python import
from python import numpy as np
Building/Running Codon files
By default, the codon run
command compiles and runs in the DEBUG
mode. To compile and run with optimizations -
codon run --release <path-to-codon-file>
To build executables use the codon build
command -
codon build --release -o <exec-name> <path-to-codon-file>
Python vs. Codon
To compare the Python vs. Codon performance, we will use the following code to calculate the 40th fibonacci number. This can be found in the documentation.
# fib.py
from time import time
def fib(n):
return n if n < 2 else fib(n - 1) + fib(n - 2)
t0 = time()
ans = fib(40)
t1 = time()
print(f'Computed fib(40) = {ans} in {t1 - t0} seconds.')
From the above screenshot, we can see that Codon is ~38x
faster than Python!
Conclusion
Working with Codon is really exciting. To have the syntax of Python and such improved performance is amazing. There
are many other features that Codon has - native multithreading (no GIL!!), extending classes (including built-in classes
like int
, str
), the @python
decorator, and many more. I might explore more features in other posts related to Codon.
References
[1] https://docs.exaloop.io/codon
[2] https://medium.com/intuition/codon-a-python-compiler-3d5322e1c0a5