Last active
August 30, 2021 19:43
-
-
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)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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