编辑:
目标:
通过从公共变量的功率计算中重新使用预先计算/缓存的功率,生成一种无处不在的方法,以获得优于内置pow(double, uint)
的自定义幂函数。
已经做了什么:
我已经推出了这样一个函数,它比内置函数大约快40%,但这是一个强力的手动派生函数 - 我想要一种自动生成这样的电源功能块的方法任意uint
权力。
的已知,
要获得最佳自定义pow(double, uint)
,您需要一些知识。对于这个问题,知识(澄清)是:
N_MAX
)。r2
,r4
和r6
)。r2
始终是一直计算的
其他预先计算的权力。解决方案要求
需要单独程序编写case
查找表或预处理程序逻辑以生成此类表的最佳解决方案是可接受的,但是,使用手动生成(即强力导出)查找表的非最佳解决方案手头的权力将不被接受(正如我已经拥有的那样,并且在我的例子中表明......想法是远离这个)。
可能的解决途径
作为建议,您知道N_MAX
以及一组预先计算的权限B
(我的示例为B={2,4,6}
)。您可以在单独的程序中或在预处理器中生成Sq(Bi, x
)< = N_MAX . You can use this to form a basis set
A , which you then search somehow to determine the least number of terms that can be summed to produce an arbitrary exponent of
n>> 1 , where
n&lt的所有方格的表格; = N_MAX`(转移是由于我们通过检查LSB并乘以sqrt(r2)来处理奇数情况)。
理论背景
我相信下面的方法是正方形的修改版本:方形:
http://en.wikipedia.org/wiki/Exponentiation_by_squaring
....利用了某些低阶幂已经必然预先计算的事实,因此它通过平方(我假设pow(double, int)
使用)从一个香草指数中移出最佳乘法集。
然而,通过使用存储的小功率中间体而不是简单的exp,可以节省大量成本。通过r2
上的方块。
理论绩效
例如,对于一组对象n=14
....在此方案exp中。由权力提供
double r4 = Sq(r2), r14=Sq(r4)*r4*r2; //4 op.
...需要 4 FP乘法 .....但是使用r2
和r6
我们有
double r14=Sq(r6)*r2; //2 op.
.... 2 FP乘法 ....换句话说,来自" dumb"用方块表示我修改过的exp。通过使用常见指数预处理的正方形,我在乘法方面减少了50%的计算成本...至少在考虑内存成本之前。
真实表现
使用我当前的方法(使用gcc -O3
编译),我得到 35.1秒。来运行我的程序100万个周期,而不是(没有其他修改) 56.6 s 使用内置的int pow(double, int)
....几乎是理论上的加速。
此时,您可能会想到如何在单条指令行上减少50%的乘法可以提供约40%的加速。但基本上这行代码每个周期被称为1,000+次,是迄今为止整个程序中评估最多/最昂贵的代码行。因此,该程序似乎对此块中的小优化/改进非常敏感。
原始发布和示例代码
我需要替换pow(double, int)
函数,因为我已经计算了第6个幂项,并且保存了第2个,第4个幂中间体,所有这些都可以用来减少第二个pow
调用中的乘法,使用相同的double
基础。
更具体地说,在我的c ++代码中,我有一个性能关键的计算代码片段,其中我将3D点之间的距离的倒数提高到6次幂和n次幂。 e.g:
double distSq = CalcDist(p1,p2), r2 = a/distSq, r6 = r2 * r2 * r2;
results += m*(pow(sqrt(r2), n) - r6);
m
和a
是与拟合方程相关的常数,n
是任意幂。
稍微高效的形式是:
double distSq = CalcDist(p1,p2), r2 = a/distSq, r6 = r2 * r2 * r2;
results += m*(pow(r2, n)*(n&0x1?sqrt(r2):1.0) - r6);
但是,这也不是最佳选择。我发现明显更快的是使用自定义pow
函数,该函数使用r2,r4和r6的倍数,我必须在第二个任期内计算它。
e.g:
double distSq = CalcDist(p1,p2), r2 = a/distSq, r4 = r2 * r2, r6 = r4 * r2;
results += m*(POW(r2, r4, r6 n) - r6);
功能内部:
double POW(double r2, double r4, double r6, uint n)
{
double results = (n&0x1 : sqrt(r2) : 1.0);
n >>= 1;
switch (n)
{
case 1:
....
case 12:
Sq(Sq(r6));
}
return result;
}
好处是我的功能在初步测试中显得很快。坏消息是,它不是无处不在,而且很长,因为我需要从case
到int
左右的8
次幂50
语句(可能甚至是未来更高)。进一步每个案例我都要检查并尝试不同的组合来通过强力推导找到r2
,r4
和r6
的组合产生最少的乘法
有没有人对pow(double, int)
替代品有一个更普遍的解决方案,它使用基数的预先计算的功率来减少必要的乘法次数,和/或有一个无处不在的理论来说明如何确定理想的组合产生任意n
和一组预先计算的倍数的最小乘法??
答案 0 :(得分:1)
这是一个类似DP的算法,它可以为给定的n
和可用的幂x^i
提供最小数量的乘法,以及通过回溯的最佳策略。对于每个可能的指数n
,将一对(minimum number of multiplications to get here, type of multiplication that gets you there)
关联到第二个数字的位置,只需编写i
或特殊符号S
进行平方。
你显然是从1 -> (0, /)
开始。
鉴于n -> (m_n, Action_m)
,如果n+i ->
小于可能先前计算的最小移动次数(m_n + 1, i)
,则将m_n + 1
设置为n+i
。同样,如果这比先前可能的解决方案更好,则设置2n -> (m_n + 1, S)
。
此算法大致为O(n_max * #available powers)
提供最佳策略。我并不认为算法本身具有最佳效率,但使用“动态”确实没有任何意义。它只有在你有一个合理的n_max
(100,在你的情况下,当然没问题)和存储策略的有效方法时才有用。
要考虑两个想法:
(1)在进行基准测试之前,我不相信它会通过平方显着提高标准exp的性能(当然,严重依赖于可用的功率)。
(2)此类策略的数值误差行为(以及平方展示)与pow(double, double)
完全不同。