我需要Math.exp()
经常从 java 计算,是否有可能让本机版本比javaMath.exp()
运行得更快?
我只尝试了 jni + C,但它比普通的java慢。
我需要Math.exp()
经常从 java 计算,是否有可能让本机版本比javaMath.exp()
运行得更快?
我只尝试了 jni + C,但它比普通的java慢。
这已被多次请求(参见例如此处)。这是从这篇博文复制而来的 Math.exp() 的近似值:
public static double exp(double val) {
final long tmp = (long) (1512775 * val + (1072693248 - 60801));
return Double.longBitsToDouble(tmp << 32);
}
它与具有 2048 个条目和条目之间的线性插值的查找表基本相同,但所有这些都使用 IEEE 浮点技巧。它比我机器上的 Math.exp() 快 5 倍,但如果使用 -server 编译,这可能会有很大差异。
+1 编写自己的 exp() 实现。也就是说,如果这确实是您的应用程序中的瓶颈。如果您可以处理一些不准确的问题,那么有许多非常有效的指数估计算法,其中一些可以追溯到几个世纪以前。据我了解,Java 的 exp() 实现相当慢,即使对于必须返回“精确”结果的算法也是如此。
哦,不要害怕用纯 Java 编写 exp() 实现。JNI 有很多开销,而且 JVM 能够在运行时优化字节码,有时甚至超出了 C/C++ 能够实现的程度。
使用 Java 的。
此外,缓存 exp 的结果,然后您可以比再次计算更快地查找答案。
您还想Math.exp()
在 C 中包装任何循环调用。否则,Java 和 C 之间的编组开销将压倒任何性能优势。
如果您分批执行它们,您也许可以让它运行得更快。进行 JNI 调用会增加开销,因此您不想为需要计算的每个 exp() 都这样做。我会尝试传递一个包含 100 个值的数组并获取结果以查看它是否有助于提高性能。
真正的问题是,这是否已成为您的瓶颈?您是否分析过您的应用程序并发现这是导致速度变慢的主要原因?
如果没有,我建议使用 Java 的版本。尽量不要预先优化,因为这只会导致开发速度变慢。您可能会在一个可能不是问题的问题上花费大量时间。
话虽这么说,我认为你的测试给了你答案。如果 jni + C 速度较慢,请使用 java 的版本。
Commons Math3附带一个优化版本:FastMath.exp(double x)
. 它确实显着加快了我的代码速度。
Fabien进行了一些测试,发现它的速度几乎是以下速度的两倍Math.exp()
:
0.75s for Math.exp sum=1.7182816693332244E7
0.40s for FastMath.exp sum=1.7182816693332244E7
这是javadoc:
计算 exp(x),函数结果接近四舍五入。对于 99.9% 的输入值,它将正确四舍五入为理论值,否则会出现 1 个 UPL 错误。
方法:
Lookup intVal = exp(int(x))
Lookup fracVal = exp(int(x-int(x) / 1024.0) * 1024.0 );
Compute z as the exponential of the remaining bits by a polynomial minus one
exp(x) = intVal * fracVal * (1 + z)
准确度:计算以 63 位精度完成,因此结果应正确舍入 99.9% 的输入值,否则 ULP 误差小于 1。
由于 Java 代码将使用即时 (JIT) 编译器编译为本机代码,因此没有理由使用 JNI 调用本机代码。
此外,您不应缓存输入参数为浮点实数的方法的结果。在时间上获得的收益将在使用的空间量上损失很多。
使用 JNI 的问题是调用 JNI 所涉及的开销。Java 虚拟机现在已经非常优化,对内置 Math.exp() 的调用会自动优化为直接调用 C exp() 函数,甚至可以优化为直接的 x87 浮点程序集指示。
使用 JNI 会产生一些开销,另请参阅:http: //java.sun.com/docs/books/performance/1st_edition/html/JPNativeCode.fm.html
因此,正如其他人建议的那样,尝试整理涉及使用 JNI 的操作。
编写您自己的,根据您的需求量身定制。
例如,如果所有指数都是 2 的幂,则可以使用位移。如果您使用有限范围或一组值,则可以使用查找表。如果您不需要精确的精度,您可以使用不精确但速度更快的算法。
跨 JNI 边界调用会产生成本。
如果您也可以将调用 exp() 的循环移动到本机代码中,以便只有一个本机调用,那么您可能会得到更好的结果,但我怀疑它会比纯 Java 解决方案快得多。
我不知道您的应用程序的详细信息,但如果您有一组相当有限的调用可能参数,您可以使用预先计算的查找表来使您的 Java 代码更快。
exp 有更快的算法,具体取决于您要完成的工作。问题空间是否限制在一定范围内,是否只需要一定的分辨率、精度或准确度等。
如果你很好地定义了你的问题,你可能会发现你可以使用一个带有插值的表格,例如,这将使几乎任何其他算法都被淘汰。
您可以对 exp 应用哪些约束来获得性能权衡?
-亚当
我运行了一个拟合算法,拟合结果的最小误差远大于 Math.exp() 的精度。
超越函数总是比加法或乘法慢得多,这是众所周知的瓶颈。如果您知道您的值在一个狭窄的范围内,您可以简单地构建一个查找表(两个排序数组;一个输入,一个输出)。使用 Arrays.binarySearch 查找正确的索引并使用 [index] 和 [index+1] 处的元素插入值。
另一种方法是拆分号码。让我们取例如 3.81 并将其拆分为 3+0.81。现在将 e = 2.718 乘以 3 次,得到 20.08。
现在到 0.81。0 到 1 之间的所有值都与众所周知的指数级数快速收敛
1+x+x^2/2+x^3/6+x^4/24....等
尽可能多地使用精确度;不幸的是,如果 x 接近 1,它会变慢。假设你去 x^4,那么你得到 2.2445 而不是正确的 2.2448
然后将结果 2.781^3 = 20.08 与 2.781^0.81 = 2.2445 相乘,得到结果 45.07,误差为两千分之一(正确:45.15)。