LLVM-backed JIT for Python using CPython's own parser, with transparent interpreter fallback. Targets CPU (working), NVIDIA GPU and FPGA (experimental IR emission).
Project description
HanaJit
An LLVM-backed JIT compiler for Python — compile ordinary functions and NumPy code to native machine code, with a transparent interpreter fallback and no DSL to learn.
ها أنا — "here I am" (Darija) + JIT
Project status: alpha. HanaJit is under active development. The CPU compiler is stable and tested (207 tests across Python 3.10–3.14 on Linux, Windows, and macOS). GPU support is code generation only — hanajit emits GPU assembly that real vendor toolchains accept, but does not yet launch kernels on a GPU (see Scope & honest limitations). APIs may change before 1.0.
What is HanaJit?
HanaJit takes a normal Python function, compiles it to optimized native machine code through LLVM (via llvmlite), and runs that instead of the interpreter — often 10–100× faster than CPython, and competitive with or faster than Numba on the workloads it targets.
You do not learn a new language, annotate types, or restructure your data. You add a decorator:
from hanajit import jit
@jit
def sum_squares(x):
acc = 0.0
for i in range(len(x)):
acc += x[i] * x[i]
return acc
The first call with a given argument type compiles a specialization; later calls reuse it. Anything HanaJit can't compile falls back to the normal Python interpreter transparently, with a single warning — so adopting it never breaks working code.
Why it exists
HanaJit was built in the R&D pipeline of EZducate, an AI-powered special-education platform, to accelerate the numeric and array-heavy code (on-device inference, simulation, data processing) that sits between "too slow in pure Python" and "not worth rewriting in C." It is designed around three principles:
- No DSL. It compiles the Python you already wrote, parsed by CPython's
own
astmodule — not a restricted dialect, not a new syntax. - Correctness is never negotiable. Every optimization is either provably equivalent, or a bounded, opt-in trade-off (like float32 precision) documented with its exact cost. Anything HanaJit can't compile runs in the interpreter rather than compiling something wrong.
- Honesty about performance. Every number in this README is measured and
reproducible from the scripts in
benchmarks/. Where a feature ties or loses, we say so.
Highlights
- Drop-in
@jiton ordinary functions — loops, recursion, math, NumPy. - Lazy fusion engine — whole-array NumPy expressions compile to a single allocation-free loop (no temporaries), beating NumPy and Numba structurally.
reduce_reassoc=True— numpy-class reduction throughput by vectorizing accumulators, without global fast-math; integers stay bit-exact.- Native float32 — pass a
float32array and get 32-bit compute: half the memory bandwidth, 2× the SIMD lanes, exact (bounded) f32 precision. - Genetic optimizer (
f.evolve()) — an equivalence-preserving search over compilation strategies that tunes each kernel to your machine. - Near-zero dispatch — ~20–50 ns/call via an LLVM-compiled vectorcall path on CPython 3.12+.
- Multithreading —
prange,pmap,nogil, andparallel=Trueauto-parallelization. - GPU code generation — emits CUDA PTX, AMD GCN, Intel SPIR-V, and Apple Metal, each validated against the real vendor assembler (emission only; see scope below).
- Transparent fallback — unsupported code runs in CPython automatically.
Performance
Measured on a single core (shared CI container — ratios are reliable,
absolute milliseconds are noisy; rerun on your hardware with the scripts in
benchmarks/). Compared against NumPy 2.x and Numba 0.66.
| Benchmark | Result |
|---|---|
| 5-operation fused NumPy reduction | 3.2× vs NumPy, 3.9× vs Numba |
20M-element reduction (reduce_reassoc) |
2.5× over the float64 baseline |
20M-element reduction (float32 + reduce_reassoc) |
3.2× over the float64 baseline |
Genetic optimizer (evolve) on an fp reduction |
2.1×, equivalence-guaranteed |
| Dispatch / call overhead | ~36 ns (3.5× faster than Numba) |
fib(30) recursion |
1.85× vs Numba |
How to read this honestly: on bare scalar loops, HanaJit and Numba are at parity — they share the LLVM backend, so loop codegen is a wash. HanaJit's wins come from (a) the fusion engine, which is a structural advantage on array expressions, (b) targeted reduction vectorization, (c) native float32, (d) dispatch latency and cold-start. Where the underlying operation is already at the hardware roofline, there is no magic to extract, and we don't pretend otherwise.
Installation
HanaJit requires Python 3.10+ and depends only on llvmlite (which ships
prebuilt LLVM wheels for all major platforms — you do not need to install
LLVM yourself).
From PyPI
Not yet published. Once released, this will be:
pip install hanajitSee
docs/publishing.mdfor the release process.
From GitHub (available now)
Install the latest version directly from this repository:
pip install "git+https://github.com/ezducate/HanaJit.git"
Pin to a specific tag or commit for reproducibility:
# a released tag
pip install "git+https://github.com/ezducate/HanaJit.git@v0.20.0"
# a specific commit
pip install "git+https://github.com/ezducate/HanaJit.git@<commit-sha>"
Add it to requirements.txt:
hanajit @ git+https://github.com/ezducate/HanaJit.git@v0.20.0
Or to pyproject.toml dependencies:
dependencies = [
"hanajit @ git+https://github.com/ezducate/HanaJit.git@v0.20.0",
]
Optional extras
# run the test suite
pip install "hanajit[test] @ git+https://github.com/ezducate/HanaJit.git"
# run the benchmarks (adds numba + scipy for comparison)
pip install "hanajit[bench] @ git+https://github.com/ezducate/HanaJit.git"
From a local clone (for development)
git clone https://github.com/ezducate/HanaJit.git
cd HanaJit
pip install -e ".[test]" # editable install with test deps
python -m pytest tests/ -q # run the suite
python -m hanajit.doctor # environment + capability diagnostic
Quick start
Accelerate a numeric function
from hanajit import jit
import numpy as np
@jit
def euclidean_norm(x):
acc = 0.0
for i in range(len(x)):
acc += x[i] * x[i]
return acc ** 0.5
a = np.random.rand(1_000_000)
euclidean_norm(a) # compiles on first call, runs native thereafter
Fuse a NumPy expression (no temporaries)
@jit
def score(a, b):
# compiles to ONE loop — no intermediate arrays are ever allocated
return np.sum(np.exp(-a * a) * b + np.where(a > 0, a, 2 * a))
Numpy-class reductions
@jit(reduce_reassoc=True) # vectorizes the accumulator; integers stay exact
def total(x):
acc = 0.0
for i in range(len(x)):
acc += x[i]
return acc
Native float32 (2× on memory-bound work)
total(a.astype(np.float32)) # 32-bit compute, exact f32 precision
Tune a kernel to your machine
f = jit(heavy_kernel)
f(example_args) # compile
report = f.evolve(example_args) # genetic search, keeps the fastest
# equivalence-verified variant
Parallelize
from hanajit import jit, prange
@jit(parallel=True) # outermost range loop auto-parallelizes
def process(x, out):
for i in range(len(x)):
out[i] = expensive(x[i])
return 0
See docs/ for the full API, and examples/ for
runnable programs.
How it works
HanaJit is ~3,000 lines of readable Python. One IR, many machines:
- Frontend —
inspect.getsource+ast.parsegive the exact tree CPython would execute. No custom parser. - Type inference — a fixpoint over a small lattice (
i64,f64,f32,bool, pointers, array kinds). Anything outside it raisesUnsupportedError→ transparent interpreter fallback. - Code generation — the typed AST lowers to LLVM IR (via
llvmlite.ir), including the fusion engine that turns array expressions into element generators fused into one loop. - Backends — the same IR module is optimized (
-O3) and either JITed for your exact host CPU, or re-targeted for GPUs (PTX / GCN / SPIR-V / MSL) or exported for FPGA HLS.
For a deeper tour, see docs/architecture.md.
Scope & honest limitations
HanaJit is deliberately clear about what it does and does not do.
What works and is tested:
- CPU compilation of a useful subset of Python + NumPy, with transparent fallback for the rest.
- The fusion engine, reductions, float32, the genetic optimizer, inlining, auto-parallelization, and multithreading.
- 207 tests passing across Python 3.10–3.14 on Linux, Windows 11, and macOS (Apple Silicon).
GPU: code generation is verified; kernel launch is not implemented.
HanaJit emits GPU assembly and this output is validated by the real vendor
toolchains — NVIDIA ptxas assembles our PTX to a cubin, LLVM's AMDGPU
llvm-mc assembles our GCN to an object, and xcrun metal compiles our Metal
source on Apple Silicon. However, HanaJit does not yet launch kernels on a
GPU (the host-side cuLaunchKernel/HIP/Metal dispatch bridge is on the
roadmap). Today the GPU backends are a verified compiler target, not a
runtime. Claims in this repo say "emits and assembles," never "runs on GPU."
FPGA support is IR + Vitis HLS TCL export only — an FPGA is synthesized, not JITed, so it can never be a runtime target.
Not supported (falls back to CPython): allocating new arrays inside a kernel, most of the object model (classes, dicts, arbitrary Python objects), generators, exceptions as control flow, and the long tail of the NumPy API. HanaJit targets numeric, loop- and array-heavy code, not general Python.
Numerical notes: reduce_reassoc reorders float additions (like NumPy's
pairwise sum) so results are not bit-identical to a sequential sum, but stay
within the same ~1e-10 tolerance; integers are unaffected. float32 gives
exact float32 precision (~7 significant digits), a bounded trade-off you opt
into by passing float32 arrays. Experimental modes
(evolve_hyper, rewrite=True) are opt-in, CPU-only, and documented with
their risks in docs/experimental.md.
Diagnostics
HanaJit ships a self-diagnostic that probes your environment and writes a report:
python -m hanajit.doctor
It checks compilation, dispatch, threading, caching, the GPU code-generation
backends (and runs the real assemblers if ptxas / llvm-mc are on your
PATH), and hardware detection — then writes hanajit_report_<platform>.md.
Committed example reports live in reports/.
Project layout
hanajit/ the package (frontend, typeinfer, codegen, backends, ...)
backends/ cpu, gpu (cuda/amd/intel), metal, fpga
docs/ API, architecture, GPU, performance, limitations, ...
benchmarks/ reproducible benchmark scripts
examples/ runnable example programs
tests/ the test suite (207 tests)
reports/ committed doctor reports (Linux / Windows / macOS)
site/ the project landing page
Contributing
Issues and pull requests are welcome. Please run the suite before submitting:
pip install -e ".[test]"
python -m pytest tests/ -q
New optimizations must include tests that verify correctness against a reference before any performance claim — that is the core rule of this project. Contributions are accepted under the repository's license (below).
License
Licensed under the Apache License 2.0 — see LICENSE.
Apache-2.0 was chosen over MIT for its explicit patent grant and
patent-retaliation clause, which matter for a compiler project in
patent-adjacent fields. If you prefer MIT, the change is a one-file swap plus
a line in pyproject.toml — but note that relicensing after external
contributions requires contributors' agreement.
Acknowledgements
Built on LLVM and
llvmlite. Benchmarked against
NumPy and Numba. Some
ergonomic ideas (helper inlining, auto-parallel for) were inspired by
Taichi — the ideas, implemented
without a DSL.
Developed in the R&D pipeline of EZducate.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file hanajit-0.20.1.tar.gz.
File metadata
- Download URL: hanajit-0.20.1.tar.gz
- Upload date:
- Size: 94.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0411d800a41542c49a97e34ee950861990abce20f35df2b2be0b1cbdfeac6865
|
|
| MD5 |
ab584268172b0c607ac1503934b58426
|
|
| BLAKE2b-256 |
4c5d4a9eded51415fd3c613dacc2b187097ffc468fce8aa54ea8191f5de81ab1
|
Provenance
The following attestation bundles were made for hanajit-0.20.1.tar.gz:
Publisher:
publish.yml on ezducate/HanaJit
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hanajit-0.20.1.tar.gz -
Subject digest:
0411d800a41542c49a97e34ee950861990abce20f35df2b2be0b1cbdfeac6865 - Sigstore transparency entry: 2139219787
- Sigstore integration time:
-
Permalink:
ezducate/HanaJit@a931e5b5b972f6e514589ee51a47e4011affbc17 -
Branch / Tag:
refs/tags/v0.20.1 - Owner: https://github.com/ezducate
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a931e5b5b972f6e514589ee51a47e4011affbc17 -
Trigger Event:
release
-
Statement type:
File details
Details for the file hanajit-0.20.1-py3-none-any.whl.
File metadata
- Download URL: hanajit-0.20.1-py3-none-any.whl
- Upload date:
- Size: 73.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
845a083025e332a9abe355a471f2a3ab635fdd8cb650cb1e7e215126c1e39f70
|
|
| MD5 |
29a702ffb22f138e8d6d30703f7f21e3
|
|
| BLAKE2b-256 |
d193b74587de51cb4e599ace82e8695e33c726ee40e734fac8436abd975b8a71
|
Provenance
The following attestation bundles were made for hanajit-0.20.1-py3-none-any.whl:
Publisher:
publish.yml on ezducate/HanaJit
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hanajit-0.20.1-py3-none-any.whl -
Subject digest:
845a083025e332a9abe355a471f2a3ab635fdd8cb650cb1e7e215126c1e39f70 - Sigstore transparency entry: 2139219826
- Sigstore integration time:
-
Permalink:
ezducate/HanaJit@a931e5b5b972f6e514589ee51a47e4011affbc17 -
Branch / Tag:
refs/tags/v0.20.1 - Owner: https://github.com/ezducate
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a931e5b5b972f6e514589ee51a47e4011affbc17 -
Trigger Event:
release
-
Statement type: