import numpy as np, cimo x = np.random.rand(1_000_000) y = np.empty_like(x)
dependencies outside the Python standard library
import cimo
to compile a kernel file, then cached
faster than pure Python on stencils, flood fills, DP
C kernels from Python
Compile a string or a .c file. Every non-static function becomes a Python callable, typed from its C signature. Arrays are passed by pointer and never copied.
- Scripts and notebooks. The same call in a script, Jupyter, JupyterLite or marimo.
- Cached. The second run of the same source starts instantly.
- Buffers in place. numpy arrays,
bytearray,memoryviewand ctypes arrays.
import numpy as np
import cimo
k = cimo.compile(r"""
void blur(int h, int w, const float src[h][w], float dst[h][w]) {
for (int i = 1; i < h - 1; i++)
for (int j = 1; j < w - 1; j++)
dst[i][j] = 0.2f * (src[i][j] + src[i-1][j] + src[i+1][j]
+ src[i][j-1] + src[i][j+1]);
}
""")
src = np.random.rand(4096, 4096).astype(np.float32)
dst = np.zeros_like(src)
k.blur(*src.shape, src, dst) # native code, on your arrays in place
# numpy: the same result, bit for bit, 1.6x slower (Linux arm64)
ref = np.zeros_like(src)
ref[1:-1, 1:-1] = 0.2 * (src[1:-1, 1:-1] + src[:-2, 1:-1] + src[2:, 1:-1]
+ src[1:-1, :-2] + src[1:-1, 2:])
assert np.array_equal(dst, ref)
import numpy as np, cimo
k = cimo.compile(r"""
void blur(int h, int w,
const float src[h][w],
float dst[h][w]) {
for (int i = 1; i < h - 1; i++)
for (int j = 1; j < w - 1; j++)
dst[i][j] = 0.2f * (src[i][j]
+ src[i-1][j] + src[i+1][j]
+ src[i][j-1] + src[i][j+1]);
}
""")
src = np.random.rand(4096, 4096)
src = src.astype(np.float32)
dst = np.zeros_like(src)
k.blur(*src.shape, src, dst) # in place
# numpy: same bits, 1.6x slower
Native code at runtime, without a toolchain
Compute-graph engines, PDE solvers and symbolic code generators emit C at runtime and call the system compiler on the user's machine. Where there is none, they fail or fall back to slow paths. Numba avoids the compiler by bundling LLVM. cimo is a compiler written in Python: any machine that runs Python can compile and run C.
Pure-Python packages
Ship C kernels as source. No wheel matrix across OSes, architectures and Python versions, and no compiler needed for a source install.
Desktop applications
Native-speed kernels in a frozen app, without bundling LLVM or installing a C compiler on every build machine.
Browsers and restricted machines
Pyodide, JupyterLite and marimo; plugin repositories that accept no binaries; machines where nothing can be installed.
Close to gcc and clang at -O3
cimo has a loop optimizer and a full instruction-selecting backend rather than a one-pass code generator.
Lower is better; dashed line is parity. Scalar code, FMA contraction off on both sides. Results are bit-identical to the reference compiler.
View benchmarks- Loop optimizer. Unrolling, unroll-and-jam, scalar replacement, strength reduction, runtime alias checks.
- Backend. Instruction selection and register allocation, encoded to machine code in Python. No assembler, no linker.
- Caching. Compiled modules are cached by content hash.
- Optimization reports.
cimo reportlists which loops were optimized and why the others were not.
$ cimo report kernels.ckernels.c:12 loop j: optimized (unrolled x4, alias check src/dst)kernels.c:19 loop i,j: optimized (unroll-and-jam 2x4, scalar replacement)kernels.c:27 loop k: optimized (strength reduction, 3 loads hoisted)kernels.c:31 loop i: not optimized: base of `p` depends on a branchkernels.c:38 loop t: not optimized: y[t] depends on y[t-1]kernels.c:44 loop n: not optimized: calls `next_sample` (not inlined)3 of 6 loops optimized$
C11 and a bundled C library
cimo ships its own libc, so kernels behave the same on every platform, including the browser.
- Language. Structs, unions, bitfields, pointers of any depth, function pointers, recursion, varargs, VLAs,
goto,switch, globals,consttables. - Headers.
stdio.h,stdlib.h,string.h,math.h,ctype.h,inttypes.hand the freestanding headers.printfwrites tosys.stdout, so output appears in notebooks. - SIMD. GNU vector types for NEON, AVX2 and AVX-512.
#pragma omp simdandrestricthonored as written. - System libraries. Link the host libc for files, clocks, threads and sockets, or any shared library such as zlib, BLAS or the Python C API.
Not supported: inline assembly, setjmp/longjmp, 128-bit long double. Each is a compile error that says so.
k = cimo.compile(src, link=["libc"], host_headers=True)
k = cimo.compile(src, link=["libz.so.1"])
k = cimo.load("kernels.c") # or keep kernels in a file
| C parameter | Pass from Python |
|---|---|
| float *, const uint8_t *, VLAs | numpy arrays, bytearray, memoryview |
| int, double, size_t | Python numbers |
| struct point | a ctypes structure or a dict |
Threads and error handling
Kernels release the GIL and are safe to call concurrently. There is no launch API; use ordinary Python threads. malloc is lock-free, with a heap per thread.
from concurrent.futures import ThreadPoolExecutor
n = 16
with ThreadPoolExecutor() as pool:
list(pool.map(lambda b: k.blur(b, n, h, w, src, dst), range(n)))
assert, abort(), exit() and traps unwind back to Python. Debug builds bounds-check every access and catch use-after-free, double free and leaks.
k.blur(*src.shape, src, dst)
cimo.BoundsError: blur: read of src[4096][1] is out of bounds (src is 4096 x 4096)
at line 5
Train a network you draw
In Pyodide, JupyterLite and marimo, cimo compiles C to WebAssembly in the page and links it into Pyodide's memory. Here that runs a small deep-learning stack: draw a network, Python turns it into C for its forward and backward passes, cimo compiles the C, and it trains in your browser.
drag nodes to move · drag from a dot to a dot to connect · click a wire to cut it
Loads Pyodide and cimo (shared with the demo at the top).
or click the plot
In a notebook: await micropip.install("cimo"), then cimo.compile(src) as anywhere else. The code generator is one Python file.
Supported platforms
| Platform | Backend | Loader |
|---|---|---|
| Linux arm64 / x86-64 | arm64, amd64 | mmap + mprotect (W^X) |
| macOS, Apple silicon | arm64 | MAP_JIT, W^X fallback |
| Windows x64 | amd64 | VirtualAlloc + VirtualProtect |
| Pyodide / browser | WebAssembly | shared-memory side module |
Kernels run with the full privileges of your Python process, like ctypes. There is no sandbox.
Architecture
One kernel through every stage of the compiler. This is cimo's real output for it, regenerated from the compiler.
cproc and QBE are ported to Python and checked against the originals by diffing their IR pass by pass. The loop optimizer and machine-code encoder are new. Every release is differential-tested against gcc and clang on generated random programs.