matlaboptimizationpre-allocation

I want advice about how to optimize my code. It takes too long for execution


I wrote a MATLAB code for finding seismic signal (ex. P wave) from SAC(seismic) file (which is read via another code). This algorithm is called STA/LTA trigger algorithm (actually not that important for my question)

Important thing is that actually this code works well, but since my seismic file is too big (1GB, which is for two months), it takes almost 40 minutes for executing to see the result. Thus, I feel the need to optimize the code.

I heard that replacing loops with advanced functions would help, but since I am a novice in MATLAB, I cannot get an idea about how to do it, since the purpose of code is scan through the every time series. Also, I heard that preallocation might help, but I have mere idea about how to actually do this.

Since this code is about seismology, it might be hard to understand, but my notes at the top might help. I hope I can get useful advice here. Following is my code.

function[pstime]=classic_LR(tseries,ltw,stw,thresh,dt)

% This is the code for "Classic LR" algorithm
% 'ns' is the number of measurement in STW-used to calculate STA 
% 'nl' is the number of measurement in LTW-used to calculate LTA
% 'dt' is the time gap between measurements i.e. 0.008s for HHZ and 0.02s for BHZ
% 'ltw' and 'stw' are long and short time windows respectively
% 'lta' and 'sta' are long and short time windows average respectively
% 'sra' is the ratio between 'sta' and 'lta' which will be each component
% for a vector containing the ratio for each measurement point 'i'
% Index 'i' denotes each measurement point and this will be converted to actual time

nl=fix(ltw/dt);
ns=fix(stw/dt);
nt=length(tseries);
aseries=abs(detrend(tseries));
sra=zeros(1,nt);

for i=1:nt-ns
    if  i>nl 
        lta=mean(aseries(i-nl:i));
        sta=mean(aseries(i:i+ns));
        sra(i)=sta/lta;
    else
        sra(i)=0;
    end
end

[k]=find(sra>thresh);
if ~isempty(k)
    pstime=k*dt;
else 
    pstime=0;
end

return;

Solution

  • If you have MATLAB 2016a or later, you can use movmean instead of your loop (this means you also don't need to preallocate anything):

    lta = movmean(aseries(1:nt-ns),nl+1,'Endpoints','discard');
    sta = movmean(aseries(nl+1:end),ns+1,'Endpoints','discard');
    sra = sta./lta;
    

    The only difference here is that you will get sra with no leading and trailing zeros. This is most likely to be the fastest way. If for instance, aseries is 'only' 8 MB than this method takes less than 0.02 second while the original method takes almost 6 seconds!


    However, even if you don't have Matlab 2016a, considering your loop, you can still do the following:

    1. Remove the else statement - sta(i) is already zero from the preallocating.
    2. Start the loop from nl+1, instead of checking when i is greater than nl.

    So your new loop will be:

    for i=nl+1:nt-ns
        lta = mean(aseries(i-nl:i));
        sta = mean(aseries(i:i+ns));
        sra(i)=sta/lta;
    end
    

    But it won't be so faster.