更快的Math.exp()通过JNI?

时间:2008-09-15 20:06:16

标签: java c optimization java-native-interface

我需要非常频繁地从java计算Math.exp(),是否可以让本机版本比 java Math.exp()更快地运行?

我尝试了jni + C,但它比普通的 java 慢。

15 个答案:

答案 0 :(得分:15)

这已经被多次请求(参见例如here)。以下是从this blog posting复制的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 :(得分:12)

+1编写自己的exp()实现。也就是说,如果这是真的你应用​​程序中的瓶颈。如果你可以处理一些不准确的问题,那里有许多非常有效的指数估计算法,其中一些可追溯到几个世纪。据我了解,Java的exp()实现相当慢,即使对于必须返回“精确”结果的算法也是如此。

哦,不要害怕在纯Java中编写exp()实现。 JNI有很多开销,JVM能够在运行时优化字节码,有时甚至超出C / C ++能够实现的范围。

答案 2 :(得分:6)

使用Java。

此外,缓存exp的结果然后你可以比再次计算它们更快地查找答案。

答案 3 :(得分:5)

你想要在C中包装任何循环调用Math.exp()。否则,Java和C之间的编组开销将超过任何性能优势。

答案 4 :(得分:3)

如果你批量生产它们,你或许可以让它运行得更快。进行JNI调用会增加开销,因此您不希望为需要计算的每个exp()执行此操作。我尝试传递一个包含100个值的数组并获得结果,看它是否有助于提高性能。

答案 5 :(得分:2)

真正的问题是,这会成为你的瓶颈吗?您是否对您的应用程序进行了分析并发现这是导致速度减慢的主要原因?
如果没有,我建议您使用Java的版本。尽量不要预先优化,因为这只会导致开发速度变慢。您可能会花费更长的时间来处理可能不是问题的问题
话虽如此,我认为你的测试给了你答案。如果jni + C较慢,请使用java的版本。

答案 6 :(得分:1)

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%的输入值,否则小于1 ULP错误。

答案 7 :(得分:0)

由于Java代码将使用实时(JIT)编译器编译为本机代码,因此没有理由使用JNI来调用本机代码。

此外,您不应该缓存输入参数是浮点实数的方法的结果。及时获得的收益将因使用的空间量而大大减少。

答案 8 :(得分:0)

使用JNI的问题是调用JNI所涉及的开销。 Java虚拟机目前已经过优化,对内置Math.exp()的调用会自动优化,直接调用C exp()函数,甚至可以优化为直接的x87浮点组件指令。

答案 9 :(得分:0)

使用JNI只会产生一些开销,另请参阅: http://java.sun.com/docs/books/performance/1st_edition/html/JPNativeCode.fm.html

正如其他人建议尝试整理涉及使用JNI的操作。

答案 10 :(得分:0)

根据您的需求自行编写。

例如,如果所有指数都是2的幂,则可以使用位移。如果使用有限范围或一组值,则可以使用查找表。如果您不需要精确定位,则使用不精确但速度更快的算法。

答案 11 :(得分:0)

跨JNI边界调用会产生相关费用。

如果你可以将调用exp()的循环移动到本机代码中,那么只有一个本机调用,那么你可能会得到更好的结果,但我怀疑它会比纯Java解决方案快得多

我不知道您的应用程序的详细信息,但是如果您有一组相当有限的可能参数,那么您可以使用预先计算的查找表来提高Java代码的速度。

答案 12 :(得分:0)

对于exp,有更快的算法取决于你想要完成的事情。问题空间是否限制在一定范围内,您是否只需要一定的分辨率,精度或准确度等。

如果你很好地定义了你的问题,你可能会发现你可以使用一个带插值的表,例如,几乎可以将任何其他算法从水中吹出来。

您可以使用哪些约束来获得性能权衡?

- 亚当

答案 13 :(得分:0)

  

我运行拟合算法,拟合结果的最小误差更大   而不是Math.exp()的精度。

超越函数总是比加法或乘法慢得多,也是众所周知的瓶颈。如果您知道您的值在一个狭窄的范围内,您可以简单地构建一个查找表(两个排序的数组;一个输入,一个输出)。使用Arrays.binarySearch查找正确的索引并使用[index]和[index + 1]处的元素插值。

另一种方法是分割数字。让我们拿,例如3.81并将其分成3 + 0.81。 现在你将e = 2.718乘以三次得到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)。

答案 14 :(得分:0)

它可能不再相关,但只是你知道,在OpenJDK的最新版本中(参见here),Math.exp应该是一个内在的(如果你不知道那是什么)是,检查here)。

这将使大多数体系结构的性能无与伦比,因为这意味着Hotspot VM将在运行时通过特定于处理器的exp实现替换对Math.exp的调用。你永远不会打败这些电话,因为它们针对架构进行了优化......