【问题标题】:Why is numpy's exp slower than Matlab? How to make it faster?为什么 numpy 的 exp 比 Matlab 慢?如何让它更快?
【发布时间】:2017-10-26 16:07:41
【问题描述】:

我有一个非常简单的例子,它表明 NumPy 的 np.exp 比 Matlab 慢大约 10 倍。如何加快 Python 速度?我正在运行 32 位 Python 2.7、NumPy 版本 1.11.3,而 numpy 正在使用 MKL blas & lapack 库。

而且,时间上的差异是如此之大,以至于我认为计时机制没有太大的影响。

Python 中的代码示例:

import numpy as np
import timeit

setup='import numpy as np; import numexpr as ne; n=100*1000; a = np.random.uniform(size=n)'
time = timeit.timeit('b=np.exp(a)', setup=setup, number=1000)
print 'Time for 1000 (np.exp): ',time
time = timeit.timeit('b=ne.evaluate("exp(a)")', setup=setup, number=1000)
print 'Time for 1000 (numexpr): ',time

结果:

Time for 1000 (np.exp):  2.25906916167
Time for 1000 (numexpr):  0.591470532849

在 Matlab 中:

a = rand([100*1000,1]);
times = [];
for i=1:1000,
    tic
    b = exp(a);
    t=toc;
    times(i) = t;
end

fprintf('Time for 1000: %f\n',sum(times));

导致:

Time for 1000: 0.268527

【问题讨论】:

  • timeit 文档说没有多行字符串文字。如果将其作为 timeit 运行的一部分进行评估,我猜测您在设置部分中传递的方式。您可以通过在 python 中模仿 matlab 中的 tic/tock 设置来进行直接的苹果对苹果的比较。
  • @SlaterTyranus:文档说stmtsetup 不能包含 多行字符串文字,并不是说它们本身不能跨越多行。我相信这是因为 reindent helper timeit 用于调整多行输入的缩进会破坏输入中的多行字符串文字。
  • 另外,至于如何让它更快的问题,这实际上与 numpy 无关,而与你在引擎盖下的 lapack 无关。如果你想改进它,那就是你必须关注的地方。
  • 是的,我将设置字符串更改为用分号分隔的单个字符串,相同的计时结果。
  • @user2357112 我的错。我过去遇到过一些微妙的问题,并且无法在心理上解析输入字符串。

标签: python matlab performance numpy


【解决方案1】:

为了提高性能,尤其是在大型数据集上,我们可以利用 numexpr module 来实现此类超越函数 -

import numexpr as ne

b = ne.evaluate('exp(a)')

基准测试

为了进行适当的基准测试,我会使用 timeit on MATLABNumPy's %timeit -

设置 #1

MATLAB:

>> a = rand([100*1000,1]);
>> func = @() exp(a);
>> timeit(func)
ans =
    0.0013 % That's 1.3 m-sec

在相同大小的数据集上使用 NumPy:

In [417]: n=100*1000
     ...: a = np.random.uniform(size=n)
     ...: 

In [418]: %timeit np.exp(a)
1000 loops, best of 3: 1.5 ms per loop

In [419]: %timeit ne.evaluate('exp(a)')
1000 loops, best of 3: 397 µs per loop

因此,

MATLAB  : 1.3 m-sec
NumPy   : 1.5 m-sec
Numexpr : 0.4 m-sec

设置 #2

MATLAB:

>> a = rand([1000*10000,1]);
>> func = @() exp(a);
>> timeit(func)
ans =
    0.0977  % That's 97 m-sec

NumPy:

In [412]: n=1000*10000
     ...: a = np.random.uniform(size=n)
     ...: 

In [413]: %timeit np.exp(a)
10 loops, best of 3: 154 ms per loop

In [414]: %timeit ne.evaluate('exp(a)')
10 loops, best of 3: 36.5 ms per loop

因此,

MATLAB  :  97 m-sec
NumPy   : 154 m-sec
Numexpr :  36 m-sec

使用tic-toc进行适当的基准测试

问题中的基准测试的错误在于,我们在一个循环中获取了 toc 已用时间,而该循环的运行时间不足以为我们提供任何准确的时间。普遍接受的想法是toc 经过的时间必须至少接近1 秒标记。

因此,通过这些更正,使用 tic-toc 进行更准确的计时测试将是 -

tic
for i=1:1000,
    b = exp(a);
end
t=toc;
timing = t./1000

这会产生 -

timing =
    0.0010

这与我们的1.3 m-sectimeit 很接近。

【讨论】:

  • 这里更大的问题是为什么你的运行速度比 OP 快一千倍
  • 我的时间结果是所有 exp(a) 运行的总时间,而不是 1 的平均时间。@Divakar - 我添加了 numexpr,这很好,可以帮助很多人,但仍然我们比 Matlab 慢 2 倍以上。
  • @SlaterTyranus 实际上 %timeit 为我们提供了 1000 次运行的平均值。 OP 正在累积它,这不是平均的。不过,不确定这如何成为更大的问题。我们应该专注于加速。
  • @Divakar ha,就可以了。下降到 3-4 倍的增量,这更合理的是硬件增量。
  • @IsaacAsher 使用您信任的 setup='import numpy as np; import num.. 代码,然后使用我在帖子末尾发布的 tic-toc 代码。三个版本的结果如何:MATLAB、NumPy、Numexpr?您能否将其添加到问题中或在 cmets 中告诉我?
猜你喜欢
  • 1970-01-01
  • 2017-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-16
  • 2020-11-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多