关于Java:通过JNI更快的Math.exp()吗?

关于Java:通过JNI更快的Math.exp()吗?

faster Math.exp() via JNI?

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

我只尝试了jni + C,但是它比纯java慢。


已经多次请求了此请求(请参见此处)。这是从此博客文章中复制的Math.exp()的近似值:

1
2
3
4
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的结果,然后您可以比重新计算更快地查找答案。


您还希望在C中包装任何循环调用Math.exp()。否则,在Java和C之间进行编组的开销将压倒任何性能优势。


如果分批执行,则可能使它运行得更快。进行JNI调用会增加开销,因此您不想为需要计算的每个exp()都这样做。我尝试传递100个值的数组,并获取结果以查看它是否对性能有所帮助。


真正的问题是,这成为您的瓶颈吗?您是否已对应用程序进行了概要分析,并发现这是速度下降的主要原因?如果没有,我建议使用Java版本。尽量不要进行预优化,因为这只会导致开发速度变慢。您可能会花较长的时间解决可能不是问题的问题。

话虽如此,我认为您的测试给了您答案。如果jni + C较慢,请使用Java版本。


Commons Math3附带了优化版本:FastMath.exp(double x)。它确实大大加快了我的代码的速度。

Fabien运行了一些测试,发现它的速度几乎是Math.exp()的两倍:

1
2
 0.75s for Math.exp     sum=1.7182816693332244E7
 0.40s for FastMath.exp sum=1.7182816693332244E7

这是javadoc:

计算exp(x),函数结果几乎取整。对于输入值的99.9%,它将正确舍入为理论值,否则将有1 UPL误差。

方法:

1
2
3
4
    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误差。


它可能不再相关,但是可以知道,在OpenJDK的最新版本中(请参见此处),应将Math.exp设为内在函数(如果您不知道这是什么,请在此处检查)。

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


I run a fitting algorithm and the minimum error of the fitting result is way larger
than the precision of the 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,错误为2000的一部分(正确:45.15)。


根据您要完成的工作,有更快的exp算法。问题空间是否限制在特定范围内,您是否仅需要特定的分辨率,精度或准确性等?

如果您很好地定义了问题,则可能会发现您可以使用带有插值的表,例如,该表将使几乎所有其他算法无效。

您可以对exp应用哪些约束来获得性能折衷?

-亚当


跨越JNI边界进行调用会产生一定的成本。

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

我不知道您的应用程序的详细信息,但是如果调用的可用参数集相当有限,则可以使用预先计算的查找表来加快Java代码的速度。


根据您的需求编写自己的。

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


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

因此,正如其他人所建议的那样,尝试整理涉及使用JNI的操作。


使用JNI的问题是调用JNI涉及的开销。如今,Java虚拟机已进行了非常优化,并且对内置Math.exp()的调用已自动进行了优化,以直接调用C exp()函数,甚至可能对其进行了优化,使其成为直接的x87浮点程序集说明。


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

另外,您不应缓存输入参数为浮点实数的方法的结果。及时获得的收益将在使用的空间量上大量损失。


推荐阅读

    脚本linux上运行命令?

    脚本linux上运行命令?,工具,代码,时间,密码,系统,环境,名字,位置,第三,下来,t

    linux运行命令的脚本?

    linux运行命令的脚本?,系统,服务,工具,脚本,意外,技术,分析,文件,方法,命令,s

    重启计算机命令linux?

    重启计算机命令linux?,系统,工作,命令,服务,标准,设备,灵活,首要,意义,参数,L

    linux转发请求命令?

    linux转发请求命令?,系统,工作,密码,网络,服务,信息,工具,项目,状态,标准,关

    linux运行脚本的命令?

    linux运行脚本的命令?,系统,工具,代码,服务,脚本,状态,密码,环境,位置,暂停,l

    linux计算机的命令?

    linux计算机的命令?,系统,工作,信息,设备,技术,命令,网站,管理,灵活,基础,lin

    linux命令行运行中断?

    linux命令行运行中断?,连续,工作,系统,信息,程序,命令,设备,工具,网络,情况,L

    vim运行linux命令?

    vim运行linux命令?,系统,工作,信息,地址,命令,标准,时间,情况,工具,基础,linu

    linux下并行运行命令?

    linux下并行运行命令?,系统,服务,工作,命令,环境,网络,暂停,文件,脚本,参数,l

    jar运行命令linux?

    jar运行命令linux?,项目,系统,平台,工具,上期,命令,选项,日志,文件名,目录,Li

    jar运行命令linux?

    jar运行命令linux?,项目,系统,平台,工具,上期,命令,选项,日志,文件名,目录,Li

    linux下并行运行命令?

    linux下并行运行命令?,系统,服务,工作,命令,环境,网络,暂停,文件,脚本,参数,l

    linux模拟请求命令?

    linux模拟请求命令?,地址,工作,系统,工具,信息,标准,命令,目录,发行,数据,lin

    linux命令行后台运行?

    linux命令行后台运行?,服务,状态,标准,暂停,命令,后台,连续,地方,工作,方法,l

    linux用计算器的命令?

    linux用计算器的命令?,代码,环境,情况,异常,工具,数据,位置,平台,精密,设计,

    脚本运行linux命令?

    脚本运行linux命令?,系统,环境,工具,工作,位置,底部,代码,发行,官网,终端,lin

    linux进程运行命令?

    linux进程运行命令?,系统,工作,状态,地址,信息,进程,基础,命令,管理,软件,lin

    linux显示运行命令?

    linux显示运行命令?,系统,服务,状态,信息,工具,数据,电脑,标准,管理,时间,如

    linux运行vim命令?

    linux运行vim命令?,系统,工具,官方网站,模式,基础,数据,代码,环境,入口,命令