Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Assess Superlet Performance Issues #176

Open
pantaray opened this issue Jan 5, 2022 · 3 comments
Open

Assess Superlet Performance Issues #176

pantaray opened this issue Jan 5, 2022 · 3 comments
Labels
Explore Examine novel functionality/proposed changes etc. Does not necessarily involve coding things. Performance Improve the number crunching

Comments

@pantaray
Copy link
Member

pantaray commented Jan 5, 2022

Computing a spectrum using superlets is at times significantly slower than a (comparable) wavelet setup, e.g., same data-set (3 trials, 4 channels), auto-freq selection (0-500Hz), ca. 90 seconds for max_order = 2 superlets compared to around 8 seconds for Morlet.
The increase in computational time seems non-linear wrt max_order too (for max_order = 10 the runtime for the same dataset is ca. 20 minutes)

@pantaray pantaray added the Explore Examine novel functionality/proposed changes etc. Does not necessarily involve coding things. label Jan 5, 2022
@tensionhead tensionhead added the Performance Improve the number crunching label Jan 5, 2022
@tensionhead
Copy link
Contributor

tensionhead commented Jan 5, 2022

So I just ran a few ad-hoc backend timing tests, and these are the results for a single 25 000 sample signal:

order_max run time
2 667ms
10 3340ms
20 6860ms

I see pretty perfect linear scaling 😄

@pantaray
Copy link
Member Author

pantaray commented Jan 5, 2022

Hm, can't reproduce this with the frontend. Using the synthetic data-set from the testing module defined in _make_tf_signal

def _make_tf_signal(nChannels, nTrials, seed, fadeIn=None, fadeOut=None):
(using as mentioned 4 channels and 3 trials) the timing for order_min = 1 and order_max = 2 is:

Syncopy <freqanalysis> INFO: Automatic superlet frequency selection from 0.0Hz to 500.0Hz
100% |███████████████████████ 3/3 [01:21<00:00]

for order_max = 4 I get

Syncopy <freqanalysis> INFO: Automatic superlet frequency selection from 0.0Hz to 500.0Hz
100% |███████████████████████| 3/3 [04:05<00:00]

and order_max = 8 gives

Syncopy <freqanalysis> INFO: Automatic superlet frequency selection from 0.0Hz to 500.0Hz
100% |███████████████████████| 3/3 [13:56<00:00]

@tensionhead
Copy link
Contributor

tensionhead commented Jan 12, 2022

I did a few frontend tests, so simple calls ala freqanalysis(data, method='superlet', order_max=..), the data has 3 Trials. 10 Channels and 2600 samples:

order_max = 2 gives 3.1s runtime and profiled:

 ncalls  tottime  percall  cumtime  percall filename:lineno(function)
      954    2.006    0.002    2.006    0.002 {built-in method scipy.fft._pocketfft.pypocketfft.c2c}
        3    0.685    0.228    3.094    1.031 superlet.py:107(multiplicativeSLT)
      318    0.153    0.000    0.153    0.000 superlet.py:286(time)
      318    0.141    0.000    2.209    0.007 signaltools.py:334(_freq_domain_conv)
      642    0.036    0.000    0.036    0.000 {built-in method numpy.zeros}
        6    0.015    0.003    2.409    0.401 superlet.py:336(cwtSL)
        3    0.013    0.004    0.022    0.007 dataset.py:584(__setitem__)

order_max = 4 gives 9.2s runtime and profiled:

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     1908    6.594    0.003    6.594    0.003 {built-in method scipy.fft._pocketfft.pypocketfft.c2c}
        3    1.332    0.444    9.164    3.055 superlet.py:107(multiplicativeSLT)
      636    0.490    0.001    0.490    0.001 superlet.py:286(time)
      636    0.467    0.001    7.223    0.011 signaltools.py:334(_freq_domain_conv)
     1284    0.098    0.000    0.098    0.000 {built-in method numpy.zeros}
       12    0.052    0.004    7.832    0.653 superlet.py:336(cwtSL)
     1908    0.029    0.000    0.120    0.000 helper.py:109(_fix_shape)
        3    0.014    0.005    0.022    0.007 dataset.py:584(__setitem__)

order_max = 8 gives 33.2s runtime and profiled:

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     3816   26.142    0.007   26.142    0.007 {built-in method scipy.fft._pocketfft.pypocketfft.c2c}
        3    2.665    0.888   33.175   11.058 superlet.py:107(multiplicativeSLT)
     1272    1.754    0.001   28.438    0.022 signaltools.py:334(_freq_domain_conv)
     1272    1.742    0.001    1.742    0.001 superlet.py:286(time)
     2568    0.365    0.000    0.365    0.000 {built-in method numpy.zeros}
       24    0.156    0.006   30.510    1.271 superlet.py:336(cwtSL)
     3816    0.094    0.000    0.445    0.000 helper.py:109(_fix_shape)
     1276    0.041    0.000    0.041    0.000 {built-in method numpy.arange}
     1272    0.028    0.000    0.028    0.000 {method 'copy' of 'numpy.ndarray' objects}
     1272    0.025    0.000    0.065    0.000 superlet.py:383(_get_superlet_support)
     8949    0.019    0.000    0.019    0.000 {built-in method numpy.array}
     1272    0.018    0.000   28.531    0.022 signaltools.py:432(fftconvolve)
     5088    0.017    0.000    0.048    0.000 helper.py:41(_init_nd_shape_and_axes)
     1272    0.016    0.000    0.027    0.000 signaltools.py:263(_centered)
     3816    0.015    0.000   26.665    0.007 basic.py:141(c2cn)
        3    0.014    0.005    0.022    0.007 dataset.py:584(__setitem__)

So there is indeed some unexpected scaling, order_max doubles but runtime triples. However not in that ludicrous range (seconds vs. minutes) that @pantaray reported for a smaller dataset with only 12 signals (here we have 3 * 10 = 30 signals).

EDIT: small inspection of the profiles shows, that scipy.fft._... itself does not scale with a factor of 2 but rather more 3ish :D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Explore Examine novel functionality/proposed changes etc. Does not necessarily involve coding things. Performance Improve the number crunching
Projects
None yet
Development

No branches or pull requests

2 participants