fastmath, boundscheck and other options

Numba · Advanced · 11 min

These flags unlock SIMD vectorization and remove checks, but each changes semantics in specific ways.

The JIT decorators accept options that change what the compiler may assume.

  • fastmath=True lets LLVM ignore strict IEEE 754 rules: it may reorder additions (enabling SIMD vectorization of reductions), assume no NaNs or infinities, and use approximate reciprocals. Loops summing floats often get several times faster. The price: results can differ in the last bits, and code that relies on NaN checks (x != x, np.isnan) may be optimised away. You can pass a set of flags such as {"nsz", "arcp", "contract", "afn", "reassoc"} to enable only some relaxations, leaving NaN and infinity handling intact.
  • boundscheck=True turns on index bounds checking. By default Numba does not check bounds, so a[10] on a 5-element array reads or writes arbitrary memory. Enable checks while debugging, or globally with NUMBA_BOUNDSCHECK=1.
  • error_model="numpy" makes division by zero return inf or nan like NumPy instead of raising ZeroDivisionError as Python does. Removing the check makes division-heavy loops faster.
  • inline="always" inlines a small @njit function into callers at the Numba IR level; LLVM usually inlines small functions anyway.
  • nogil=True and cache=True are covered elsewhere.

Treat these as measured optimisations: benchmark with and without, and test numerical results against the strict version.

Example

import numpy as np
from numba import njit

@njit(fastmath=True)
def fast_sum(a):
    s = 0.0
    for x in a:
        s += x            # may be reordered and vectorized
    return s

@njit(error_model="numpy")
def safe_ratio(a, b):
    return a / b

@njit(boundscheck=True)
def get(a, i):
    return a[i]

a = np.random.default_rng(0).random(10_000_000)
print(np.isclose(fast_sum(a), a.sum()))   # True (last bits may differ)
print(safe_ratio(1.0, 0.0))               # inf  (Python error model would raise)
try:
    get(np.zeros(5), 10)
except IndexError as e:
    print("IndexError:", e)               # IndexError: index is out of bounds

Remember

  • fastmath allows reordering and SIMD; results may change slightly and NaN checks may vanish
  • Bounds are not checked by default; enable boundscheck while debugging
  • error_model='numpy' returns inf/nan for division by zero

Watch out for

  • Using fastmath=True in code that tests for NaN, which may then never detect it.

Check yourself

  1. By default, what happens in @njit code when you read a[10] from a 5-element array?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: numba.readthedocs.io/en/stable/reference/jit-compilation.html