In [282]:
%matplotlib inline
In [283]:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
In [284]:
ran_t = np.arange(0.0, 1.0, 1/44100, dtype=np.float32)
ran_t
Out[284]:
array([  0.00000000e+00,   2.26757365e-05,   4.53514731e-05, ...,
         9.99931931e-01,   9.99954641e-01,   9.99977291e-01], dtype=float32)

No decorator

In [285]:
def g(freq, t) -> np.int16:
    return np.sin(freq * 2 * np.pi * t)
In [286]:
vectorized_secs = %timeit -o Hz_17 = g(17, ran_t)
vectorized_secs.best
1000 loops, best of 3: 457 µs per loop

Out[286]:
0.0004566969659936149
In [287]:
Hz_17_16bit = np.int16(np.float32(32767) * Hz_17)
Hz_17_16bit
Out[287]:
array([   0,   79,  158, ..., -238, -158,  -79], dtype=int16)
In [288]:
ran_t.dtype, Hz_17.dtype, Hz_17_16bit.dtype
Out[288]:
(dtype('float32'), dtype('float64'), dtype('int16'))
In [289]:
plt.plot(Hz_17_16bit);
In [290]:
loop_secs = %timeit -o for t in ran_t: g(7, t)
loop_secs.best
1 loops, best of 3: 252 ms per loop

Out[290]:
0.25186636099533644
In [291]:
def comparison(slower, faster):
    'slower, faster: seconds'
    ratio = slower/faster
    order_of_magnitude = np.log10(ratio)
    return ratio, order_of_magnitude
In [292]:
print("With record_history disabled:\n"
      "Vectorized approach is %d times (about %.1f orders of magnitude) faster" 
      % comparison(slower=loop_secs.best, faster=vectorized_secs.best))
With record_history disabled:
Vectorized approach is 551 times (about 2.7 orders of magnitude) faster

With no decorator,

it's ~2.7 orders of magnitude faster to use a function as a numpy ufunc than to call it in a for loop (looping through ran_t)

A record_history-decorated version of the same function

In [293]:
from log_calls import record_history
@record_history()
def f(freq, t):
    return np.sin(freq * 2 * np.pi * t)

record_history disabled

In [294]:
f.record_history_settings.enabled = False
In [295]:
vectorized_secs_rh_disabled = %timeit -o Hz_17 = f(17, ran_t)
vectorized_secs_rh_disabled.best
1000 loops, best of 3: 490 µs per loop

Out[295]:
0.0004900190659973305
In [296]:
loop_secs_rh_disabled = %timeit -o for t in ran_t: f(7, t)
loop_secs_rh_disabled.best
1 loops, best of 3: 1.73 s per loop

Out[296]:
1.72951482499775
In [297]:
print("With record_history disabled:\n"
      "Vectorized approach is %d times (about %.1f orders of magnitude) faster" 
      % comparison(slower=loop_secs_rh_disabled.best, faster=vectorized_secs_rh_disabled.best))
With record_history disabled:
Vectorized approach is 3529 times (about 3.5 orders of magnitude) faster

With record_history disabled,

it's 3.5 orders of magnitude faster to use a function as a numpy ufunc than to call it in a for loop (looping through ran_t)

record_history enabled

In [298]:
f.record_history_settings.enabled = True
f.stats.clear_history()
In [299]:
vectorized_secs_rh_enabled = %timeit -o f.stats.clear_history(); Hz_17 = f(17, ran_t)
vectorized_secs_rh_enabled.best
1000 loops, best of 3: 701 µs per loop

Out[299]:
0.0007013606440013973
In [300]:
len(f.stats.history)
Out[300]:
1
In [301]:
def size_of_t_for_row(row):
    return f.stats.history[row].argvals[1].size

size_of_t_for_row(0)
Out[301]:
44100
In [302]:
f.stats.history[0].retval.size
Out[302]:
44100
In [303]:
f.stats.history
Out[303]:
(CallRecord(call_num=1, argnames=['freq', 't'], argvals=(17, array([  0.00000000e+00,   2.26757365e-05,   4.53514731e-05, ...,
         9.99931931e-01,   9.99954641e-01,   9.99977291e-01], dtype=float32)), varargs=(), explicit_kwargs=OrderedDict(), defaulted_kwargs=OrderedDict(), implicit_kwargs={}, retval=array([ 0.        ,  0.00242209,  0.00484416, ..., -0.00727302,
       -0.00484692, -0.00242842], dtype=float32), elapsed_secs=0.0004661083221435547, timestamp='11/07/14 20:33:05.452003', prefixed_func_name='f', caller_chain=['inner']),)
In [304]:
f.stats.clear_history()
In [305]:
loop_secs_rh_enabled = %timeit -o for t in ran_t: f(7, t); f.stats.clear_history()
loop_secs_rh_enabled.best
1 loops, best of 3: 10.5 s per loop

Out[305]:
10.49896942700434
In [306]:
print("With record_history enabled:\n"
      "Vectorized approach is %d times (about %.1f orders of magnitude) faster" 
      % comparison(slower=loop_secs_rh_enabled.best, faster=vectorized_secs_rh_enabled.best))
With record_history enabled:
Vectorized approach is 14969 times (about 4.2 orders of magnitude) faster

With record_history enabled,

it's 4+ orders of magnitude faster to use a function as a numpy ufunc than to call it in a for loop (looping through ran_t)

So recording history slows things down by another order of magnitude :|


In [307]:
f.stats.clear_history()
for t in ran_t: f(7, t)
df = f.stats.history_as_DataFrame
In [308]:
from IPython.display import display
display(df.head())
display(df.tail())
freq t retval elapsed_secs timestamp prefixed_fname caller_chain
call_num
1 7 0.000000 0.000000 0.000041 11/07/14 20:34:31.921803 'f' ['<module>']
2 7 0.000023 0.000997 0.000016 11/07/14 20:34:31.922090 'f' ['<module>']
3 7 0.000045 0.001995 0.000015 11/07/14 20:34:31.922319 'f' ['<module>']
4 7 0.000068 0.002992 0.000015 11/07/14 20:34:31.922545 'f' ['<module>']
5 7 0.000091 0.003989 0.000014 11/07/14 20:34:31.922770 'f' ['<module>']
freq t retval elapsed_secs timestamp prefixed_fname caller_chain
call_num
44096 7 0.999887 -0.004986 0.000012 11/07/14 20:34:41.982512 'f' ['<module>']
44097 7 0.999909 -0.003990 0.000011 11/07/14 20:34:41.982698 'f' ['<module>']
44098 7 0.999932 -0.002994 0.000011 11/07/14 20:34:41.982883 'f' ['<module>']
44099 7 0.999955 -0.001995 0.000015 11/07/14 20:34:41.983069 'f' ['<module>']
44100 7 0.999977 -0.000999 0.000012 11/07/14 20:34:41.983258 'f' ['<module>']
In [309]:
df[:5]
Out[309]:
freq t retval elapsed_secs timestamp prefixed_fname caller_chain
call_num
1 7 0.000000 0.000000 0.000041 11/07/14 20:34:31.921803 'f' ['<module>']
2 7 0.000023 0.000997 0.000016 11/07/14 20:34:31.922090 'f' ['<module>']
3 7 0.000045 0.001995 0.000015 11/07/14 20:34:31.922319 'f' ['<module>']
4 7 0.000068 0.002992 0.000015 11/07/14 20:34:31.922545 'f' ['<module>']
5 7 0.000091 0.003989 0.000014 11/07/14 20:34:31.922770 'f' ['<module>']
In [311]:
len(f.stats.history)
Out[311]:
44100
In [312]:
df[['t', 'retval']].head()
Out[312]:
t retval
call_num
1 0.000000 0.000000
2 0.000023 0.000997
3 0.000045 0.001995
4 0.000068 0.002992
5 0.000091 0.003989
In [313]:
plt.plot(df.t, df.retval);
In []: