【问题标题】:Java JNI calls are slower than expected (at least 2 ms/call)Java JNI 调用比预期慢(至少 2 毫秒/调用)
【发布时间】:2013-09-05 17:29:30
【问题描述】:

我从其他几份报告中读到,人们在简单的基本 JNI 调用中通常会在 4-80 ns 左右:

来自What makes JNI calls slow?

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

来自Possible increase of performace using JNI?

但是 JNI 调用通常需要大约 30 ns ..

当我在 JNI 代码中调用简单方法时(简单我的意思是返回类型 int 的时间参数不超过一个参数),我得到的往返调用时间(使用 System.nanoTIme 测量)在 50,000- 80,000 纳秒。

如果我做一个虚拟机“热身”并在计时之前运行几百次调用,我仍然会得到大约 2000-4000 ns(低于 800-1000)。 (如上所述,我听说其他人报告

这是正常速度吗?是什么导致我的本机代码被调用这么慢?

更新:

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) 是一种校正像素伽马值的方法(自发布以来我已经实现了本机代码)。

Java 基准测试:

    for(int i = 0; i < 100; i++) {
        long t1 = System.nanoTime();
        correctGammaNative(0xFFF);
        long t2 = System.nanoTime();
        System.out.println(t2 - t1);
    }

这是“热身”代码。大多数 println 在初始调用后读取 800-1000ns。

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

系统信息:

在 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 位)

【问题讨论】:

  • 你的原生方法是做什么的?它只是返回参数,还是做更多的事情?
  • 因此,“热身”可能是扫描 PATH、从文件系统中找到 DLL、将其加载到内存中然后动态调用本机函数所花费的时间。你能检查一下你的 PATH 环境变量有多长吗? 50/80ms 这不是很长的时间。我不觉得它特别慢:50-80 毫秒来查找和加载一个 DLL 还不错。不过,我希望后续调用的时间为 0-1 毫秒,因为您的方法正在执行 nothing。有没有需要转换的参数/返回值?
  • “原始海报”似乎很多其他人报告 0-100 ns...并且在频繁通话时加起来确实高出 10-20 倍。
  • 你能显示你正在计时的代码块和你使用的计时方法吗?
  • 您是否考虑过对整个图像而不是一次一个像素进行伽马校正?

标签: java c performance java-native-interface


【解决方案1】:

这是正常速度吗?

没有。如果您真的在每次 JNI 调用中获得 50,000-80,000 ns,那么就会发生一些奇怪的事情。

是什么导致我的本机代码被调用这么慢?

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

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


好的,您的更新表明您之前报告的时间(50,000-80,000 或 2000-4000)不正确或不相关。考虑到以下情况,800-1000ns 的时间听起来是合理的。

我认为您的基准测试存在三个缺陷。

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

  • 您的代码没有将进行 JNI 调用所用的时间与执行调用主体所用的时间分开。 (或者也许它确实......你还没有向我们展示那个代码。)。我怀疑 gamma 校正将花费比 JNI 调用开销更长的时间。

  • 您的热身不足。令人怀疑的是,您运行代码的时间是否足以让 JIT 编译启动。此外,您的基准代码仅限于单个方法调用这一事实意味着即使 JIT 编译器确实运行了,您也有可能“ d 从不调用该方法的 JIT 编译版本。将基准代码放入一个方法中,反复调用该方法。

【讨论】:

  • 之前的时间也不一定不正确。在最初的几个电话中,我仍然会收到这些号码。
  • 嗯,是的......但是你的问题的写法,乍一看好像这些是你的“真实”数字。直到问题的 2/3 时,您才给出实际数字。误导。
  • 我很抱歉。我会修改问题。
【解决方案2】:

热身时间太短了。在约 10.000 次调用后会触发一个方法。另外将循环体移动到一个方法中,因为“就地”无限循环抖动和单一方法抖动之间存在差异。

【讨论】:

    猜你喜欢
    • 2021-02-26
    • 2017-09-03
    • 1970-01-01
    • 1970-01-01
    • 2019-07-27
    • 2016-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多