Java JNI调用比预期慢(至少2毫秒/次)

Java JNI调用比预期慢(至少2毫秒/次),java,c,performance,java-native-interface,Java,C,Performance,Java Native Interface,我从其他几份报告中读到,人们在一个简单的基本JNI调用中通常会得到大约4-80纳秒: 从 对于普通的本机方法,去年我发现在我的Windows桌面上平均有40 ns的调用,在我的Mac桌面上平均有11 ns的调用 从 然而,JNI呼叫通常需要30纳秒左右 当我在JNI代码中调用simple方法时(simple指的是不超过一个time int返回类型int的参数),我得到的往返调用时间(用System.nanoTIme度量)在50000-80000 ns之间 如果我做一个虚拟机“预热”,并在计时之前

我从其他几份报告中读到,人们在一个简单的基本JNI调用中通常会得到大约4-80纳秒:

对于普通的本机方法,去年我发现在我的Windows桌面上平均有40 ns的调用,在我的Mac桌面上平均有11 ns的调用

然而,JNI呼叫通常需要30纳秒左右

当我在JNI代码中调用simple方法时(simple指的是不超过一个time int返回类型int的参数),我得到的往返调用时间(用System.nanoTIme度量)在50000-80000 ns之间

如果我做一个虚拟机“预热”,并在计时之前运行数百次呼叫,我仍然会得到大约2000-4000纳秒(低于800-1000纳秒)。(如上所述,我曾听到其他人报告<100纳秒..并且在频繁拨打电话时,其总和高于100纳秒的10-20倍。)

这是正常速度吗?是什么原因导致我的本机代码被调用得如此之慢

更新:

JNIEXPORT jint JNICALL Java_com_snap2d_gl_RenderControl_correctGammaNative
  (JNIEnv *env, jobject obj, jint pixel) {
    return X2D_correctGamma(pixel, 1.0f);
}
其中X2D_correctGamma(int,float)是一种校正像素的gamma值的方法(自从发布以来,我已经实现了本机代码)

Java基准测试:

    for(int i = 0; i < 100; i++) {
        long t1 = System.nanoTime();
        correctGammaNative(0xFFF);
        long t2 = System.nanoTime();
        System.out.println(t2 - t1);
    }
for(int i=0;i<100;i++){
long t1=System.nanoTime();
校正管理(0xFFF);
long t2=System.nanoTime();
系统输出打印LN(t2-t1);
}
这就是“热身”代码。大多数println在初始调用后读取800-1000ns

不幸的是,我可能不得不放弃它,因为它应该用于渲染,每秒调用数千次会将帧速率降低到1fps

系统信息:

在JDK1.6.0_32(64位)、JDK1.7.0_04(64位)和JRE1.7.0_10(32位)上的行为类似

Windows 7 64位

16GB内存

i7-3770四核CPU@3.4-3.9ghz

GNU GCC MinGW编译器(32位和64位)

这是正常速度吗

不。如果你真的在每次JNI呼叫中获得50000-80000纳秒,那么有些奇怪的事情正在发生

是什么原因导致我的本机代码被调用得如此之慢

不知道。它几乎可以是任何东西。但是,如果您向我们展示本机代码和Java代码,我们将能够更好地进行解释

我的钱会花在这上面,根本不是JNI调用的问题。相反,我认为这是您进行基准测试的方式的产物。您可以做(或不做)很多事情,这会导致Java基准测试产生虚假的结果。我们需要查看您的基准测试代码


那么,您的更新表明您以前报告的计时(50000-80000或2000-4000)不正确或不相关。考虑到以下情况,800-1000ns的计时听起来似乎是合理的

我认为你的基准有三个缺陷

  • 您正试图测量几纳秒量级的时间间隔。但您的度量没有考虑的是调用
    System.nanoTime()
    需要花费大量时间。您需要做的是测量在每对
    System.nanoTime()
    调用之间进行数千或百万次JNI调用所需的时间,然后计算并打印平均值

  • 您的代码没有将进行JNI调用所花费的时间与执行调用体所花费的时间分开。(或者可能是这样……而您还没有向我们展示该代码。)。我怀疑gamma校正所需的时间会比JNI调用开销长得多

  • 你的热身不充分。您运行代码的时间是否足以让JIT编译生效是值得怀疑的。此外,您的基准代码仅限于一个方法调用这一事实意味着,即使JIT编译器确实运行了,您也可能永远不会调用该方法的JIT编译版本。将基准代码放入一个方法中,并重复调用该方法


  • 热身太短了。方法在大约10.000次调用后进行JIT。另外,将循环体移动到一个方法,因为“就地”无限循环jitting和jitting单个方法之间存在差异。

    您的原生方法是做什么的?它只是返回参数,还是做了更多的事情?因此,“预热”可能是扫描路径、从文件系统中找到DLL、将其加载到内存中然后动态调用本机函数所花费的时间。你能检查一下你的PATH env var有多长吗?50/80毫秒没那么长时间。我并不觉得它特别慢:50-80ms来查找和加载DLL并不是那么糟糕。不过,我希望后续调用的时间为0-1ms,因为您的方法什么都不做。是否有任何参数/返回值需要转换?“原始海报”似乎许多其他人报告0-100纳秒。。。在频繁调用时,比这高出10-20倍确实会增加。你能显示你正在计时的代码块和你使用的计时方法吗?你是否考虑过对整个图像而不是一次一个像素运行gamma校正?而且以前的计时也不一定不正确。我还是会收到前几个电话的号码是的。。。但是你的问题写的方式,乍一看就好像这些是你的“真实”数字。直到2/3通过问题,你才给出实际的数字。我很抱歉。我将修改这个问题。