Notebook

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 []: