np.exp比np.e慢得多吗?

8
In [49]: timeit.timeit("np.exp(100)", setup="import numpy as np")
Out[49]: 1.700455904006958

In [50]: timeit.timeit("np.e**100", setup="import numpy as np")
Out[50]: 0.16629505157470703

为什么使用CPython实现的np.e**100速度比使用numpy版本慢那么多?由于numpy被推到C代码中,numpy版本不应该更快吗?


1
我相信前者正在使用技巧来近似解析指数函数(可能还考虑了复平面)。后者可能正在使用浮点近似,这可以理解为更快的方法。 - Asad Saeeduddin
我想感谢您发布的这篇文章!我从未想过测试np.e**而不是np.exp,这使我的计算时间大大缩短了! - Alain
难道不应该numpy版本更快吗?因为它被转化为C代码了。无论如何,指数计算都是在C代码中完成的。当你让NumPy一次操作整个数组时,它可以将循环转化为C代码,但你给它的是一个标量值。 - user2357112
3个回答

8

一个显而易见的原因是np.exp被设置为处理数组,这可能涉及到在确定输入的类型/维度方面存在一些开销。尝试使用这样的情况,您可能会看到差异减少或消失:

timeit.timeit("np.exp(x)", 
              setup="import numpy as np; x = np.array([99, 100, 101])")
# This actually seems to be faster than just calculating
#   it for a single value
Out[7]: 1.0747020244598389

timeit.timeit("[np.e**n for n in x]", 
              setup="import numpy as np; x = [99, 100, 101]")
Out[8]: 0.7991611957550049

2
我想指出,当你给它数组时,np.expnp.e更快。我在numpy版本1.22和Python 3.9上进行了测试。
你得到的确切加速可能取决于你的架构。我在一台现代的32线程AMD CPU上进行了测试。
对于实值数据,np.exp大约比np.e快5倍。对于复值数据,这个因子接近2。
实值数组:
>>> timeit.timeit("np.e**x", setup="import numpy as np; x = np.random.random(size=(10,10))")
2.7911060999904294
>>> timeit.timeit("np.exp(x)",setup="import numpy as np; x = np.random.random(size=(10,10))")
0.606489199999487

复数数组:
>>> timeit.timeit("np.e**x", setup="import numpy as np; x = 1j*np.random.random(size=(10,10))")
7.959791200002655
>>> timeit.timeit("np.exp(x)", setup="import numpy as np; x = 1j*np.random.random(size=(10,10))")
3.637887800025055

0

可能是函数调用开销等问题。即使是np.exp(0)也同样慢,而实际上它不应该计算任何东西。

>>> timeit.timeit("np.exp(100)", setup="import numpy as np")
2.8452274883430846
>>> timeit.timeit("np.exp(0)", setup="import numpy as np")
2.836107299807054

6
дҪ и®Өдёәдёәд»Җд№Ҳи®Ўз®—exp(0)жҜ”и®Ўз®—exp(100)жӣҙеҝ«пјҹйҷӨйқһжңүдё“й—ЁжЈҖжҹҘиҫ“е…Ҙдёә0зҡ„д»Јз ҒпјҢеҗҰеҲҷжҜҸдёӘи°ғз”ЁйғҪдјҡеҸҳж…ўгҖӮ - Mark Ransom
1
@MarkRansom 嗯,我承认我不知道它是如何实现的,而且我现在在numpy或Python中找不到它,我只在C中找到了这个,看起来像是这样的特殊处理。 - Stefan Pochmann
@MarkRansom 虽然我想的不是特殊处理,而是自然的早停,就像幂级数的和在初始值1之后立即为0,因此计算循环立即停止。再次声明,我不知道它实际上是如何计算的,如果我的理解有误,指数的值对计算速度没有影响,请指教:-)。特别是,如果您可以指向实际实现或告诉我如何计算,我将不胜感激。 - Stefan Pochmann
@MarkRansom 好的,我在其他地方找到了实际的计算方法,其中没有循环,而是一个固定的t*(P1+t*(P2+t*(P3+t*(P4+t*P5))))。但即使在那里,我也看到了一种特殊情况的处理方式,我认为这将防止指数为零时的计算。 - Stefan Pochmann

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接