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

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

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

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

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

这已经被多次请求(参见例如此处 )。 这是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编译,这可能会有很大的不同。

使用Java。

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

你想要在C中包装任何调用Math.exp()循环。 否则,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%的输入值,否则小于1 ULP错误。

由于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算法取决于你想要完成的事情。 问题空间是否限制在一定范围内,您是否只需要一定的分辨率,精度或准确度等。

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

您可以使用什么约束来获得性能权衡?

-亚当

我运行拟合算法,拟合结果的最小误差大于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)。

它可能不再相关,但你知道,在最新版本的OpenJDK中(见这里 ),Math.exp应该是一个内在的(如果你不知道那是什么,请点击这里 )。

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