Java实时性能

时间:2009-05-21 23:10:24

标签: java performance real-time

我正在使用需要非常高级的图像处理的Java项目。事实上,我正在使用OpenCV进行大部分操作,而我正在使用JNI来包装我需要的OpenCV函数。我对OpenCV给出的性能非常满意,编写OpenCV代码的人应该为代码赢得极大的荣誉。与Java开发人员编写的代码形成鲜明对比。

我开始对我的编程语言的选择持乐观态度,我对该项目的第一次工作迭代工作正常,但其性能远不及实时(每2秒获得大约1帧)。我做了一些优化我的代码及其帮助很多。我已经能够将帧速率提高到大约每秒10-20帧,这很好,但我发现要进行任何进一步的优化我必须重写Java代码来做同样的事情,但10效率提高-20倍。

我对Java的开发人员如何很少关注性能感到震惊,尤其是在为Media相关类编写类时。我已经下载了OpenJDK,我正在探索我正在使用的功能。例如,在Raster类下有一个名为getPixels(...)的函数,它获取图像的像素。我期望这个函数在源代码中是一个高度优化的函数,有几个调用System.arrayCopy来进一步优化性能。相反,我发现的是非常“优雅”的代码,他们调用5-6个不同的类和10-20种不同的方法,只是为了完成我在一行中可以做的事情:

for (int i =0; i < n; i++) {
  long p = rawFrame[i];
  p = (p << 32) >>> 32;
  byte red = (byte) ((p >> 16) & 0xff);
  byte green = (byte) ((p >> 8) & 0xff);
  byte blue = (byte) ((p) & 0xff);
  byte val = (byte)(0.212671f * red + 0.715160f * green + 0.072169f * blue);
  data[i] = val;
  grayFrameData[i] = (val & 0x80) + (val & (0x7f)); 
}

上面的代码将图像转换为灰度并获得浮点像素数据,大约1-10ms。如果我想对Java内置函数做同样的事情,转换为灰度本身需要200-300ms,然后抓取浮动像素需要大约50-100ms。这对于实时性能来说是不可接受的。注意为了获得加速,我大量使用按位运算符,Java开发人员不愿意这样做。

我理解他们需要处理一般情况,但即使如此,他们也不能至少给出优化选项,或者至少警告这段代码可能执行的速度有多慢。

我的问题是,在开发的最后阶段(我已经进行了第一次迭代,而不是我正在进行第二次实时执行更多)我应该咬紧牙关切换到C / C ++我可以更好地调整事物,或者我应该坚持使用Java并希望事情变得更加实时友好,这样我就不必重写已经实现的Java代码来获得加速。

我真的开始厌恶“优雅”和Java的速度。那里的课程数量似乎有些过分。

9 个答案:

答案 0 :(得分:15)

我已经完成了使用Java的计算机视觉工作,我可能会因为这个问题而投票,但它完全可用于计算机视觉和实时工作,你只需要知道如何使用它。

潜在优化:

如果您需要帮助优化代码,我很乐意提供帮助 - 例如,我可以告诉您,通过制作方法可能会提升性能

`public static final int getGrayScale(final int pixelRGB){
    return (0.212671f * ((pixelRGB >> 16) & 0xff) + 0.715160f * ((pixelRGB >> 8) & 0xff) + 0.072169f * ((pixelRGB) & 0xff));
}`

并在for {pixel}循环中使用它。通过使用方法调用,JVM可以更加大量地优化此操作,并且可以更好地优化for循环。

如果你有要刻录的RAM,你可以为所有可能的24位像素像素颜色创建一个输出灰度字节的静态最终查找表。这将是~16 MB的RAM,但是你不必进行任何浮点运算,只需要一个数组访问。这个可能更快,具体取决于您使用的JVM,以及它是否可以优化数组边界检查。

找到类似,更快的图像处理代码的地方:

我强烈建议您查看ImageJ图像处理应用程序的代码(由于StackOverflow被延迟而无法链接)&amp;它的库,特别是ij.process.TypeConverter。就像你的代码一样,它在很大程度上依赖于直接数组操作,具有比特错误最少的额外数组创建。 Java2D库(标准JRE的一部分)和Java高级成像(JAI)库(由于StackOverflow被延迟而无法链接)提供了其他方法,可以快速直接对图像数据进行图像处理,而无需每次都进行自己的操作时间。对于Java2D,您只需要小心使用哪些函数。

为什么Java2D库如此间接:

大多数“类别”是由于支持多种颜色模型和存储格式(I.E.HSB图像,基于浮动的颜色模型,索引颜色模型)。间接存在是有原因的,有时实际上提升了性能 - BufferedImage类(例如)直接挂钩到最近VM中的图形内存中,以使某些操作更快。间接让它可以在很多时候屏蔽用户。

答案 1 :(得分:6)

  

我的问题是,在开发的最后阶段(我已经进行了第一次迭代,而不是我正在进行第二次实时执行更多)我应该咬紧牙关切换到C / C ++我可以更好地调整事物,或者我应该坚持使用Java并希望事情变得更加实时友好,这样我就不必重写已经实现的Java代码来获得加速。

你问我应该

  1. 切换到可以满足我的性能要求的语言。
  2. 坚持使用Java并希望事情有所改善。
  3. 可能还有其他选项......但是选项2看起来并不现实,你不能只是“希望”代码变得更快:p

    需要注意几点:

    1. OpenJDK与Sun JDK没有相同的性能,你试过Sun JDK吗?
    2. 如果您需要完成的性能优化只有几种方法,那么可能值得重新编写它们并坚持使用Java ...

答案 2 :(得分:3)

我的建议是,这取决于图像操作与整个项目的比较有多重要,以及相对于Java带来的任何优势。显然,如果需要,您可以在java中编写快速代码(如您所示)。但是,如果80%的项目将包含这样的优化,我肯定会重新考虑Java作为语言选择。

另一方面,如果这代表应用程序的20%,而其他80%是提供此转换的用户功能,那么可能必须完成工作以完成操作是值得的权衡取舍必须处理你自己的内存管理,并拥有java为你提供用户交互的任何其他API(Web,Swing,SWT,无论你使用什么)。

由于垃圾收集器,Java因其实时能力而闻名。这也许会让你感到困惑,所以要小心。

答案 3 :(得分:1)

目前尚不清楚您是否真的在询问实时情况。实时和实际快速之间存在差异。对于真正的快速,考虑平均案例行为就足够了。吞吐量是主要关注点。实时意味着能够在固定的时间内完成一些任务。或者,有些应用程序需要两者。

在传统的Java实现中,例如OpenJDK,垃圾收集器是实现实时行为的最大问题。这是因为垃圾收集器可以在任何时候中断程序来完成它的工作。我的公司aicas实现了Java,它不需要单独的线程来进行垃圾收集。相反,在分配时完成了一些GC工作。实际上,通过为每个释放的块标记或扫描几个块来支付分配。这需要完全重新实现虚拟机。

编译是实时Java与传统Java实现不同的另一点。实时Java技术倾向于使用静态或Ahead-of-Time(AoT)编译而不是JIT编译。 JiT可能适合您的应用程序,因为您可以容忍传统VM编译最常用类所需的“预热”时间。如果是这样,那么您可能没有实时要求,只需要吞吐量。

如果您对确保帧解码不会被垃圾收集中断感兴趣,那么使用Java的实时实现以及AoT编译也是有意义的。 Java实时规范(RTSJ)还为实时和嵌入式编程提供了其他支持,例如RelatimeThread,AsyncEventHandler和RawMemoryAccess。

当然,获得良好的性能,无论是实时还是真正的快速,都需要注意细节。过度使用临时对象没有帮助。分配总是需要额外的成本,所以应该最小化。这是函数式语言的一个主要挑战,它不允许更改对象的状态。但是,应该注意理解正在编写的代码的关键路径,以避免不必要的优化。分析对于了解最佳使用优化工作的位置至关重要。

答案 4 :(得分:1)

我不知道你会获得多少性能提升,但是如果你有一个长时间运行的进程在做重复的事情,你应该尝试使用java -server来运行Server Hotspot VM。它比Windows上的默认客户端VM执行much better,该客户端VM针对快速启动时间进行了优化。

答案 5 :(得分:0)

过早优化是万恶之源。

而不是抱怨,编写一组优化的库并释放它们,但创建一个针对某些不存在的目标进行预优化的“参考”java实现是错误的。

参考实现的要点是制作可理解的,可维护的代码 - 它必须是。我认为,总有一种期望,必要的供应商会分析这个可理解的版本并重新实现部件的速度。

答案 6 :(得分:0)

除了其他人所说的内容之外,您还可以为JDK做出优化。如果你能提供一个不牺牲一般性或可读性的强大优化,我希望你能够在未来的JDK版本中包含你的补丁。

因此,您不必希望 JDK可以变得更好。你可以帮助实现它。

答案 7 :(得分:0)

据我了解,最新版本的Java(或者可能是JavaFX)具有允许您访问系统视频硬件中的高级功能的方法。对不起,我是如此普遍,我相信我在Java Posse上听说过,因为我陷入了Java 1.3的土地,我从来没有真正有机会检查它 - 但我确实记得听过类似的东西。

以下是关于它的内容:But it looks like it will only be in Java 7 :(

看起来它最初只支持播放流和基本流操作 - 但也许“等待和Java会改进”的方法可能实际上有效。

答案 8 :(得分:0)

是什么阻止您编写您希望使用的方法的优化版本而不是使用内置方法?如果这不可能,为什么不用更原生的语言编写对象,并将其导入现有的应用程序?