Python 在numpy中,为什么log2和log1p比log和log10快得多?

Python 在numpy中,为什么log2和log1p比log和log10快得多?,python,performance,numpy,glibc,logarithm,Python,Performance,Numpy,Glibc,Logarithm,在玩游戏时,我注意到一些我无法解释的关于,和的相对性能的事情: np.log2比np.log和np.log10快约3倍。也许更直观地计算LN(x+1),与NP.Log2:是一致的。 In [4]: %%timeit x = np.random.rand(100000) np.log1p(x) ....: 1000 loops, best of 3: 1.46 ms per loop 我在numpy v1.10.1和v1.8.2中获得了几乎相同的计时 运行时性能的这些差异是否有直观的解释

在玩游戏时,我注意到一些我无法解释的关于,和的相对性能的事情:

np.log2
np.log
np.log10
快约3倍。也许更直观地计算LN(x+1),与NP.Log2:

是一致的。
In [4]: %%timeit x = np.random.rand(100000)
np.log1p(x)
   ....: 
1000 loops, best of 3: 1.46 ms per loop
我在numpy v1.10.1和v1.8.2中获得了几乎相同的计时


运行时性能的这些差异是否有直观的解释?

这只是一个注释,但比注释要长。显然,这与您的特定安装有关:

import numpy as np
import numexpr as ne
x = np.random.rand(100000)
我从conda和icc编译的版本中获得了与numpy 1.10相同的计时:

%timeit np.log2(x)
1000 loops, best of 3: 1.24 ms per loop

%timeit np.log(x)
1000 loops, best of 3: 1.28 ms per loop
我认为这可能与获取MKL VML包有关,但看起来这是一个否定:

%timeit ne.evaluate('log(x)')
1000 loops, best of 3: 218 µs per loop

看起来你的numpy安装从两个不同的地方获取了它的log/log2实现,这很奇怪。

我发现你的问题非常有趣,所以我花了几个小时做了一些研究;我想我已经找到了性能差异的解释,因为它适用于整数(感谢您的注释)-不清楚如何将该推理扩展到浮点:

计算机使用基数2-根据参考文献中链接的文章,log2的计算是一个4个处理器周期的过程-log10的计算需要将log2(val)乘以1/log2(10),再加上5个周期

查找日志2是一个问题。(视频大约在23分钟后)

比特黑客:

比特黑客:

首先使用以下公式之一计算整数对数基数10: 查找日志库的上述技术2。根据关系 log10(v)=log2(v)/log2(10),我们需要将其乘以1/log2(10), 约为1233/4096,或1233,然后右移 12需要添加一个,因为IntegerLogBase2会向下舍入。最后,由于t值只是一个近似值,可能会被 其一,通过减去v的结果来获得精确值< 功率为10[t]

此方法比IntegerLogBase2多执行6次操作。可能是 通过修改日志加快了速度(在具有快速内存访问的机器上) 上面的base 2表格查找方法,以便条目保存 计算t(即预加、-mulitply和-shift)。这样做 总共只需9次操作即可找到以10为基数的日志, 假设使用了4个表(v的每个字节一个表)

值得注意的是:使用Debrijn序列查找和位移位技术来计算log2(36分钟后的视频)

值得注意的是,这篇文章展示了

警告:我没有检查numpy源代码来验证它是否确实实现了类似的技术,但令人惊讶的是它没有。事实上,从OP的帖子下的评论中,我们已经检查了:

实际上,numpy使用glibc中的math.h,您将看到相同的差异 在C/C++中,如果使用math.h/cmath.h。你可以找到评论很好的 这两个函数的源代码,例如。 和 – [9]


免责声明:我既不是可靠的消息来源,也不是官方消息来源

我几乎可以肯定,log到base e函数的任何实现都可以像log2函数一样快,因为要将一个函数转换为另一个函数,需要用一个常数进行一次除法。这当然是假设一次除法运算只占其他计算的一小部分;这在对数的精确实现中是正确的

在大多数情况下,numpy使用
glibc
中的
math.h
,如果使用
math.h
/
cmath.h
,您将在C/C++中看到相同的差异。在评论中,一些人观察到
np.log
np.log2
的速度相同;我怀疑这可能来自不同的构建/平台

您可以在的
dbl-64/
flt-32/
子目录中的文件
e_log2.c
e_logf.c
e_log2f.c
中找到这两个函数的注释良好的源代码

为了实现双精度,在
glibc
中,
log
函数从2001年开始实施了与IBM完全不同的算法(与
log2
),该算法包含在其
libultim
库中。而
log2
是1993年由Sun Microsystems提供的。只要看一下代码,就可以看到正在实现两种不同的近似。相比之下,对于单精度,除了在
log2
情况下除以
ln2
之外,
log
log2
两个函数都相同,因此速度相同

有关未来要包含在
glibc
中的基础算法、备选方案和讨论的更多背景信息,请参阅。

(可能是一个注释,但太长了…)

为了让这一点更有趣,2018年在Windows10 64位机器上,结果是相反的

默认蟒蛇

Python 3.6.3 |Anaconda, Inc.| (default, Oct 15 2017, 03:27:45) [MSC v.1900 64 bit (AMD64)]
Type 'copyright', 'credits' or 'license' for more information
IPython 6.1.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import numpy as np; np.random.seed(0); x = np.random.rand(100000)
   ...: %timeit np.log2(x)
   ...: %timeit np.log1p(x)
   ...: %timeit np.log(x)
   ...: %timeit np.log10(x)
   ...:
1.48 ms ± 18 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
1.33 ms ± 36.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
840 µs ± 7.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
894 µs ± 2.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Python 3.6.3 |Intel Corporation| (default, Oct 17 2017, 23:26:12) [MSC v.1900 64 bit (AMD64)]
Type 'copyright', 'credits' or 'license' for more information
IPython 6.1.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import numpy as np; np.random.seed(0); x = np.random.rand(100000)
   ...: %timeit np.log2(x)
   ...: %timeit np.log1p(x)
   ...: %timeit np.log(x)
   ...: %timeit np.log10(x)
   ...:
1.01 ms ± 2.57 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
236 µs ± 6.08 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
161 µs ± 1.77 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
171 µs ± 1.34 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

在数学中,SE似乎认为某些方法在计算任何日志时会减少
log2
。这可能意味着np的log函数的实现以某种方式依赖于log2和/或ln(x+1)。我认为这也和它们的泰勒级数有关,这是一个非常有趣的观察结果。我决不是一个高效计算例程的低级实现专家。直觉上,我猜这与所有对数在概念上都是相关的这一事实有关。如果你知道一个,你基本上通过简单的变换就能知道它们。因此,在某个时刻,您必须决定哪一个可以在处理器上高效计算。然后通过转换计算其他值显然需要更多的时间。但我很想在这里看到一个专家的答案。也许是因为宾
Python 3.6.3 |Intel Corporation| (default, Oct 17 2017, 23:26:12) [MSC v.1900 64 bit (AMD64)]
Type 'copyright', 'credits' or 'license' for more information
IPython 6.1.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import numpy as np; np.random.seed(0); x = np.random.rand(100000)
   ...: %timeit np.log2(x)
   ...: %timeit np.log1p(x)
   ...: %timeit np.log(x)
   ...: %timeit np.log10(x)
   ...:
1.01 ms ± 2.57 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
236 µs ± 6.08 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
161 µs ± 1.77 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
171 µs ± 1.34 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)