Iâm trying to split the simulation into multiple files. I thought that I couldnât do that because of the globals, but there might be a way. Itâd be able to toggle just the decorator, but since the functions call each other, it doesnât seem possible to do in this way. One neat approach might be to import functions in a way that applies the decorator to each function within it: from dEdt import * using decorator
. Maybe https://github.com/numba/numba/issues/5028 would solve some of these issues?
#decorator_test.py
from numba import njit, jit, objmode
from decorators import timer, jit_timer, reset_globals, profile_results
from cupy_or_numpy import xp
from dEdt import DE_dEdt_njit, dEdt_njit
#decorator = jit_timer
decorator = njit
dEdt_njit = decorator(dEdt_njit)
DE_dEdt_njit = decorator(DE_dEdt_njit)
@timer
def test():
tO = xp.arange(10)*10 + xp.random.uniform(0,4,10)
tD = xp.arange(10)*10 + xp.random.uniform(0,4,10)
return DE_dEdt_njit(tD, tO, 150)
@timer
def main():
a = test()
print(a)
if __name__ == '__main__':
reset_globals()
main()
profile_results()
#cupy_or_numpy.py
try:
import cupy as xp
print("using cupy")
except:
import numpy as xp
print("using numpy")
#decorators.py
import time
from numba import njit, jit, objmode
from cupy_or_numpy import xp
results = {}
tree = {'stack':['main'], 'main':set()}
def wrapper_objm_start(f):
start = time.time()
tree[ tree['stack'][-1] ].add( f.__name__ )
tree['stack'] += [ f.__name__ ]
if f.__name__ not in results:
tree[f.__name__] = set()
# print(tree['stack'])
return start
def wrapper_objm_end(f, start):
run_time = time.time() - start
if f.__name__ in results:
results[f.__name__] += [run_time]
else:
results[f.__name__] = [run_time]
tree['stack'] = tree['stack'][:-1]
def timer(f):
def wrapper(*args, **kwargs):
start = wrapper_objm_start(f)
g = f(*args)
wrapper_objm_end(f, start)
return g
return wrapper
def jit_timer(f):
jf = njit(f)
@njit(cache=False)
def wrapper(*args):
with objmode(start='float64'):
start = wrapper_objm_start(f)
g = jf(*args)
# g = f(*args)
with objmode():
wrapper_objm_end(f, start)
return g
return wrapper
def reset_globals():
global results
results = {}
global tree
tree = {'stack':['main'], 'main':set()}
def print_tree(node, layer):
for n in node:
rt = xp.sum(results[n])
rtr = rt / xp.sum(results['main'])
print('{0:>6.3f} {1:.03f}'.format( rt, rtr ), '-|-'*layer, n)
print_tree(tree[n], layer+1)
def profile_results():
print(results)
print(tree)
l = []
for k in results:
a = xp.asarray(results[k])
# l += [[k+' '*(17-len(k)), xp.sum(a[1:])]]
l += [[k+' '*(17-len(k)), xp.sum(a)]]
l = sorted(l, key=lambda x: x[1])
# for i in range(len(l)):
# print( '{:.6f}'.format( l[i][1] ), l[i][0] )
# print( l[i][0], "{:.6f}".format( l[i][1] ) )
print_tree(tree['main'], 0)
Since DE_dEdt_njit calls dEdt_njit it seems that I canât apply the decorator outside of this file.
#dEdt
from decorators import timer, jit_timer, reset_globals, profile_results
from cupy_or_numpy import xp
def dEdt_njit(tD, tO, i, T):
sum1 = 0.0
for j in range(tO.size):
# if tO[j] + tO[i] > 0:
n = tO[j] * ( ( tO[j] - tO[i] ) - ( tO[i] / T ) * ( tO[j] + tO[i] ) )
d = ( (tO[j] + tO[i]) ** 3.0 )
e = xp.exp( -( tO[j] + tO[i] ) / T)
sum1 += (n / d) * e
sum2 = 0.0
for j in range(tD.size):
# if tD[j] + tO[i] > 0:
n = tD[j] * ( ( tD[j] - tO[i] ) - ( tO[i] / T ) * ( tD[j] + tO[i] ) )
d = ( (tD[j] + tO[i]) ** 3.0 )
e = xp.exp( -( tD[j] + tO[i] ) / T)
sum2 += (n / d) * e
return 2 * (sum1 - sum2)
def DE_dEdt_njit(tD, tO, T):
r = xp.zeros(tO.size)
for i in xp.arange(tO.size):
r[i] = -dEdt_njit(tD, tO, i, T)
return r