更快地估计对数运算
Faster estimation of logarithm operation
我有一个相当简单的函数,涉及以 10 为底的对数(f1
如下所示)。我需要它尽快 运行 因为它作为较大代码的一部分被调用了数百万次。
我尝试使用 Taylor approximation(下面的 f2
),但即使扩展很大,准确性也很差,更糟,它结束了花费更多时间。
我是否达到了 numpy
的性能极限?
import time
import numpy as np
def f1(m1, m2):
return m1 - 2.5 * np.log10(1. + 10 ** (-.4 * (m2 - m1)))
def f2(m1, m2):
"""
Taylor expansion of 'f1'.
"""
x = -.4 * (m2 - m1)
return m1 - 2.5 * (
0.30102999 + .5 * x + 0.2878231366 * x ** 2 -
0.0635837 * x ** 4 + 0.0224742887 * x ** 6 -
0.00904311879 * x ** 8 + 0.00388579 * x ** 10)
# The data I actually use has more or less this range.
N = 1000
m1 = np.random.uniform(5., 30., N)
m2 = np.random.uniform(.7 * m1, m1)
# Test both functions
M = 5000
s = time.clock()
for _ in range(M):
mc1 = f1(m1, m2)
t1 = time.clock() - s
s = time.clock()
for _ in range(M):
mc2 = f2(m1, m2)
t2 = time.clock() - s
print(t1, t2, np.allclose(mc1, mc2, 0.01))
用乘法替换f2
中的所有幂:
def f2(m1, m2):
"""
Taylor expansion of 'f1'.
"""
x = -0.4 * (m2 - m1)
x2 = x * x
x4 = x2 * x2
x6 = x4 * x2
return m1 - 2.5 * (
0.30102999 + .5 * x + 0.2878231366 * x2 -
0.0635837 * x4 + 0.0224742887 * x6 -
0.00904311879 * x4 * x4 + 0.00388579 * x4 * x6)
有了这个代码片段,我不确定你是否应该优化日志,但更多的是整个向量表达式本身。
您可以尝试 numexpr(Python、NumPy 等的快速数值数组表达式求值器),它可能对您有很大帮助。
尝试这个的想法来自 Ignacio 的评论,这让我想到他的加速来自哪里(我敢肯定,它不是来自对数计算本身)。
我对你的代码进行了简单的修改:
import numexpr as ne
def f1(m1, m2):
return ne.evaluate("m1 - 2.5 * log10( 1.0 + 10 ** (-0.4 * (m2-m1)))")
上面的结果似乎是(未优化的)f2(近似值) 的 5 - 6 倍,同时仍提供原始精度。
它速度几乎是原始 numpy 方法 f1 的两倍。
这些数字可能会根据 numexpr 的设置而改变,例如也可以使用 Intels MKL。由于我懒得检查我基于 anaconda 的设置,所以我将其作为技术演示提供,每个人都可以尝试。
虽然我过去曾几次使用 numexpr 来处理简单的事情,但我可能会补充说,它也在 pandas 中使用,只是提一下取决于它的正确工作原理的真实世界项目。
免责声明:我使用您的基准作为模板(并希望缓存和 co 不会发挥作用)。
我有一个相当简单的函数,涉及以 10 为底的对数(f1
如下所示)。我需要它尽快 运行 因为它作为较大代码的一部分被调用了数百万次。
我尝试使用 Taylor approximation(下面的 f2
),但即使扩展很大,准确性也很差,更糟,它结束了花费更多时间。
我是否达到了 numpy
的性能极限?
import time
import numpy as np
def f1(m1, m2):
return m1 - 2.5 * np.log10(1. + 10 ** (-.4 * (m2 - m1)))
def f2(m1, m2):
"""
Taylor expansion of 'f1'.
"""
x = -.4 * (m2 - m1)
return m1 - 2.5 * (
0.30102999 + .5 * x + 0.2878231366 * x ** 2 -
0.0635837 * x ** 4 + 0.0224742887 * x ** 6 -
0.00904311879 * x ** 8 + 0.00388579 * x ** 10)
# The data I actually use has more or less this range.
N = 1000
m1 = np.random.uniform(5., 30., N)
m2 = np.random.uniform(.7 * m1, m1)
# Test both functions
M = 5000
s = time.clock()
for _ in range(M):
mc1 = f1(m1, m2)
t1 = time.clock() - s
s = time.clock()
for _ in range(M):
mc2 = f2(m1, m2)
t2 = time.clock() - s
print(t1, t2, np.allclose(mc1, mc2, 0.01))
用乘法替换f2
中的所有幂:
def f2(m1, m2):
"""
Taylor expansion of 'f1'.
"""
x = -0.4 * (m2 - m1)
x2 = x * x
x4 = x2 * x2
x6 = x4 * x2
return m1 - 2.5 * (
0.30102999 + .5 * x + 0.2878231366 * x2 -
0.0635837 * x4 + 0.0224742887 * x6 -
0.00904311879 * x4 * x4 + 0.00388579 * x4 * x6)
有了这个代码片段,我不确定你是否应该优化日志,但更多的是整个向量表达式本身。
您可以尝试 numexpr(Python、NumPy 等的快速数值数组表达式求值器),它可能对您有很大帮助。
尝试这个的想法来自 Ignacio 的评论,这让我想到他的加速来自哪里(我敢肯定,它不是来自对数计算本身)。
我对你的代码进行了简单的修改:
import numexpr as ne
def f1(m1, m2):
return ne.evaluate("m1 - 2.5 * log10( 1.0 + 10 ** (-0.4 * (m2-m1)))")
上面的结果似乎是(未优化的)f2(近似值) 的 5 - 6 倍,同时仍提供原始精度。
它速度几乎是原始 numpy 方法 f1 的两倍。
这些数字可能会根据 numexpr 的设置而改变,例如也可以使用 Intels MKL。由于我懒得检查我基于 anaconda 的设置,所以我将其作为技术演示提供,每个人都可以尝试。
虽然我过去曾几次使用 numexpr 来处理简单的事情,但我可能会补充说,它也在 pandas 中使用,只是提一下取决于它的正确工作原理的真实世界项目。
免责声明:我使用您的基准作为模板(并希望缓存和 co 不会发挥作用)。