Optimization 如何加速近似自然对数函数?
我实现了一个基于截断泰勒级数的Padé近似的近似自然对数函数。精度可以接受(±0.000025),但尽管进行了几轮优化,其执行时间仍然是标准库Optimization 如何加速近似自然对数函数?,optimization,rust,natural-logarithm,Optimization,Rust,Natural Logarithm,我实现了一个基于截断泰勒级数的Padé近似的近似自然对数函数。精度可以接受(±0.000025),但尽管进行了几轮优化,其执行时间仍然是标准库ln函数的2.5倍左右!如果它不是更快,也不是那么准确,那它就一文不值了!尽管如此,我还是用它来学习如何优化我的Rust代码。(我的计时来自于使用标准板条箱。我使用blackbox,对循环中的值求和,并根据结果创建一个字符串以击败优化器。) 在铁锈操场上,我的代码是: 算法 我的算法概述,该算法处理无符号整数的比率: 通过除以不超过以下值的两个最大幂,将
ln
函数的2.5倍左右!如果它不是更快,也不是那么准确,那它就一文不值了!尽管如此,我还是用它来学习如何优化我的Rust代码。(我的计时来自于使用标准
板条箱。我使用blackbox,对循环中的值求和,并根据结果创建一个字符串以击败优化器。)
在铁锈操场上,我的代码是:
算法
我的算法概述,该算法处理无符号整数的比率:
- 分子的变化表示→ <代码>2ⁿ·N其中1≤ N≤ 2
- 改变分母的表示→ <代码>2ᵈ·D其中1≤ D≤ 2
log(分子/分母)=log(2ⁿ·N/2ᵈ·D) =(n-D)·日志(2)+日志(n)-日志(D)
y=x/(2+x)
f(y)=Log((1+y)/(1-y))
=Log((1+x/(2+x))/(1-x/(2+x)))
=Log((2+2x)/2)
=Log(1+x)
- 对于对数(1+x)→ <代码>x-x²/2+x³/3-y⁴/4+…
- 对于对数((1+y)/(1-y))→ <代码>y+y³/3+y⁵/5+…
y+y³/3+y使用Padé近似⁵/5…
2y·(15-4y²)/(15-9y²)
///近似一加一个范围(0..1)内的数字的自然对数。
///
///对对数((1+y)/(1-y))的截断泰勒级数使用Padé近似。
///
///-x-必须是介于0和1之间的值(包括0和1)。
#[内联]
fn log_1_plus_x(x:f64)->f64{
//这是私有的,它的调用者已经检查了底片,所以不需要在这里再次检查。
//此外,尽管ln(1+0)==0是一个简单的情况,但它不太可能是参数
//与其他值不同,因此无需进行特殊测试。
设y=x/(2.0+x);
设y_平方=y*y;
//原来的公式是:2y·(15-4y²)/(15-9y²)
//2.0*y*(15.0-4.0*y_平方)/(15.0-9.0*y_平方)
//减少乘法:(8/9)y·(3.75-y²)/(5/3-y²)
0.888888888989*y*(3.75-y_平方)/(1.6666666667-y_平方)
}
显然,没有更多的加速
最高有效位
到目前为止影响最大的变化是优化我的计算,以获得最高有效位的位置。我需要它来缩小射程
以下是我的msb
函数:
///为数值类型提供“msb”方法以获取基于零的
///最高有效位集的位置。
///
///基于本文使用的算法:
/// https://prismoskills.appspot.com/lessons/Bitwise_Operators/Find_position_of_MSB.jsp
最重要的一点{
///获取整数类型的最高有效位的从零开始的位置。
///如果数字为零,则返回零。
///
///##示例:
///
/// ```
///使用ClusterFebia::clustering::msb::MostSignificanBit;
///
///断言!(0_u64.msb()==0);
///断言!(1_64.msb()==0);
///断言!(2_64.msb()==1);
///断言!(3_64.msb()==1);
///断言!(4_64.msb()==2);
///断言!(255_u64.msb()==7);
///断言!(1023_u64.msb()==9);
/// ```
fn msb(自我)->使用;
}
#[内联]
///返回是否为楼层(log2(x))=楼层(log2(y))
///零表示假,1表示真,因为这来自C!
fn ld_neq(x:u64,y:u64)->u64{
设neq=(x^y)>(x&y);
如果neq{1}否则{0}
}
u64的impl MOSTSIMGNIFICANTBIT{
#[内联]
fn msb(自身)->使用{
/*
//我替换的较慢代码:
//二分法保证了O(logb)的性能,其中B是整数中的位数。
让mut high=63_usize;
让mut low=0_usize;
而(高-低)>1
{
设mid=(高+低)/2;
让mask_high=(1一次优化将时间缩短一半
我重写了我的msb(最高有效位)函数,以使用库函数u64::leading_zeroes
,该函数在内部使用内部函数:
fn msb(self)->usize{
//第三次尝试
设z=self.leading_zero();
如果z==64{0}
else{63-z as usize}
}
现在我的对数近似只比内在ln函数长6%,我不太可能做得更好
经验教训:使用内置日志!一次优化可将时间缩短一半
我重写了我的msb(最高有效位)函数,以使用库函数u64::leading_zeroes
,该函数在内部使用内部函数:
fn msb(self)->usize{
//第三次尝试
设z=self.leading_zero();
如果z==64{0}
else{63-z as usize}
}
现在我的对数近似只比内在ln函数长6%,我不太可能做得更好
乐