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),与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)