How do I know if Numba compiled anything?

I am kind of just 10 hours into Numba and still got a lot of questions. I’ve got the function from the ~5 minute intro.

from numba import jit
import pandas as pd

x = {'a': [1, 2, 3], 'b': [20, 30, 40]}

@jit
def use_pandas(a):
    df = pd.DataFrame.from_dict(a)
    df += 1
    return df.cov()
  1. How do I know that Numba compiled that function?
  2. How to validate that the compilation was successful?
  3. How do I know in which mode the function was compiled if the compilation was successful?
    3.1. How do I know if the function was compiled as nopython mode?
    3.2. How do I know if the function was compiled as object mode?
    3.3. How do I know if the function was compiled as python mode? Or does @jit fail if fallback to object fails?
  4. How do I know in which mode the function was compiled if the compilation was not successful?

If there is a notebook that allows to see it and play with this stuff, that would be great.

  1. Numba targets jit compilation. That means a function is compiled exactly when it is needed (unless it has been compiled before). I.e. the function will (usually) not compile when you decorate it, but when you actually call it for the first time (or when you call it with a different type signature than before). Numba can also cache compilation results to disk for use in later sessions.
    When you use @jit to decorate your function, it gets wrapped in a fairly feature rich Dispatcher object. This reveals many properties and methods to learn more about the compilation results (keep in mind compilation only happens when you CALL the function for the first time). The fields that may help you a lot are jitted_func.signatures and jitted_func.nopython_signatures. Once a new overload of your function is compiled, its signature will show up here.
    I recommend making a decorated function in a REPL, and then use dir(jitted_func) or jitted_func.__dict__ to learn more about those class members.

  2. In 99% of all cases it will visibly smoke if it didn’t work. That said, it is often a good idea to have some sanity checks / tests for your functions to make sure that they do no just run, but also return the correct result. If you run into strange errors, it may occasionally be useful to compare the numba result to the Python result (small numerical differences are to be expected in some circumstances)

  3. nopython mode is the go to these days. Numba will complain visibly if it cannot get it to work (unless you explicitly told it to). So if your code compiles and returns the expected result, you can relatively safely assume that it compiled in nopython mode. Personally I like to use numba.njit instead of numba.jit because it signals right away that nopython mode compilation is expected and enforced.
    Usually you want to avoid object mode wherever possible, it can come with hefty performance penalties. For cases where you really need to object mode functionality to interact with a generic PyObject, a special context manager exists for that purpose.

  4. The information above should hopefully help to narrow it down.

I understood that I need to execute the function with the specified parameters to make Numba compile it. Then if parameters are different, it may return compiled function, or non-compiled. Does it only check parameter types? Do parameter contents also matter?

I get it that @jit decorator substitutes original function with Dispatcher object. And when function is about to be executed, the Dispatcher jits it and returns jitted_func which is then called. jitted_func then can be of three variants.

  • nopython jitted if jitted_func.nopython_signatures is … ?
  • object jitted if jitted_func.signatures is … ?
  • not jitted if Dispatcher fails and throws with exception?

My FreeMoCap setup is not ready for experiments, and unfortunately I am not sure when I can back to it to see what dir(jitted_func) returns.