for loop in python is 10x slower than matlab
Asked Answered
T

5

5

I run python 2.7 and matlab R2010a on the same machine, doing nothing, and it gives me 10x different in speed

I looked online, and heard it should be the same order. Python will further slow down as if statement and math operator in the for loop

My question: is this the reality? or there is some other way let them in the same speed order?


Here is python code

import time

start_time = time.time()

for r in xrange(1000):

      for c in xrange(1000):

         continue

elapsed_time = time.time() - start_time

print 'time cost = ',elapsed_time

Output: time cost = 0.0377440452576

Here is matlab code

tic

for i = 1:1000

    for j = 1:1000

    end

end

toc

Output: Escaped time is 0.004200 seconds

Trahurn answered 21/6, 2013 at 19:13 Comment(4)
I am not too if there is any difference to the compiler optimizer, but did you try pass instead of continue? Maybe if you add a very basic calculation, t = t+1 then it would change.Dollhouse
Testing the loop with python -m timeit shows that using pass is a bit slower than continue, but not in a significant way. On my machine the loops take 16 msec, which is less than half of what the OP claims. Also note that MATLAB has a JIT, hence such differences are to be expected especially with simple loops.Pavlish
Might be worth putting something inside the loop, just in case Matlab is clever enough to realize the loops don't actually do anything, and has optimized them out.Silassilastic
@Pavlish Thanks for posting that link. I had not read it but it was very informative. I have updated my answer to address that.Dollhouse
D
10

The reason this is happening is related to the JIT compiler, which is optimizing the MATLAB for loop. You can disable/enable the JIT accelerator using feature accel off and feature accel on. When you disable the accelerator, the times change dramatically.

MATLAB with accel on: Elapsed time is 0.009407 seconds.

MATLAB with accel off: Elapsed time is 0.287955 seconds.

python: time cost = 0.0511920452118

Thus the JIT accelerator is directly causing the speedup that you are noticing. There is another thing that you should consider, which is related to the way that you defined the iteration indices. In both cases, MATLAB and python, you used Iterators to define your loops. In MATLAB you create the actual values by adding the square brackets ([]), and in python you use range instead of xrange. When you make these changes

% MATLAB
for i = [1:1000]
    for j = [1:1000]

# python
for r in range(1000):
  for c in range(1000):

The times become

MATLAB with accel on: Elapsed time is 0.338701 seconds.

MATLAB with accel off: Elapsed time is 0.289220 seconds.

python: time cost = 0.0606048107147

One final consideration is if you were to add a quick computation to the loop. ie t=t+1. Then the times become

MATLAB with accel on: Elapsed time is 1.340830 seconds.

MATLAB with accel off: Elapsed time is 0.905956 seconds. (Yes off was faster)

python: time cost = 0.147221088409

I think that the moral here is that the computation speeds of for loops, out-of-the box, are comparable for extremely simple loops, depending on the situation. However, there are other, numerical tools in python which can speed things up significantly, numpy and PyPy have been brought up so far.

Dollhouse answered 21/6, 2013 at 19:29 Comment(4)
Python's xrange is a simple iterator. If you want to compare the MATLAB version with the python's one you should use plain range.Pavlish
@Pavlish You are right, I changed my post to use range and updated the runtime, although nothing really changed.Dollhouse
What results do you get if you compare the same code blocks, but using Matlab's for i = 1:1000 vs Python 2.x's xrange?Silassilastic
I have updated my post giving the times for all different methods that I compared/discussedDollhouse
H
3

The basic Python implementation, CPython, is not meant to be super-speedy. If you need efficient matlab-style numerical manipulation, use the numpy package or an implementation of Python that is designed for fast work, such as PyPy or even Cython. (Writing a Python extension in C, which will of course be pretty fast, is also a possible solution, but in that case you may as well just use numpy and save yourself the effort.)

Heaver answered 21/6, 2013 at 19:32 Comment(0)
B
2

If Python execution performance is really crucial for you, you might take a look at PyPy

I did your test:

import time
for a in range(10):
    start_time = time.time()
    for r in xrange(1000):
        for c in xrange(1000):
            continue

    elapsed_time = time.time()-start_time
    print elapsed_time

with standard Python 2.7.3, I get:

0.0311839580536
0.0310959815979
0.0309510231018
0.0306520462036
0.0302460193634
0.0324130058289
0.0308878421783
0.0307397842407
0.0304911136627
0.0307500362396

whereas, using PyPy 1.9.0 (which corresponds to Python 2.7.2), I get:

0.00921821594238
0.0115230083466
0.00851202011108
0.00808095932007
0.00496387481689
0.00499391555786
0.00508499145508
0.00618195533752
0.005126953125
0.00482988357544

The acceleration of PyPy is really stunning and really becomes visible when its JIT compiler optimizations outweigh their cost. That's also why I introduced the extra for loop. For this example, absolutely no modification of the code was needed.

Blowzy answered 21/6, 2013 at 19:23 Comment(1)
The problem with PyPy is that it still doesn't support NumPy, so it's not an ideal MATLAB replacement.Endophyte
G
0

This is just my opinion, but I think the process is a bit more complex. Basically Matlab is an optimized layer of C, so with the appropriate initialization of matrices and minimization of function calls (avoid "." objects-like operators in Matlab) you obtain extremely different results. Consider the simple following example of wave generator with cosine function. Matlab time = 0.15 secs in practical debug session, Python time = 25 secs in practical debug session (Spyder), thus Python becomes 166x slower. Run directly by Python 3.7.4. machine the time is = 5 secs aprox, so still be a non negligible 33x.

MATLAB:

AW(1,:) = [800 , 0    ]; % [amp frec]
AW(2,:) = [300 , 4E-07]; 
AW(3,:) = [200 , 1E-06];
AW(4,:) = [ 50 , 4E-06];
AW(5,:) = [ 30 , 9E-06];
AW(6,:) = [ 20 , 3E-05];
AW(7,:) = [ 10 , 4E-05];
AW(8,:) = [  9 , 5E-04];
AW(9,:) = [  7 , 7E-04];
AW(10,:)= [  5 , 8E-03];

phas    = 0

tini    = -2*365 *86400; % 2 years backwards in seconds
dt      = 200;        % step, 200 seconds
tfin    = 0;          % present
vec_t   = ( tini: dt: tfin)'; % vector_time

nt      = length(vec_t);
vec_t   = vec_t - phas;
wave    = zeros(nt,1);

for it = 1:nt
    suma = 0;
    t    = vec_t(it,1);
    for iW = 1:size(AW,1)
        suma = suma + AW(iW,1)*cos(AW(iW,2)*t);
    end
    wave(it,1) = suma;
end

PYTHON:

import numpy as np

AW      = np.zeros((10,2))
AW[0,:] = [800 , 0.0]
AW[1,:] = [300 , 4E-07]; # [amp frec]
AW[2,:] = [200 , 1E-06];
AW[3,:] = [ 50 , 4E-06];
AW[4,:] = [ 30 , 9E-06];
AW[5,:] = [ 20 , 3E-05];
AW[6,:] = [ 10 , 4E-05];
AW[7,:] = [  9 , 5E-04];
AW[8,:] = [  7 , 7E-04];
AW[9,:] = [  5 , 8E-03];

phas    = 0

tini    = -2*365 *86400 # 2 years backwards
dt      = 200
tfin    = 0           # present
nt      = round((tfin-tini)/dt) + 1 
vec_t   = np.linspace(tini,tfin1,nt) - phas

wave    = np.zeros((nt))

for it in range(nt):
    suma   = 0
    t      = vec_t[fil]
    for iW in range(np.size(AW,0)):
        suma = suma + AW[iW,0]*np.cos(AW[iW,1]*t)
    #endfor iW
    wave[it] = suma
#endfor it

To deal such aspects in Python I would suggest to compile into executable directly to binary the numerical parts that may compromise the project (or for example C or Fortran into executable and be called by Python afterwards). Of course, other suggestions are appreciated.

Geomorphic answered 13/8, 2019 at 16:0 Comment(0)
A
0

I tested a FIR filter with MATLAB and same (adapted) code in Python, including a frequency sweep. The FIR filter is pretty huge, N = 100 order, I post below the two codes, but leave you here the timing results:

MATLAB: Elapsed time is 11.149704 seconds.

PYTHON: time cost = 247.8841781616211 seconds.

PYTHON IS 25 TIMES SLOWER !!!

MATLAB CODE (main):

f1 = 4000; % bandpass frequency (response = 1).
f2 = 4200; % bandreject frequency (response = 0).
N = 100;   % FIR filter order.
k = 0:2*N;
fs = 44100; Ts = 1/fs; % Sampling freq. and time.

% FIR Filter numerator coefficients:
Nz = Ts*(f1+f2)*sinc((f2-f1)*Ts*(k-N)).*sinc((f2+f1)*Ts*(k-N));
f = 0:fs/2;
w = 2*pi*f;
z = exp(-i*w*Ts);

% Calculation of the expected response:
Hz = polyval(Nz,z).*z.^(-2*N);
figure(1)
plot(f,abs(Hz))
title('Gráfica Respuesta Filtro FIR (Filter Expected Response)') 
xlabel('frecuencia f (Hz)') 
ylabel('|H(f)|') 
xlim([0, 5000])
grid on

% Sweep Frequency Test:

tic
% Start and Stop frequencies of sweep, t = tmax = 50 seconds = 5000 Hz frequency:

fmin = 1; fmax = 5000; tmax = 50; 
t = 0:Ts:tmax;
phase = 2*pi*fmin*t + 2*pi*((fmax-fmin).*t.^2)/(2*tmax);
x = cos(phase);

y = filtro2(Nz, 1, x); % custom filter function, not using "filter" library here.

figure(2)
plot(t,y)
title('Gráfica Barrido en Frecuencia Filtro FIR (Freq. Sweep)') 
xlabel('Tiempo Barrido: t = 10 seg = 1000 Hz') 
ylabel('y(t)') 
xlim([0, 50])
grid on
toc

MATLAB CUSTOM FILTER FUNCTION

function y = filtro2(Nz, Dz, x)

Nn = length(Nz);
Nd = length(Dz);


N = length(x);
Nm = max(Nn,Nd);


x1 = [zeros(Nm-1,1) ; x'];
y1 = zeros(Nm-1,1);
for n = Nm:N+Nm-1
    y1(n) = Nz(Nn:-1:1)*x1(n-Nn+1:n)/Dz(1);
    if Nd > 1
        y1(n) = y1(n) - Dz(Nd:-1:2)*y1(n-Nd+1:n-1)/Dz(1);
    end
end
y = y1(Nm:Nm+N-1);
end

PYTHON CODE (main):

import numpy as np
from matplotlib import pyplot as plt
import FiltroDigital as fd
import time

j = np.array([1j])
pi = np.pi

f1, f2 = 4000, 4200
N = 100
k = np.array(range(0,2*N+1),dtype='int')
fs = 44100; Ts = 1/fs;
Nz = Ts*(f1+f2)*np.sinc((f2-f1)*Ts*(k-N))*np.sinc((f2+f1)*Ts*(k-N));
f = np.arange(0, fs/2, 1)
w = 2*pi*f
z = np.exp(-j*w*Ts)
Hz = np.polyval(Nz,z)*z**(-2*N)
plt.figure(1)
plt.plot(f,abs(Hz))
plt.title("Gráfica Respuesta Filtro FIR") 
plt.xlabel("frecuencia f (Hz)") 
plt.ylabel("|H(f)|") 
plt.xlim(0, 5000)
plt.grid()
plt.show()


start_time = time.time()
fmin = 1; fmax = 5000; tmax = 50;
t = np.arange(0, tmax, Ts)
fase = 2*pi*fmin*t + 2*pi*((fmax-fmin)*t**2)/(2*tmax)
x = np.cos(fase)
y = fd.filtro(Nz, [1], x)
plt.figure(2)
plt.plot(t,y)
plt.title("Gráfica Barrido en Frecuencia Filtro FIR") 
plt.xlabel("Tiempo Barrido: t = 10 seg = 1000 Hz") 
plt.ylabel("y(t)") 
plt.xlim(0, 50)
plt.grid()
plt.show()
elapsed_time = time.time() - start_time
print('time cost = ', elapsed_time)

PYTHON CUSTOM FILTER FUNCTION

import numpy as np

def filtro(Nz, Dz, x):

    Nn = len(Nz); 
    Nd = len(Dz);
    Nz = np.array(Nz,dtype=float)
    Dz = np.array(Dz,dtype=float)
    x = np.array(x,dtype=float)


    N = len(x);
    Nm = max(Nn,Nd);
    
    x1 = np.insert(x, 0, np.zeros((Nm-1,), dtype=float))
    y1 = np.zeros((N+Nm-1,), dtype=float)              

     for n in range(Nm-1,N+Nm-1) :
        y1[n] = sum(Nz*np.flip( x1[n-Nn+1:n+1]))/Dz[0]   # = y1FIR[n]
        if Nd > 1:
            y1[n] = y1[n] - sum(Dz[1:]*np.flip( y1[n-Nd+1:n]))/Dz[0]
            print(y1[n])

    y = y1[Nm-1:]
    return y
Affirm answered 29/10, 2021 at 0:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.