【问题标题】:for loop in python is 10x slower than matlabpython中的for循环比matlab慢10倍
【发布时间】:2013-06-19 00:34:56
【问题描述】:

我在同一台机器上运行 python 2.7 和 matlab R2010a,什么都不做,它给了我 10 倍的速度差异

我上网查了一下,听说应该是同一个顺序。 Python 会进一步减慢 for 循环中的 if 语句和数学运算符

我的问题:这是现实吗?还是有其他方法让它们以相同的速度顺序排列?


这里是python代码

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

这是matlab代码

tic

for i = 1:1000

    for j = 1:1000

    end

end

toc

Output: Escaped time is 0.004200 seconds

【问题讨论】:

  • 如果编译器优化器有什么不同的话,我也不是,但是您是否尝试通过而不是继续?也许如果你添加一个非常基本的计算,t = t+1 那么它会改变。
  • python -m timeit 测试循环表明使用passcontinue 慢一点,但不是很明显。在我的机器上,循环需要 16 毫秒,不到 OP 声称的一半。另请注意,MATLAB 有一个JIT,因此尤其是使用简单循环时会出现这种差异。
  • 可能值得在循环中加入一些东西,以防万一 Matlab 足够聪明地意识到循环实际上并没有做任何事情,并且已经优化了它们。
  • @Bakuriu 感谢您发布该链接。我没有读过它,但它的内容非常丰富。我已经更新了我的答案来解决这个问题。

标签: python matlab for-loop


【解决方案1】:

发生这种情况的原因与 JIT 编译器有关,该编译器正在优化 MATLAB for 循环。您可以使用 feature accel offfeature accel on 禁用/启用 JIT 加速器。当您禁用加速器时,时间会发生巨大变化。

MATLAB 开启加速:Elapsed time is 0.009407 seconds.

关闭加速的 MATLAB:Elapsed time is 0.287955 seconds.

蟒蛇:time cost = 0.0511920452118

因此,JIT 加速器直接导致了您注意到的加速。您还应该考虑另一件事,这与您定义迭代索引的方式有关。在 MATLAB 和 python 两种情况下,您都使用迭代器来定义循环。在 MATLAB 中,您通过添加方括号 ([]) 创建实际值,而在 python 中,您使用 range 而不是 xrange。当您进行这些更改时

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

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

时代变了

MATLAB 开启加速:Elapsed time is 0.338701 seconds.

关闭加速的 MATLAB:Elapsed time is 0.289220 seconds.

蟒蛇:time cost = 0.0606048107147

最后一个考虑因素是您是否要向循环添加快速计算。即t=t+1。那么时代就变成了

MATLAB 开启加速:Elapsed time is 1.340830 seconds.

MATLAB 关闭加速:Elapsed time is 0.905956 seconds.(是关闭更快)

蟒蛇:time cost = 0.147221088409

我认为这里的寓意是,开箱即用的 for 循环的计算速度与极其简单的循环相当,具体取决于具体情况。但是,python 中还有其他可以显着加快速度的数值工具,目前已经提出了 numpy 和 PyPy。

【讨论】:

  • Python 的xrange 是一个简单的迭代器。如果您想将 MATLAB 版本与 python 版本进行比较,您应该使用普通的range
  • @Bakuriu 你说得对,我将帖子更改为使用范围并更新了运行时,尽管没有真正改变。
  • 如果您比较相同的代码块,但使用 Matlab 的 for i = 1:1000 与 Python 2.x 的 xrange,您会得到什么结果?
  • 我已经更新了我的帖子,给出了我比较/讨论的所有不同方法的时间
【解决方案2】:

基本的 Python 实现,CPython,并不意味着超级快速。如果您需要高效的 matlab 式数值操作,请使用 the numpy package 或专为快速工作而设计的 Python 实现,例如 PyPy 甚至 Cython。 (用 C 编写 Python 扩展,当然会很快,这也是一种可能的解决方案,但在这种情况下,您最好使用 numpy 并节省自己的精力。)

【讨论】:

    【解决方案3】:

    如果 Python 执行性能对你来说真的很重要,你可以看看PyPy

    我做了你的测试:

    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
    

    使用标准 Python 2.7.3,我得到:

    0.0311839580536
    0.0310959815979
    0.0309510231018
    0.0306520462036
    0.0302460193634
    0.0324130058289
    0.0308878421783
    0.0307397842407
    0.0304911136627
    0.0307500362396
    

    然而,使用 PyPy 1.9.0(对应于 Python 2.7.2),我得到:

    0.00921821594238
    0.0115230083466
    0.00851202011108
    0.00808095932007
    0.00496387481689
    0.00499391555786
    0.00508499145508
    0.00618195533752
    0.005126953125
    0.00482988357544
    

    PyPy 的加速确实令人惊叹,并且当其 JIT 编译器优化超过其成本时,它确实变得显而易见。这也是我引入额外 for 循环的原因。对于这个例子,绝对不需要修改代码。

    【讨论】:

    • PyPy 的问题是它仍然不支持 NumPy,所以它不是一个理想的 MATLAB 替代品。
    【解决方案4】:

    这只是我的看法,但我认为这个过程有点复杂。基本上,Matlab 是 C 的优化层,因此通过适当的矩阵初始化和函数调用的最小化(避免在 Matlab 中使用“.”类似对象的运算符),您将获得截然不同的结果。考虑以下具有余弦函数的波发生器的简单示例。 Matlab 时间 = 实际调试会话中的 0.15 秒,Python 时间 = 实际调试会话 (Spyder) 中的 25 秒,因此 Python 变得慢 166 倍。由 Python 3.7.4 直接运行。机器时间大约是 5 秒,所以仍然是不可忽略的 33 倍。

    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
    

    为了在 Python 中处理这些方面,我建议直接编译成可执行文件以二进制可能危及项目的数字部分(或者例如 C 或 Fortran 成可执行文件,然后由 Python 调用)。当然,也欢迎其他建议。

    【讨论】:

      【解决方案5】:

      我使用 MATLAB 和 Python 中的相同(改编)代码测试了 FIR 滤波器,包括频率扫描。 FIR 滤波器非常大,N = 100 阶,我在下面两个代码中发布,但将时序结果留在这里:

      MATLAB:经过的时间是 11.149704 秒。

      PYTHON:时间成本 = 247.8841781616211 秒。

      PYTHON 慢 25 倍!!!

      MATLAB 代码(主要):

      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 自定义过滤功能

      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 代码(主要):

      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 自定义过滤器功能

      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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-07-01
        • 2017-11-29
        • 1970-01-01
        • 1970-01-01
        • 2014-01-21
        • 2021-10-06
        相关资源
        最近更新 更多