
faster Math.exp() via JNI?
我需要非常频繁地从Java计算 我只尝试了jni + C,但是它比纯java慢。 已经多次请求了此请求(请参见此处)。这是从此博客文章中复制的Math.exp()的近似值:
它基本上与具有2048个条目以及条目之间的线性插值的查找表相同,但是所有这些都带有IEEE浮点技巧。它比我的机器上的Math.exp()快5倍,但是如果使用-server进行编译,则差异可能很大。 +1以编写自己的exp()实现。也就是说,如果这确实是您应用程序中的瓶颈。如果您可以处理一些不准确性,则可以使用许多非常有效的指数估计算法,其中一些可以追溯到几个世纪以前。据我了解,即使对于必须返回"精确"结果的算法,Java的exp()实现也相当慢。 哦,不要害怕用纯Java编写该exp()实现。 JNI有很多开销,并且JVM有时甚至可以在C / C ++不能实现的情况下在运行时优化字节码。 使用Java。 另外,缓存exp的结果,然后您可以比重新计算更快地查找答案。
您还希望在C中包装任何循环调用 如果分批执行,则可能使它运行得更快。进行JNI调用会增加开销,因此您不想为需要计算的每个exp()都这样做。我尝试传递100个值的数组,并获取结果以查看它是否对性能有所帮助。 真正的问题是,这成为您的瓶颈吗?您是否已对应用程序进行了概要分析,并发现这是速度下降的主要原因?如果没有,我建议使用Java版本。尽量不要进行预优化,因为这只会导致开发速度变慢。您可能会花较长的时间解决可能不是问题的问题。 话虽如此,我认为您的测试给了您答案。如果jni + C较慢,请使用Java版本。
Commons Math3附带了优化版本:
Fabien运行了一些测试,发现它的速度几乎是
这是javadoc: 计算exp(x),函数结果几乎取整。对于输入值的99.9%,它将正确舍入为理论值,否则将有1 UPL误差。 方法:
精度:计算以63位精度完成,因此对于99.9%的输入值,应正确舍入结果,否则应小于1个ULP误差。 它可能不再相关,但是可以知道,在OpenJDK的最新版本中(请参见此处),应将Math.exp设为内在函数(如果您不知道这是什么,请在此处检查)。 这将使性能在大多数体系结构上无与伦比,因为这意味着Hotspot VM将在运行时用特定于处理器的exp实现代替对Math.exp的调用。您永远无法击败这些电话,因为它们针对架构进行了优化。
先验功能总是比加法或乘法慢得多,并且是众所周知的瓶颈。如果知道值在狭窄范围内,则可以简单地建立一个查找表(两个排序数组;一个输入,一个输出)。使用Arrays.binarySearch查找正确的索引,并使用[index]和[index + 1]处的元素插入值。
另一种方法是拆分数字。让我们以3.81并将其拆分为3 + 0.81。 现在为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相乘得到结果 根据您要完成的工作,有更快的exp算法。问题空间是否限制在特定范围内,您是否仅需要特定的分辨率,精度或准确性等? 如果您很好地定义了问题,则可能会发现您可以使用带有插值的表,例如,该表将使几乎所有其他算法无效。 您可以对exp应用哪些约束来获得性能折衷? -亚当 跨越JNI边界进行调用会产生一定的成本。 如果您也可以将调用exp()的循环也移到本机代码中,以便只有一个本机调用,那么您可能会得到更好的结果,但是我怀疑它会比纯Java解决方案快得多。 我不知道您的应用程序的详细信息,但是如果调用的可用参数集相当有限,则可以使用预先计算的查找表来加快Java代码的速度。 根据您的需求编写自己的。 例如,如果所有指数都是2的幂,则可以使用位移。如果您使用有限的范围或一组值,则可以使用查找表。如果不需要精确度,则可以使用不精确但速度更快的算法。
使用JNI只会带来一些开销,另请参见: 因此,正如其他人所建议的那样,尝试整理涉及使用JNI的操作。 使用JNI的问题是调用JNI涉及的开销。如今,Java虚拟机已进行了非常优化,并且对内置Math.exp()的调用已自动进行了优化,以直接调用C exp()函数,甚至可能对其进行了优化,使其成为直接的x87浮点程序集说明。 由于Java代码将通过即时(JIT)编译器编译为本地代码,因此实际上没有理由使用JNI来调用本地代码。 另外,您不应缓存输入参数为浮点实数的方法的结果。及时获得的收益将在使用的空间量上大量损失。 |