Skip to content

Instantly share code, notes, and snippets.

@brunojacobs
Last active August 30, 2021 19:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brunojacobs/16da978b850faa4955b81b7cce8ecbf0 to your computer and use it in GitHub Desktop.
Save brunojacobs/16da978b850faa4955b81b7cce8ecbf0 to your computer and use it in GitHub Desktop.
Extension to https://gist.github.com/ufechner7/95db14f734edd51dcd9b that includes namedtuple and structref benchmarks (and made Python3 compatible)
import sys
import time
import timeit
import numba
import numpy as np
N_REPS = 1000000
@numba.njit
def sub(a, b, result):
""" Calculate the difference of two 3d vectors. """
result[0] = a[0] - b[0]
result[1] = a[1] - b[1]
result[2] = a[2] - b[2]
@numba.njit
def sub_plain(a, b, result):
sub(a, b, result)
@numba.njit
def reps_sub_plain(a, b, result):
for _ in range(N_REPS):
sub_plain(a, b, result)
@numba.njit
def sub_num(container):
sub(container[0], container[1], container[2])
@numba.njit
def reps_sub_num(container):
for _ in range(N_REPS):
sub_num(container)
@numba.njit
def sub_attr(container):
sub(container.v_wind, container.v_wind_gnd, container.result)
@numba.njit
def reps_sub_attr(container):
for _ in range(N_REPS):
sub_attr(container)
@numba.njit
def sub_dict(container):
sub(container['v_wind'], container['v_wind_gnd'], container['result'])
@numba.njit
def reps_sub_dict(container):
for _ in range(N_REPS):
sub_dict(container)
#
# NUMPY ARRAY
#
np_arr = np.array((
[12., .0, .0],
[8., .0, .0],
[.0, .0, .0]
))
#
# NUMPY RECORD ARRAY
#
np_recarr = np.recarray(
3,
dtype=[
('v_wind', np.float64),
('v_wind_gnd', np.float64),
('result', np.float64),
],
buf=np.array((
[12., .0, .0],
[8., .0, .0],
[.0, .0, .0]
)).T
)
#
# NAMEDTUPLE
#
from collections import namedtuple
NT = namedtuple('NT', ['v_wind', 'v_wind_gnd', 'result'])
nt = NT(
v_wind=np.array([12., .0, .0]),
v_wind_gnd=np.array([8., .0, .0]),
result=np.array([4., .0, .0])
)
#
# STRUCTREF
#
from numba.core import types
from numba.experimental import structref
@structref.register
class MyStructType(types.StructRef):
def preprocess_fields(self, fields):
return tuple((name, types.unliteral(typ)) for name, typ in fields)
class MyStruct(structref.StructRefProxy):
def __new__(cls, v_wind, v_wind_gnd, result):
return structref.StructRefProxy.__new__(cls, v_wind, v_wind_gnd, result)
@property
def v_wind(self):
# return self.v_wind
return MyStruct_get_v_wind(self)
@property
def v_wind_gnd(self):
# return self.v_wind_gnd
return MyStruct_get_v_wind_gnd(self)
@property
def result(self):
# return self.result
return MyStruct_get_result(self)
@numba.njit
def MyStruct_get_v_wind(self):
return self.v_wind
@numba.njit
def MyStruct_get_v_wind_gnd(self):
return self.v_wind_gnd
@numba.njit
def MyStruct_get_result(self):
return self.result
structref.define_proxy(MyStruct, MyStructType, ["v_wind", "v_wind_gnd", "result"])
nb_sf = MyStruct(
v_wind=np.array([12., .0, .0]),
v_wind_gnd=np.array([8., .0, .0]),
result=np.array([4., .0, .0])
)
#
# JITCLASS
#
from numba.experimental import jitclass
spec = [
('v_wind', types.float64[:]),
('v_wind_gnd', types.float64[:]),
('result', types.float64[:]),
]
@jitclass(spec)
class MyJitclass(object):
def __init__(self, v_wind, v_wind_gnd, result):
self.v_wind = v_wind
self.v_wind_gnd = v_wind_gnd
self.result = result
nb_jc = MyJitclass(
v_wind=np.array([12., .0, .0]),
v_wind_gnd=np.array([8., .0, .0]),
result=np.array([4., .0, .0])
)
#
# TYPED DICT
#
nb_td = numba.typed.Dict()
nb_td['v_wind'] = np.array([12., .0, .0])
nb_td['v_wind_gnd'] = np.array([8., .0, .0])
nb_td['result'] = np.array([.0, .0, .0])
a = np_recarr.v_wind.copy()
b = np_recarr.v_wind_gnd.copy()
result = np_recarr.result.copy()
if __name__ == "__main__":
print('Python:', sys.version)
print('Numba:', numba.__version__)
print('Numpy:', np.__version__)
print('n_repetitions:', N_REPS)
print('')
print('loops outside numba jitted code (unboxing happens each iteration)')
print('')
sub_plain(a, b, result)
print('separate input variables', timeit.timeit(lambda: sub_plain(a, b, result), number=N_REPS))
sub_num(np_arr)
print('numpy array', timeit.timeit(lambda: sub_num(np_arr), number=N_REPS))
sub_attr(np_recarr)
print('numpy rec array (attribute style)', timeit.timeit(lambda: sub_attr(np_recarr), number=N_REPS))
sub_dict(np_recarr)
print('numpy rec array (dict style)', timeit.timeit(lambda: sub_dict(np_recarr), number=N_REPS))
sub_attr(nt)
print('named tuple (attribute style)', timeit.timeit(lambda: sub_attr(nt), number=N_REPS))
sub_attr(nb_sf)
print('numba structref (attribute style)', timeit.timeit(lambda: sub_attr(nb_sf), number=N_REPS))
sub_attr(nb_jc)
print('numba jitclass (attribute style)', timeit.timeit(lambda: sub_attr(nb_jc), number=N_REPS))
sub_dict(nb_td)
print('numba typeddict (dict style)', timeit.timeit(lambda: sub_dict(nb_td), number=N_REPS))
print('')
print('loop within numba jitted code (unboxing happens once)')
print('')
reps_sub_num(np_arr)
time_start = time.time()
reps_sub_num(np_arr)
time_stop = time.time() - time_start
print('numpy array', time_stop)
reps_sub_attr(np_recarr)
time_start = time.time()
reps_sub_attr(np_recarr)
time_stop = time.time() - time_start
print('numpy rec array (attribute style)', time_stop)
reps_sub_dict(np_recarr)
time_start = time.time()
reps_sub_dict(np_recarr)
time_stop = time.time() - time_start
print('numpy rec array (dict style)', time_stop)
reps_sub_attr(nt)
time_start = time.time()
reps_sub_attr(nt)
time_stop = time.time() - time_start
print('named tuple (attribute style)', time_stop)
reps_sub_attr(nb_sf)
time_start = time.time()
reps_sub_attr(nb_sf)
time_stop = time.time() - time_start
print('numba structref (attribute style)', time_stop)
reps_sub_attr(nb_jc)
time_start = time.time()
reps_sub_attr(nb_jc)
time_stop = time.time() - time_start
print('numba jitclass (attribute style)', time_stop)
reps_sub_dict(nb_td)
time_start = time.time()
reps_sub_dict(nb_td)
time_stop = time.time() - time_start
print('numba typeddict (dict style)', time_stop)
"""
Python: 3.9.6 (default, Aug 18 2021, 12:38:10)
[Clang 10.0.0 ]
Numba: 0.54.0
Numpy: 1.20.3
n_repetitions: 1000000
loops outside numba jitted code (unboxing happens each iteration)
separate input variables 0.6867476439999995
numpy array 0.3249390510000003
numpy rec array (attribute style) 1.9444237969999998
numpy rec array (dict style) 1.8849335660000008
named tuple (attribute style) 1.6423331920000006
numba structref (attribute style) 0.8521991450000002
numba jitclass (attribute style) 0.6022664849999995
numba typeddict (dict style) 1.6010483410000003
loop within numba jitted code (unboxing happens once)
numpy array 0.0012557506561279297
numpy rec array (attribute style) 0.0011591911315917969
numpy rec array (dict style) 0.0011630058288574219
named tuple (attribute style) 0.0011470317840576172
numba structref (attribute style) 0.0028722286224365234
numba jitclass (attribute style) 0.0020322799682617188
numba typeddict (dict style) 0.22297120094299316
"""
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment