just-in-time compiler for numerical functions in the Python programming language
Numba is an open-source JIT compiler that translates a subset of Python and NumPy into fast machine code using LLVM, via the llvmlite Python package. It offers a range of options for … Wikipedia
Factsheet
Original author Continuum Analytics
Developer Community project
Release 15 August 2012; 13 years ago (2012-08-15)
Factsheet
Original author Continuum Analytics
Developer Community project
Release 15 August 2012; 13 years ago (2012-08-15)
🌐
PyData
numba.pydata.org
Numba: A High Performance Python Compiler
Numba translates Python functions to optimized machine code at runtime using the industry-standard LLVM compiler library.
🌐
GitHub
github.com › numba › numba
GitHub - numba/numba: NumPy aware dynamic Python compiler using LLVM · GitHub
Numba is an open source, NumPy-aware optimizing compiler for Python sponsored by Anaconda, Inc.
Starred by 11.1K users
Forked by 1.3K users
Languages   Python 90.6% | C 6.4% | C++ 2.8% | Shell 0.2% | HTML 0.0% | Batchfile 0.0%
Discussions

Numba
Numba is amazing, especially the guvectorize decorator. But I cannot fathom why you thought avoiding numpy would help. More on reddit.com
🌐 r/Python
35
85
February 13, 2022
python - How do I use numba on a member function of a class? - Stack Overflow
Late to the party but sometimes you have to specify the variable types and the type definitions that numba needs for that are here. Not sure why numba dynamically infers the types when applied to a plain function but needs those type definitions in the case of a class. More on stackoverflow.com
🌐 stackoverflow.com
Is Numba useful for Python game dev?
Yes, I use this code for fast render of large surfaces with numba decorators: def blit(sou_arr, dest_arr, pos) -> np.ndarray: X: int = pos[0]+dest_arr.shape[0] Y: int = pos[1]+dest_arr.shape[1] sou_arr[pos[0]:X, pos[1]:Y] = dest_arr return sou_arr More on reddit.com
🌐 r/pygame
6
5
October 7, 2025
Numba: A High Performance Python Compiler
It’s a phenomenal library for developing novel computationally intensive algorithms on numpy arrays. It’s also more versatile than Jax · In presentations, I’ve heard Leland McInnes credits numba often when he speaks of his development of UMAP. We built a very computationally intensive ... More on news.ycombinator.com
🌐 news.ycombinator.com
59
209
January 5, 2023
🌐
PyPI
pypi.org › project › numba
numba · PyPI
Numba is an open source, NumPy-aware optimizing compiler for Python sponsored by Anaconda, Inc.
      » pip install numba
    
Published   Apr 24, 2026
Version   0.65.1
🌐
Reddit
reddit.com › r/python › numba
r/Python on Reddit: Numba
February 13, 2022 -

Wow. Just wow. Was doing something math-heavy, even very recursion-heavy which is why I wasn't using numpy, and it took a few minutes to get the results. I'd tried numba once before with something and saw some very minute improvements, but figured I'd try it again. I've been doing number crunching in Python for years, and never really thought it'd make a difference.

boy was I wrong. tqdm says it took 2:48 to do the pure python. 0:05 to do with numba (I suppose including compile). I am shooketh. Entirely and completely, to my core. Even if I parallelized it completely which was my first thought, at best I'd get to 21 seconds. I can't believe it. I am sure if there's any response to this it'll be an "I told you so" but really, wow.

Numba.

Edit: because the code was pretty basic, I re-wrote the math part in C. tqdm says I'm at 1 second now. It was 3 seconds ish without the compile time (recall 5 seconds with). ~247k it/s vs ~151k it/s. Still a lot easier with the python and the jit, but the C module wasn't as painless as I thought it'd be.

🌐
Numba
numba.readthedocs.io › en › stable › user › 5minguide.html
A ~5 minute guide to Numba — Numba 0+untagged.855.g9e3087a.dirty documentation
Numba reads the Python bytecode for a decorated function and combines this with information about the types of the input arguments to the function. It analyzes and optimizes your code, and finally uses the LLVM compiler library to generate a machine code version of your function, tailored to ...
🌐
NVIDIA
nvidia.com › glossary › numba
What Is Numba and Why Does It Matter? | NVIDIA Glossary
Numba is an open-source, just-in-time compiler for Python code that developers can use to accelerate numerical functions on both CPUs and GPUs using standard Python functions.
Find elsewhere
🌐
Wikipedia
en.wikipedia.org › wiki › Numba
Numba - Wikipedia
February 1, 2026 - It offers a range of options for parallelising Python code for CPUs and GPUs, often with only minor code changes. Numba was started by Travis Oliphant in 2012 and has since been under active development with frequent releases.
🌐
Medium
medium.com › @weidagang › accelerated-python-numba-3e6a88335f83
Accelerated Python: Numba. The JIT Compiler for Python | by Dagang Wei | Medium
June 9, 2024 - Object-Oriented Overhead: Python’s object-oriented nature, while beneficial for code organization, can introduce some overhead in terms of memory usage and object creation/management. Numba is an open-source just-in-time (JIT) compiler for Python. ...
🌐
Numba
numba.pydata.org › numba-doc › dev › user › 5minguide.html
A ~5 minute guide to Numba — Numba 0.52.0.dev0+274.g626b40e-py3.7-linux-x86_64.egg documentation
Numba reads the Python bytecode for a decorated function and combines this with information about the types of the input arguments to the function. It analyzes and optimizes your code, and finally uses the LLVM compiler library to generate a machine code version of your function, tailored to ...
🌐
Hacker News
news.ycombinator.com › item
Numba: A High Performance Python Compiler | Hacker News
January 5, 2023 - It’s a phenomenal library for developing novel computationally intensive algorithms on numpy arrays. It’s also more versatile than Jax · In presentations, I’ve heard Leland McInnes credits numba often when he speaks of his development of UMAP. We built a very computationally intensive ...
🌐
GeeksforGeeks
geeksforgeeks.org › data analysis › installing-and-using-numba-for-python-a-complete-guide
Installing and Using Numba for Python: A Complete Guide - GeeksforGeeks
July 3, 2024 - Numba is a powerful just-in-time (JIT) compiler that translates Python functions into optimized machine code at runtime using the LLVM compiler library. This allows Python code to execute at speeds comparable to C or Fortran, making it an excellent ...
🌐
Numba
numba.readthedocs.io › en › stable › user › index.html
User Manual — Numba 0+untagged.855.g9e3087a.dirty documentation
Compiling Python classes with @jitclass · Basic usage · Inferred class member types from type annotations with as_numba_type · Specifying numba.typed containers as class members explicitly · Support operations · Supported dunder methods · Limitations ·
Top answer
1 of 2
1

I modified your code a bit and was able to get the function to run. I removed the Particle class and changed all the list instances to numpy arrays.

This is the output for the Zavg, Zrelm and counter:

Zavg: [0.07047501 0.06735052 0.06728123 ... 0.39516435 0.3947497  0.39433495] 
Zrelm: [-0.04179043 -0.04461464 -0.0394889  ... -0.11080628 -0.11087257
     -0.11093883] 
Counter: 538

Here is the modified code:

import numpy as np
import matplotlib.pyplot as plt
import random
from math import *
from random import *
from numba import jit


"Dynamics"

@jit(nopython=True)
def f(SP, Zavgi, Zreli, alf, dt, n):
    "Time"
    counter = 0;
    sum1, sum2 = 0, 0;
    Zavg = np.array([Zavgi]);
    Zrelm = np.array([Zreli]);
    T_plot = [0];
    for i in range(1, iter + 1):

        t = i * dt;
        T_plot.append(t)
        Z = [];
        Up = [];
        Down = [];
        c, s = cos(t), sin(t);
        c1, s1 = cos(t - dt), sin(t - dt);
        for j in range(n - 1):
            collchk = ((c * (SP[j][0]) + s * (SP[j][1])) - (c * (SP[j + 1][0]) + s * (SP[j + 1][1]))) * (
                    c1 * (SP[j][0]) + s1 * (SP[j][1]) - (c1 * (SP[j + 1][0]) + s1 * (SP[j + 1][1])));

            prel = ((c * (SP[j][1]) - s * (SP[j][0])) - (c * (SP[j + 1][1]) - s * (SP[j + 1][0]))) / 2;

            rcoeff = 1 / (1 + (prel * alf) ** 2);
            rand_value = random();

            if collchk < 0:

                SP[j], SP[j + 1] = SP[j + 1], SP[j];

                if rcoeff > rand_value:
                    counter = counter + 1
                    SP[j][2], SP[j + 1][2] = SP[j + 1][2], SP[j][2];
            if SP[j][2] == 1:
                Up.append(c * (SP[j][0]) + s * (SP[j][1]))
            else:
                Down.append(c * (SP[j][0]) + s * (SP[j][1]))
            Z.append(c * (SP[j][0]) + s * (SP[j][1]))

        Zrel = np.sum(np.array(Up)) / len(Up) - np.sum(np.array(Down)) / len(Down);
        Zrelm = np.append(Zrelm, Zrel)

        Zm = np.sum(np.array(Z)) / len(Z)
        Zavg = np.append(Zavg, Zm)


    return Zavg, Zrelm, counter, T_plot



if __name__ == '__main__':

    n = 1000
    mu = np.random.uniform(0, 1, n)
    r = [sqrt(-2 * log(1 - i)) for i in mu]
    eta = np.random.uniform(0, 1, n)
    theta = 2 * pi * eta;
    cuz = [cos(i) for i in theta]
    suz = [sin(i) for i in theta]
    Zinitial = [a * b for a, b in zip(r, cuz)];
    Pinitial = [a * b for a, b in zip(r, suz)];

    iter = 10 ** (5);
    dt = 1 / (2 * n);
    alf = sqrt(n);


    SP = np.array(sorted(np.array([  np.array([i,j,choice([0,1])]) for i, j in zip(Zinitial, Pinitial)]),
                key=lambda x: x[0]))
    Upi = [];
    Downi = [];
    count_plot = [];
    for j in range(len(SP)):
        if SP[j][2] == 1:
            Upi.append(SP[j][0])
        else:
            Downi.append(SP[j][0])
    Zavgi = sum(Zinitial) / len(Zinitial)
    Zreli = sum(Upi) / len(Upi) - sum(Downi) / len(Downi)


    Zavg, Zrelm, counter, T_plot = f(SP, Zavgi, Zreli, alf, dt, n)

    print(Zavg, Zrelm, counter)
    plt.plot(T_plot, Zrelm)
    plt.show()

This is how the plot looks like:

2 of 2
0

The error appears because Numba try to access to global variable which the type is not known at compile time. Indeed, SP is a pure-Python list called reflected list which can contain items of different types. Numba does not support such kind of list anymore. Instead, Numba supports typed lists which are compatible with reflected lists. This means you need to build a new typed list (with a well-defined type) and copy the reflected list items to the typed list. This process can be quite expensive compared to the overall computation. Thus, this is often better not to use lists when arrays can be used instead (typically when you cannot know the size of the final list): Numpy array are significantly faster, more compact and functions using them can be compiled faster.

Additionally, Numba has no idea of was is the type Particle. Numba only supports Numpy built-in types by default. There is an experimental support for jitted classes but I advise you to work with basic array since it is generally faster and also more flexible as you can use Numpy vectorized functions on the target arrays as opposed to an object-based array (which are AFAIK inefficiently stored in Numpy arrays and slow).

Moreover, you should really avoid using global variables, especially the ones that are mutated. Global variables are slower to access in CPython and are generally seen as a bad software engineering practice. For Numba, they are considered as compile time constant so it can cause some surprising results if the variables are mutated at runtime.

Top answer
1 of 1
1

There are a couple factors here affecting your results. The main two are

  • Your calculation of run time includes the numba compilation, which is a one-time overhead cost
  • Your functions are not optimized to work well with numba or numpy resulting in functions that don't benefit from numpy efficiency and compile poorly

I was able to partially reproduce your results, and I offer some suggestions for changes to make the code operate better after numba compilation as well as restructuring the testing code so that the differences between the compilation step and the regular execution can be seen a little easier.

To test the impact of numba compilation, I time and call the function once and then again in a loop to get an average run time after compilation. The run time after compilation is many times faster in some cases because that compilation represents a large but one-time cost.

To show the impact of better function design, I rewrote a couple of the functions to use numpy.ndarrays instead of lists both internally and as inputs, and it shows significant improvements in performance, in some cases >100x faster.

The last note, numpy is already highly optimized, where you can use numpy functions it may be difficult to attain better performance otherwise, even with numba compilation. When we look at the last function, which uses numpy.square, I would not expect to see much improvement (once the input is adjusted to be an expected numpy type), and the testing confirms that expectation.

Here is the testing code:

Copyimport time

import numpy as np
import numba

import matplotlib.pyplot as plt

def makeSquareLoopFor(a):
    result = []
    for i, v in enumerate(a):
        result.append(a[i] ** 2)
    return result

@numba.njit
def makeSquareLoopForJIT(a):
    result = []
    for i, v in enumerate(a):
        result.append(a[i] ** 2)
    return result

def makeSquareLoopForOpt(a):
    result = np.empty(shape=(len(a),), dtype=np.int64)
    for i, v in enumerate(a):
        result[i] = a[i] ** 2
    return result

@numba.njit
def makeSquareLoopForOptJIT(a):
    result = np.empty(shape=(len(a),), dtype=np.int64)
    for i, v in enumerate(a):
        result[i] = a[i] ** 2
    return result

def makeSquareLoopWhile(a):
    result = []
    i = 0
    while i < len(a):
        result.append(a[i] ** 2)
        i += 1
    return result

@numba.njit
def makeSquareLoopWhileJIT(a):
    result = []
    i = 0
    while i < len(a):
        result.append(a[i] ** 2)
        i += 1
    return result

def makeSquareLoopWhileOpt(a):
    result = np.empty(shape=(len(a),), dtype=np.int64)
    i = 0
    while i < len(a):
        result[i] = a[i] ** 2
        i += 1
    return result

@numba.njit
def makeSquareLoopWhileOptJIT(a):
    result = np.empty(shape=(len(a),), dtype=np.int64)
    i = 0
    while i < len(a):
        result[i] = a[i] ** 2
        i += 1
    return result
def makeSquareListComprehension(a):
    return [v ** 2 for v in a]

@numba.njit
def makeSquareListComprehensionJIT(a):
    return [v ** 2 for v in a]

def makeSquareListMap(a):
    return list(map(lambda v: v ** 2, a))

@numba.njit
def makeSquareListMapJIT(a):
    return list(map(lambda v: v ** 2, a))

def makeSquareListNumpy(a):
    return np.square(a)

@numba.njit
def makeSquareListNumpyJIT(a):
    return np.square(a)

def _main():

    trials = 10

    array_len = 5_000_000
    a_list = list(range(1, array_len, 1))
    a_array = np.array(a_list)

    print(f'{"function":<35}{"input type":<12}{"first run":>12}{"trial runs":>12}')
    function_names = []
    results = {}
    for pythonfun, numbafun, humanname in (
            (makeSquareLoopFor, makeSquareLoopForJIT, 'for'),
            (makeSquareLoopForOpt, makeSquareLoopForOptJIT, 'for opt'),
            (makeSquareLoopWhile, makeSquareLoopWhileJIT, 'while'),
            (makeSquareLoopWhileOpt, makeSquareLoopWhileOptJIT, 'while opt'),
            (makeSquareListComprehension, makeSquareListComprehensionJIT, 'comp'),
            (makeSquareListMap, makeSquareListMapJIT, 'map'),
            (makeSquareListNumpy, makeSquareListNumpyJIT, 'numpy fun')
    ):
        function_names.append(humanname)
        for fun, fun_type in (
                (pythonfun, 'python'),
                (numbafun, 'numba')
        ):


            for a_data, a_type in (
                (a_list, 'list'),
                (a_array, 'array')
            ):
                try:
                    start_first = time.perf_counter_ns()
                    fun(a_data)
                    end_first = time.perf_counter_ns()
                    start_trials = time.perf_counter_ns()
                    for i in range(trials):
                        fun(a_data)
                    end_trials = time.perf_counter_ns()

                    run_time_first = (end_first - start_first) / 10 ** 6
                    run_time_trials = ((end_trials - start_trials) / trials) / 10 ** 6

                    if fun_type not in results:
                        results[fun_type] = {}
                    if a_type not in results[fun_type]:
                        results[fun_type][a_type] = {
                            'first': [],
                            'trials': []
                        }
                    results[fun_type][a_type]['first'].append(run_time_first)
                    results[fun_type][a_type]['trials'].append(run_time_trials)

                    print(f'{fun.__name__:<35}{a_type:<12}{run_time_first:12.3f}{run_time_trials:12.3f}')
                except numba.core.errors.TypingError:
                    print(f'{fun.__name__:<35}{a_type:<12}{"failed":>12}')
        print()

    fig, axs = plt.subplots(2)
    for funtype, fundata in results.items():
        for inputtype, inputdata in fundata.items():
            for i, (runtype, rundata) in enumerate(inputdata.items()):
                axs[i].plot(rundata, '-x', label=f'{funtype} - {inputtype}')

    axs[0].legend()
    axs[1].legend()
    axs[0].set_title('With compilation step')
    axs[1].set_title('Without compilation step')
    axs[0].set_yscale('log')
    axs[1].set_yscale('log')
    axs[0].get_xaxis().set_visible(False)
    axs[1].set_xticks(range(len(rundata)), labels=function_names)
    plt.show()


if __name__ == '__main__':
    _main()

Running that code produces the following console print:

function                           input type     first run  trial runs
makeSquareLoopFor                  list             485.755     481.474
makeSquareLoopFor                  array            893.265     857.773
makeSquareLoopForJIT               list            4436.258    3915.449
makeSquareLoopForJIT               array            245.782     164.247

makeSquareLoopForOpt               list             738.646     672.020
makeSquareLoopForOpt               array           1123.080    1127.280
makeSquareLoopForOptJIT            list            4013.135    3812.577
makeSquareLoopForOptJIT            array             85.737       8.752

makeSquareLoopWhile                list             588.821     586.823
makeSquareLoopWhile                array            931.613     899.923
makeSquareLoopWhileJIT             list            4055.000    3963.027
makeSquareLoopWhileJIT             array            253.426     167.106

makeSquareLoopWhileOpt             list             791.360     790.028
makeSquareLoopWhileOpt             array           1185.349    1133.906
makeSquareLoopWhileOptJIT          list            3889.629    3778.378
makeSquareLoopWhileOptJIT          array             92.073       8.482

makeSquareListComprehension        list             314.250     312.969
makeSquareListComprehension        array            447.048     443.911
makeSquareListComprehensionJIT     list            3981.916    3908.984
makeSquareListComprehensionJIT     array            238.009     164.230

makeSquareListMap                  list             471.389     455.676
makeSquareListMap                  array            600.735     615.717
makeSquareListMapJIT               list            4212.756    3950.546
makeSquareListMapJIT               array            457.831     242.098

makeSquareListNumpy                list             182.675     182.450
makeSquareListNumpy                array              9.080       9.116
makeSquareListNumpyJIT             list              failed
makeSquareListNumpyJIT             array            127.094       9.239

And the following graph:

They key takeaways here are:

  • When written in a way that does not work well with numba, numba is consistently the slowest
  • When written to target numba compilation, numba calculation is consistently the fastest, excepting only already highly optimized numpy functions (see functions with opt in name which when numba compiled hugely outperform their unoptimized counterpart at about 20x faster)
  • When discounting the compilation step, numba compilation of appropriate functions offers extremely large performance improvements over pure python implementations.
  • Even without numba compilation, simply performing all operations on numpy objects can offer major performance improvements (see makeSquareListNumpy operating on a list vs a numpy.ndarray)

Whenever working with numpy and numba I highly recommend profiling, experimenting and comparing to see what solution works the best. Often, just using numpy functions will offer the best performance as was the case here, but sometimes numba compilation will really make a difference, and some problems are just better solved with pure python. Comparing is the surest way to get the best performance.

🌐
Intel
intel.com › developers › topics & technologies › gamedev › xᵉss
Intel® Xe Super Sampling 3 for Developers
Access technologies that dramatically boost your framerate at the highest visual quality while keeping your game responsive.
🌐
Pnavaro
pnavaro.github.io › python-notebooks › 18-Numba.html
Numba — Python notebooks
Numba generates optimized machine code from pure Python code with a few simple annotations
🌐
4chan
boards.4chan.org › g › thread › 108390798
/g/ - Will chatbots be what finally exposes Silly Con Va - Technology - 4chan
March 17, 2026 - Will chatbots be what finally exposes Silly Con Valley as nothing but frauds and scam artists? - "/g/ - Technology" is 4chan's imageboard for discussing computer hardware and software, programming, and general technology.
🌐
GitHub
gist.github.com › 3914904
numba, a jit for Python · GitHub
numba, a jit for Python . GitHub Gist: instantly share code, notes, and snippets.