MATLAB 比 Python 快吗(简单的小实验)

Is MATLAB faster than Python (little simple experiment)

我读过这个(Is MATLAB faster than Python?),我发现它有很多假设。

我在仍然运行 Windows XP 的旧计算机上进行了这个小实验。

在 MATLAB R2010b 中,我在命令 Window:

中复制并粘贴了以下代码
tic
x = 0.23;
for i = 1:100000000
  x = 4 * x * (1 - x);
end
toc
x

结果是:

Elapsed time is 0.603583 seconds.

x =

    0.947347510922557

然后我使用以下脚本保存了一个 py 文件:

import time
t = time.time()
x = 0.23
for i in range(100000000): x = 4 * x * (1 - x)
elapsed = time.time() - t
print(elapsed)
print(x)

我按了F5结果是

49.78125
0.9473475109225565

在 MATLAB 中花费了 0.60 秒; Python 花了 49.78 秒(永恒!!)。

所以问题是:有没有一种简单的方法可以让Python和MATLAB一样快?

具体来说:如何更改我的 py 脚本,使其运行速度与 MATLAB 一样快?


更新

我在 PyPy 中尝试过相同的实验(复制并粘贴与上面相同的代码):它在 1.0470001697540283 秒内在与以前相同的机器上完成.

我用 1e9 个循环重复了实验。

MATLAB 结果:

Elapsed time is 5.599789 seconds.
1.643573442831396e-004

PyPy 结果:

8.609999895095825
0.00016435734428313955

我也尝试过正常的 while 循环,结果相似:

t = time.time()
x = 0.23
i = 0
while (i < 1000000000):
    x = 4 * x * (1 - x)
    i += 1

elapsed = time.time() - t
elapsed
x

结果:

8.218999862670898
0.00016435734428313955

一会儿我要试试NumPy

一个(有点受过教育的)猜测是 python 不执行 loop unrolling on your code while MATLAB does. This means the MATLAB code is performing one large computation rather than many (!) smaller ones. This is a major reason for going with PyPy rather than CPython, as PyPy does loop unrolling

如果您使用 python 2.X,您应该将 range 替换为 xrange,因为 range(在 python 2.X) 创建一个列表来迭代。

首先,使用 time 不是测试此类代码的好方法。但是让我们忽略它。


当您的代码每次循环执行大量循环和重复非常相似的工作时,PyPy 的 JIT 会做得很好。当该代码每次都执行 完全相同的 事情时,对于可以从循环中提取的常量值,它会做得更好。另一方面,CPython每次循环迭代都要执行多个字节码,所以会很慢。从我机器上的快速测试来看,CPython 3.4.1 需要 24.2 秒,但 PyPy 2.4.0/3.2.5 需要 0.0059 秒。

IronPython 和 Jython 也是 JIT 编译的(尽管使用更通用的 JVM 和 .NET JIT),因此对于此类工作它们往往比 CPython 更快,因为嗯。


您通常也可以通过使用 NumPy 数组和向量运算而不是 Python 列表和循环来加快 CPython 本身的工作速度。例如,以下代码耗时 0.011 秒:

i = np.arange(10000000)
i[:] = 4 * x * (1-x)

当然在那种情况下,我们明确地只计算一次值并将其复制 10000000 次。但是我们可以强迫它实际计算一遍又一遍,它仍然只需要 0.12 秒:

i = np.zeros((10000000,))
i = 4 * (x+i) * (1-(x+i))

其他选项包括在 Cython (which compiles to a C extension for Python), and using Numba 中编写部分代码,它在 CPython 中进行 JIT 编译代码。对于像这样的玩具程序,两者都可能不合适——如果您只是想优化一次性的 24 秒过程。但在现实生活中的数值编程中,两者都非常有用。 (并且两者都与 NumPy 配合得很好。)

而且总是有新的项目出现。

问:如何更改我的 py 脚本,使其运行速度与 MATLAB 一样快?

因为abarnet已经给了大家很多知识性的指导,再补充一下我的二毛钱(还有一些量化的结果)。

(同样,我希望您能原谅跳过 for: 并承担更复杂的计算任务)

  • 查看代码以了解任何可能的算法改进、价值重用和 register/cache-friendly 安排(numpy.asfortranarray() 等人)

  • 尽可能在numpy中使用向量化代码执行/循环展开

  • 使用类似的 LLVM 编译器 numba 用于代码的 稳定 部分

  • 仅在代码的 final 等级上使用额外的 (JIT)-编译器技巧(nogil = True, nopython = True)以避免常见的过早-优化错误

可能的成就确实很大:

初始代码示例取自 FX arena(其中毫秒、微秒和(浪费的)纳秒确实很重要 - 检查 50% 的市场事件,您的行动时间远远少于 900 毫秒(端到端双向交易 ),不是说 HFT ... ) 用于处理 EMA(200,CLOSE) - 最近 200 GBPUSD candles/bars 的非平凡指数移动平均线大约 5200 多行的数组:

import numba
#@jit                                               # 2015-06 @autojit deprecated
@numba.jit('f8[:](i8,f8[:])')
def numba_EMA_fromPrice( N_period, aPriceVECTOR ):
    EMA = aPriceVECTOR.copy()
    alf = 2. / ( N_period + 1 )
    for aPTR in range( 1, EMA.shape[0] ):
        EMA[aPTR] = EMA[aPTR-1] + alf * ( aPriceVECTOR[aPTR] - EMA[aPTR-1] )
    return EMA

对于这段 "classical" 代码,仅 numba 编译步骤就比普通的 python/numpy 代码执行

有了改进

21x 减少到大约 半毫秒

#   541L

从大约 11499 [us](是的,从大约 11500 微秒到仅 541 [us])

#       classical numpy
# aClk.start();X[:,7] = EMA_fromPrice( 200, price_H4_CLOSE );aClk.stop()
# 11499L

但是,如果您对算法更加谨慎,并重新设计它以便更智能、更有效地工作,结果会更加丰硕

@numba.jit
def numba_EMA_fromPrice_EFF_ALGO( N_period, aPriceVECTOR ):
    alfa    = 2. / ( N_period + 1 )
    coef    = ( 1 - alfa )
    EMA     = aPriceVECTOR * alfa
    EMA[1:]+= EMA[0:-1]    * coef
    return EMA

#   aClk.start();numba_EMA_fromPrice_EFF_ALGO( 200, price_H4_CLOSE );aClk.stop()
#   Out[112]: 160814L                               # JIT-compile-pass
#   Out[113]:    331L                               # re-use 0.3 [ms] v/s 11.5 [ms] CPython
#   Out[114]:    311L
#   Out[115]:    324L

多CPU核处理的最后润色


46x 加速 大约四分之一毫秒

# ___________vvvvv__________# !!!     !!! 
#@numba.jit( nogil = True ) # JIT w/o GIL-lock w/ multi-CORE ** WARNING: ThreadSafe / DataCoherency measures **
#   aClk.start();numba_EMA_fromPrice_EFF_ALGO( 200, price_H4_CLOSE );aClk.stop()
#   Out[126]: 149929L                               # JIT-compile-pass
#   Out[127]:    284L                               # re-use 0.3 [ms] v/s 11.5 [ms] CPython
#   Out[128]:    256L

作为最后的奖励。有时更快并不等同于更好。

惊讶吗?

不,这没什么奇怪的。尝试让 MATLAB 计算 SQRT( 2 ) 到小数点后约 500.000.000 位的精度。好了。

纳秒很重要。这里越多,精度就是目标。


这不值得花费时间和精力吗?当然是。