代码之家  ›  专栏  ›  技术社区  ›  ldog

Java实时性能

  •  12
  • ldog  · 技术社区  · 16 年前

    我正在使用Java项目,它需要非常先进的图像处理。实际上,我使用opencv进行了大部分操作,并且使用jni来包装我需要的opencv函数。我对opencv提供的性能非常满意,编写opencv代码的人应该得到代码的巨大赞誉。与我在代码中体验到的JavaDEVS形成了鲜明的对比。

    我开始对编程语言的选择持乐观态度,我的项目的第一个工作迭代工作得很好,但它的性能远不接近实时(每2秒获得1帧左右)。我对代码进行了一些优化,它帮助了很多。我已经能够把帧速率提高到每秒大约10到20帧,这是伟大的,但我发现,为了做任何进一步的优化,我必须重写Java代码来做同样的事情,但10-20x更有效率。

    我对Java开发人员很少关注性能,尤其是在为媒体相关类编写类时感到惊骇。我已经下载了openjdk,正在探索我使用的功能。例如,光栅类下有一个名为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到300毫秒,然后抓取浮点像素大约需要50-100Ms。这对于实时性能来说是不可接受的。注意,为了加速,我大量使用位操作运算符,JavaDVS远离它。

    我知道他们需要处理一般情况,但即便如此,他们至少不能为优化提供选项,或者至少是警告这段代码的执行速度有多慢。

    我的问题是,在开发的这一点上(我已经有了第一次迭代,而不是我在第二次执行更多的实时操作),如果我咬紧牙关,切换到C/C++,在那里我可以对事物进行更多的微调,或者我应该坚持Java,希望事情变得更加实时友好,这样我就不必再使用RWR了。已实现Java代码以获得加速。

    我真的开始讨厌“优雅”和慢爪哇真的是什么。课程的数量似乎太多了。

    9 回复  |  直到 13 年前
        1
  •  15
  •   hhafez    16 年前

    我已经用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像素循环中使用这个。通过使用一个方法调用,JVM可以更大程度地优化这个操作,也可能更优化for循环。

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

    查找类似、更快的图像处理代码的位置:

    我强烈建议您查看ImageJ图像处理应用程序的代码(由于stackoverflow被延迟而无法链接)及其库,特别是ij.process.typeconverter。就像您的代码一样,它非常依赖于 位旋转直接数组操作 和A 额外数组创建的最小值 . Java2D库(标准JRE的一部分)和Java高级成像(JAI)库(由于StAcExoad被延迟而不能链接)提供了其他方式来直接在图像数据上进行图像处理,而不必每次滚动自己的操作。对于java2d,您只需小心使用哪些函数即可。

    为什么java2d库如此间接:

    大多数“类性”是由于支持多种颜色模型和存储格式(即hsb图像、基于浮点的颜色模型、索引颜色模型)。间接性的存在是有原因的,有时实际上会提高性能——BufferedImage类(例如)直接挂接到最近的虚拟机中的图形内存中,以使某些操作更快。间接性让它经常从用户那里屏蔽这一点。

        2
  •  6
  •   hhafez    16 年前

    我的问题是,在开发的这一点上(我已经有了第一次迭代,而不是我在第二次执行更多的实时操作),如果我咬紧牙关,切换到C/C++,在那里我可以对事物进行更多的微调,或者我应该坚持Java,希望事情变得更加实时友好,这样我就不必再使用RWR了。已实现Java代码以获得加速。

    你在问我应该吗

    1. 切换到可以满足性能要求的语言。
    2. 坚持Java,希望事情有所改善。

    可能还有其他选择……但是选项2似乎不现实,您不能仅仅“希望”代码变得更快:p

    需要注意的几点:

    1. openjdk和sun jdk的性能不一样,你试过sun jdk吗?
    2. 如果您需要完成的性能优化在一些方法中,那么它可能值得重新编写它们并坚持Java…
        3
  •  3
  •   Yishai    16 年前

    我的建议将取决于图像处理与整个项目相比的重要性,以及相对于Java带来的任何优势。显然,如果需要的话,可以用Java编写代码(如您所演示的)。但是,如果你的80%的项目将由这样的优化组成,我肯定会重新思考Java作为语言的选择。

    另一方面,如果这表示应用程序的20%,而其他80%个是提供此转换的用户功能,那么可能需要做的工作是完成操作,这是一个值得权衡的事情,不必处理您自己的内存管理,并让任何其他API Java给用户。交互(web、swing、swt,无论您使用什么)。

    Java由于垃圾收集器而不具备实时能力。这可能也会咬到你,所以要小心。

        4
  •  1
  •   ykaganovich Mike Samuel    16 年前

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

        5
  •  1
  •   Dr. James J. Hunt    13 年前

    不清楚你是否真的在问实时性。实时和快速之间有区别。对于真正的快速,考虑到平均情况行为就足够了。吞吐量是主要问题。实时意味着每次都能在固定的时间内完成某项任务。当然,也有一些应用程序需要两者。

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

    编译是实时Java与传统Java实现不同的另一点。实时Java技术倾向于使用静态或超前时间(AOT)编译而不是JIT编译。对于您的应用程序来说,JIT可能是可以的,因为您可能能够忍受传统VM编译最常用类所需的“预热”时间。如果是这样,那么您可能没有实时需求,只需要吞吐量需求。

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

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

        6
  •  0
  •   Bill K    16 年前

    过早的优化是万恶之源。

    与其抱怨,不如编写一组优化的库并释放它们,但是创建一个“引用”Java实现,这是错误的,它对一些不存在的目标进行了预优化。

    引用实现的主要目的是使代码易于理解和维护——必须这样。我认为总是有一种期望,即在必要的时候,供应商会分析这个可理解的版本,并重新实现部件以提高速度。

        7
  •  0
  •   Eddie    16 年前

    除了其他人所说的,您还可以对JDK进行优化。如果您可以提供一个不牺牲通用性或可读性的强大优化,我希望您能够在将来的JDK版本中包含您的补丁。

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

        8
  •  0
  •   Bill K    16 年前

    据我所知,最新版本的Java(或者它是JavaFX)具有允许您访问系统的视频硬件中的高级功能的方法。我很抱歉我是如此的普通,我相信我在JAVA POSSE上听说过这件事,因为我被困在Java 1.3的土地上,我从来没有真正的机会去检查它,但是我确实记得听到过这样的事情。

    以下是有关它的一些信息: But it looks like it will only be in Java 7 :(

    看起来它只支持播放流和基本流操作——但是“等待”和“Java将改进”的方法实际上可能是有效的。

        9
  •  0
  •   Steven Evers    16 年前

    是什么阻止了你编写一个你想要使用的方法的优化版本而不是使用内置方法?如果这是不可能的,为什么不用更本土化的语言编写对象,并将其导入现有的应用程序?