I’ve noticed that how things get passed between numba and python can significantly effect the runtime. For example consider the following three ways of passing parameters into an njit function:
from numba import njit, f8
from numba.types import unicode_type, Tuple
from numba.typed import List
import timeit
N=1000
def time_ms(f):
f() #warm start
return " %0.6f ms" % (1000.0*(timeit.timeit(f, number=N)/float(N)))
@njit
def separate(f1,f2,f3,f4, s1,s2,s3,s4):
return f1 + f2 + f4 + f4 + len(s1) + len(s2) + len(s3) + len(s4)
@njit
def together(t):
f1,f2,f3,f4,s1,s2,s3,s4 = t
return f1 + f2 + f4 + f4 + len(s1) + len(s2) + len(s3) + len(s4)
@njit
def from_list(l):
f1,f2,f3,f4,s1,s2,s3,s4 = l[0]
return f1 + f2 + f4 + f4 + len(s1) + len(s2) + len(s3) + len(s4)
tup = (1,2,3,4,"A","BB", "CC", "DDD")
def s():
separate(1,2,3,4,"A","BB","CC","DDD")
def t():
together(tup)
lst = List.empty_list(Tuple((f8,f8,f8,f8,unicode_type,unicode_type,unicode_type,unicode_type)))
lst.append(tup)
def l():
from_list(lst)
print(time_ms(s)) # 0.006556 ms
print(time_ms(t)) # 0.040448 ms
print(time_ms(l)) # 0.001097 ms
Somewhat counter-intuitively wrapping the arguments in a tuple in a typed list works best. I assume this is because the actual guts of List() doesn’t get boxed/unboxed when you just pass it between numba and python. My use case involves moving a lot of different Dicts between python and numba. Most of the time I don’t need to read into these Dicts on the python side, but I do need to pass them around. It would be nice to have some kind of custom context object that was just like a struct or tuple that I could pass into numba functions and change as needed, but that doesn’t unbox into a native type.
My question is is there any straightforward way to make this sort of custom type that I can use to just pass around my dictionaries and stuff without any substantial boxing/unboxing. Something that can be AOT-compiled is preferable. Haven’t had much luck with jitclasses and structrefs, they don’t seem to vibe well with AOT compilation.