Quick Start
This guide covers the basics of using JustJIT to accelerate your Python code.
Basic Usage
The simplest way to use JustJIT is with the @jit decorator:
import justjit
@justjit.jit
def add(a, b):
return a + b
result = add(1, 2) # Compiled to native code on first call
When you call add(1, 2), JustJIT:
Extracts the Python bytecode from the function
Translates it to LLVM IR
Compiles to native machine code
Executes the native code
Subsequent calls to add skip the compilation step and run the cached native code directly.
Choosing a Mode
JustJIT supports 12 native modes. Each mode compiles your function to work with a specific data type, eliminating Python object overhead.
Integer Mode
For pure integer arithmetic:
@justjit.jit(mode='int')
def factorial(n):
result = 1
for i in range(2, n + 1):
result = result * i
return result
factorial(20) # Returns 2432902008176640000
Float Mode
For floating-point arithmetic:
@justjit.jit(mode='float')
def average(a, b):
return (a + b) / 2.0
average(3.0, 5.0) # Returns 4.0
Auto Mode (Default)
When using mode='auto' (the default), JustJIT uses the full Python object mode. This is the most compatible but has more overhead:
@justjit.jit # mode='auto' is the default
def flexible_add(a, b):
return a + b
flexible_add(1, 2) # Works with integers
flexible_add(1.5, 2.5) # Works with floats
flexible_add("hello", " ") # Works with strings
See Compilation Modes for a complete list of available modes.
Inspecting Generated IR
Use dump_ir() to see the LLVM IR generated for a function:
import justjit
@justjit.jit(mode='float')
def add(a, b):
return a + b
# Trigger compilation
add(1.0, 2.0)
# Print the LLVM IR
print(justjit.dump_ir(add))
Output:
define double @add(double %0, double %1) {
entry:
%fadd = fadd double %0, %1
ret double %fadd
}
This shows that the Python function compiles to a single fadd instruction.
Optimization Levels
Control the LLVM optimization level with opt_level:
@justjit.jit(opt_level=0) # No optimization (fastest compile)
def debug_function(a, b):
return a + b
@justjit.jit(opt_level=3) # Maximum optimization (default)
def fast_function(a, b):
return a + b
opt_level=0: No optimization, fastest compilationopt_level=1: Basic optimizationopt_level=2: More optimizationopt_level=3: Maximum optimization (default)
Loop Optimization
JustJIT excels at optimizing loops. Native for i in range(n) loops compile to tight machine code:
@justjit.jit(mode='int')
def sum_range(n):
total = 0
for i in range(n):
total = total + i
return total
# This runs 38,000x faster than the Python interpreter
sum_range(10_000_000)
The generated code avoids:
Python object allocation
Type checking on each iteration
Interpreter dispatch overhead
Generator Support
JustJIT can compile generator functions to native state machines:
@justjit.jit
def countdown(n):
while n > 0:
yield n
n = n - 1
for value in countdown(5):
print(value) # Prints 5, 4, 3, 2, 1
Generators are compiled as step functions with state persistence across yields. The send() and throw() methods are fully supported.
Async Function Support
Async functions (coroutines) are also supported:
import asyncio
@justjit.jit
async def async_add(a, b):
await asyncio.sleep(0.1)
return a + b
asyncio.run(async_add(1, 2)) # Returns 3
The JIT compiler handles await, delegating to awaited objects and properly extracting return values from StopIteration.
Next Steps
Compilation Modes - Learn about all 11 native modes
API Reference - Full API reference
Performance - Benchmarks and optimization tips
Async and Generators - Deep dive into generator/coroutine compilation