我已阅读此内容(Is MATLAB faster than Python?),我发现它有很多ifs。
我在仍在Windows XP上运行的旧计算机上尝试过这个小实验。
在MATLAB R2010b中,我在命令窗口中复制并粘贴了以下代码:
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
。
答案 0 :(得分:11)
首先,使用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)
当然,在这种情况下,我们明确只计算一次值并将其复制10000次。但我们可以强制它实际反复计算,而且它仍然只需要0.12秒:
i = np.zeros((10000000,))
i = 4 * (x+i) * (1-(x+i))
其他选项包括在Cython中编写部分代码(编译为Python的C扩展),并使用Numba,JIT编译CPython中的代码。对于这样的玩具程序,两者都不合适 - 自动生成和编译C代码所花费的时间可能会淹没运行C代码而不是Python代码所节省的时间,如果您只是尝试优化一次性24-第二个过程。但在现实生活中的数值编程中,两者都非常有用。 (两者都与NumPy很好地配合。)
并且总会有新项目出现。
答案 1 :(得分:4)
(有些受过教育的)猜测是python在loop unrolling时代码上没有MATLAB does。这意味着MATLAB代码执行一个大型计算而不是许多(!)较小的计算。这是使用PyPy而不是CPython的主要原因,如PyPy does loop unrolling。
如果您正在使用python 2.X,则应将range
替换为xrange
,因为range
(在python 2.X中)会创建一个列表来迭代。
答案 2 :(得分:0)
py
脚本,使其运行速度与MATLAB一样快?因为 abarnet
已经为您提供了很多知识渊博的指示,让我加上我的两分钱(以及一些定量结果)。
(同样地,我希望你能原谅跳过for:
&amp;假设一个更复杂的计算任务)
查看代码,了解任何可能的算法改进,价值重用以及注册/缓存友好的安排( numpy.asfortranarray()
等)
尽可能在 numpy
中使用向量化代码执行/循环展开
使用LLVM编译器 numba
代码稳定部分代码
仅在代码的 final 等级上使用附加(JIT) - 编译器技巧(nogil = True,nopython = True),以避免常见的过早优化错误
< / LI>
初始代码样本来自FX竞技场(其中毫秒,微秒和(浪费)纳秒确实很重要 - 检查50%市场事件你有远远少于900毫秒的行动(端到端bi) - 方向交易),而不是谈论HFT ...)处理 EMA(200,CLOSE)
- 一个大约5200+的最后200英镑美元蜡烛/酒吧的非平凡指数移动平均线行:
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
对于这个“经典”代码,只是非常numba
编译步骤比普通的python / numpy代码执行有所改进
21x 降至半毫秒
# 541L
从大约11499 [us](是的,从大约11500微秒到大约541 [我们])
# 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位的精度。它就在那里。
纳秒很重要。在这里,精确度是目标。