在我正在编写的C ++ CPU绑定模拟中,我通过程序中的valgrind跟踪了一个瓶颈cmath::exp
。它目前占我模拟时间的40%以上。我可以将输入绑定到一个相对较小的域,但我想控制准确性。我正在考虑转移到LUT(查找表)来替换exp
,但我不太确定如何以“正确的方式”(tm)执行此操作。我有些担忧:
为exp
实施/(从库中包含)LUT的最佳方法是什么?
答案 0 :(得分:1)
最佳查找表大小取决于您在性能,准确性和实现复杂性之间的权衡。你将不得不描述,我们无法告诉你答案(我们不知道答案)。
使用lrint
中的<math.h>
将double
转换为long int
。我不确定它是否在<cmath>
。
我不确定将浮点数转换为整数时有什么斜率。你能详细说明你担心的事情吗?
是的,你正在重新发明轮子。你曾经做过的事情已经被任何曾经实施过数学图书馆的人一遍又一遍地完成。有很多关于这个主题的文献。
直观查找表远非最佳。您将需要使用某种多项式近似,也许是从查找表中选择系数的分段多项式近似。对于像exp
一样平滑且可预测的函数,多项式将为相同数量的计算工作提供更高的精度。所需的多项式将取决于复杂性和准确性之间的权衡,以及是否要最小化预期误差,最小化最大误差或使用其他一些损失函数。
限制exp
的域名实际上并没有多大帮助,因为它很容易扩展到整个域名。
// only works for x in [0, 1]
double exp2_limited(double x);
// works for all x, but doesn't handle overflow properly
double exp2(double x)
{
return scalbn(exp2_limited(fmod(x, 1.0)), (long) floor(x));
}
<强>要点:强>
在设计此类功能之前,您必须知道所需的准确度。
您还必须知道损失函数(即选择损失函数)。
在知道速度有多快之前,你必须先了解一下。
使用多项式。
答案 1 :(得分:1)
我遇到了这个问题,我拿了一些堆栈样本来诊断它。
这样做是告诉调用来自何处以及参数值是什么。
我发现当从特定位置调用exp
时,参数值是高度可重复的。
这表明了一种记忆方法,它产生了巨大的差异。
它需要一个简单的“包装”功能:
double exp_cached(double arg, double* old_arg, double* old_result){
if (arg== *old_arg) return *old_result;
*old_arg = arg;
*old_result = exp(arg);
return *old_result;
}
以及以前在exp(foo)
调用的地方,执行:
static double old_arg = -999999999, old_result;
...
... exp_cached(foo, &old_arg, &old_result)...
这样,exp
如果在调用它的地方的参数与之前的参数值相同,则不会被调用。
答案 2 :(得分:0)
以前曾提出过一个非常类似的问题。这是我的答案:
该问题的作者提出了这种方法,并且我能够有效地实现它(小查找表和查找后的最小额外工作)。它在C#中,但是对C ++的翻译应该是直截了当的,如果你遇到麻烦我可以提供帮助。