【问题标题】:Java performance in numerical algorithms数值算法中的 Java 性能
【发布时间】:2009-11-09 01:00:32
【问题描述】:

我对 Java 数值算法的性能感到好奇,例如矩阵矩阵双精度乘法,使用最新的 JIT 机器,例如与手动调整的 SSE C++/汇编器或 Fortran 对应物进行比较。

我在网上查看过,但大部分结果来自近 10 年前,我知道 Java 从那时起取得了很大进步。

如果您有在数字密集型应用程序中使用 Java 的经验,可以分享一下您的经验。此外,Java 在循环相对较短且内存访问不是很均匀但仍在 L1 缓存范围内的内核中表现如何?如果这样的内核连续执行多次,JVM可以在运行时对其进行优化吗?

谢谢

【问题讨论】:

  • 您最好自己测试一下,因为与 C++ 的比较会很困难,因为这将是最快的,并且任何比较都无法访问您的特定实现。
  • 请注意其他来到此页面的人:这个问题和大多数答案都来自 2009 年。现在的 JVM 比以前好多了。
  • 您可能想查看 ND4J,它支持 Java 的 n 维数组。 nd4j.org/benchmarking.html

标签: java performance optimization numerical sse


【解决方案1】:

我用 Java 编写了一些相当大且对性能敏感的数字代码(通常处理大型双精度数组)。

我发现 Java 对于快速数值计算来说“足够好”。尤其是当您考虑到您通常不受 CPU 限制时 - 内存延迟和缓存感知可能是大型数据集的最大问题。

但是,您仍然可以使用手动优化的 C/C++ 代码击败 Java,这些代码利用特定的矢量化指令等或高度自定义的内存布局。因此,对于最快的代码,您可以考虑用 C/C++ 编写核心算法并使用 JNI 从 Java 调用它。

就我个人而言,我发现创建原生代码依赖项通常比其价值更麻烦,因此我倾向于坚持使用纯 Java 方法。

【讨论】:

    【解决方案2】:

    这来自 .NET 方面,但我 90% 确信 Java 也是如此。虽然 JIT 会在可能的情况下使用 SSE 指令,但它目前在处理(例如)矩阵乘法时不会自动矢量化您的代码。使用编译器内在函数/内联汇编的手动矢量化 C++ 在这里肯定会更快。

    【讨论】:

      【解决方案3】:

      java 中最薄弱的地方之一是(原生)矩阵运算。这是由于 Java 矩阵的性质:

      • 您不能将矩阵声明为矩形,即。每行可以有不同的列数。

      • 从技术上讲,矩阵不是“双精度(或整数,...)矩阵”,而是 ... 数组的数组。最大的区别在于,由于数组是 Java 对象,您可以将相同的数组对象分配给多于 1 行。

      这两个属性使编译器无法进行许多标准矩阵优化。

      使用在单个长数组上模拟矩阵的 Java 库可能会获得更好的性能。但是,您需要为所有访问带来方法调用的开销。

      【讨论】:

      • 我认为您的意思是您不能将二维数组声明为矩形。但是您似乎在争辩说,Java 中最直接和最直接的矩阵实现存在一些问题。为什么这是唯一可能的实现?如果不是,那么这里就没有太多关于“Java 矩阵的性质”的陈述。像 Colt 这样的 Java 矩阵库呢?
      • 矩阵并不总是以这种方式表示。请参阅java.awt.image.Kernel 以获取由一维数组表示的矩阵示例
      • 这类库的问题是所有的矩阵访问都是通过方法完成的。方法调用通常比数组访问慢,并且它们会阻止某些编译器优化。例如。 for(int i=0; i<m; i++) { x = a[i]; ...} 聪明的编译器可以在开头添加一个 if 语句来检查 i m 是否小于或等于 a[] 的长度,如果这是真的,则强制消除 for 循环中的所有边界检查(如果它也可以证明 m 确实不变)。这也适用于在矩阵运算中非常常见的嵌套 for 循环,因此可以节省大量检查。
      • 使用长数组而不是多维数组的另一个问题是更容易并行运行代码。如果我知道执行的 2 个部分访问数组中的不同行或列(例如嵌套循环),我知道它们不能互相妨碍。编译器要知道对一个长数组的操作要困难得多。
      【解决方案4】:

      C++ 肯定会更快。您甚至可以为您的目的使用一些手动优化的库,其中包含每个主要 CPU 的汇编代码。没有比这更好的了。

      之后,如果需要,您可以使用 JNI 从 Java 调用它。

      Java 不适用于像这样的高性能算术计算。如果您依赖这些,我建议您选择一种适当的低级语言来实现它。或者,您也可以使用低级语言编写特定于性能的部分,然后使用 JNI 或其他 IPC 方法将其连接到 Java 前端。

      【讨论】:

        【解决方案5】:

        This 是指向 java 与 c++ 的编程语言决战页面的链接,它将让您比较 java 在几种计算密集型算法上的速度。它还将向您展示最高性能的 java 代码是什么样的。在大多数情况下,对于这几个特定的​​基准,java 需要更多时间(但不超过 2 或 3 次)来运行。

        【讨论】:

        • 我无法立即判断 - 这种比较是否忽略了预热时间。 Java 在达到巡航速度之前仍然需要大量的初始工作。
        • 真的。如果您想编写一个启动、执行一些计算然后关闭的程序,您可能不需要 java.util.但是如果您的程序将运行几分钟,那么启动时间只是噪音。当然,另一种选择是启动一个 java 进程并让它充当一个计算服务器——每次你需要计算时,你只需调用一个已经运行的实例。
        • @Thorbjørn Ravn Andersen - 1) 阅读常见问题解答! 2)注意程序运行几秒而不是微秒! 3) 查看稳态近似值shootout.alioth.debian.org/u64q/… 4) 阅读常见问题解答!关于Javashootout.alioth.debian.org/u64q/faq.php#dynamic
        • @igouy 如果您将稳定状态与 java -server 结果进行比较,它们并没有太大的不同。
        • @Peter - 我知道,告诉 Thorbjørn :-) [但还要检查常见问题解答中显示的测量值]
        【解决方案6】:

        其次,您最好自己测试一下,因为性能会根据您的具体操作而有所不同。我很难相信 Shane C. Mason 的回答,即 Java 性能将与 C++ 或 Fortran 性能相同,因为对于某些科学计算算法,即使 C++ 和 Fortran 也不能真正具有可比性。

        我有一个使用 C++ 编写的计算流体动力学代码,并且基本上将相同的代码翻译成 Fortran。我还不确定为什么,但 Fortran 版本的速度大约是 C++ 版本的两倍。我猜想使用边界检查和垃圾收集等功能,Java 会比两者都慢,但直到我测试后我才知道。

        【讨论】:

        • 你在你的C++代码中使用过restrict关键字吗? Fortran 编译器不必保证内存指针没有别名,而 C++ 编译器必须假设内存是别名,除非另有说明。你用了什么编译器?我用内在函数用 C++ 编写了我的程序,英特尔编译器比 GCC 快得多,我猜英特尔 C++ 对指令的排序更好,因为除了排序之外,汇编非常相似。
        • 我隐约意识到别名问题,但我对它的理解还不够好。我还没有尝试过限制,不幸的是,我还没有时间花在这上面。我在带有-O3的Linux上使用icpc和ifort(都是英特尔编译器)。请注意,我的观点不是 C++ 性能无法与 fortran 相提并论,而是除了语言之外,您还需要比较实现。
        • Fortran 也有一个比 C++ 更宽松的数字模型——默认情况下,它允许进行很多令人讨厌的数学优化,而你只能在 C/C++ 中使用 -ffast-math 和类似的方法进行优化。有时这无关紧要,有时它会使您的结果不太准确。
        【解决方案7】:

        这可能取决于您在 C++ 代码中所做的工作。

        例如,您使用的是 GPU 吗? 编辑我忘了 jogl,所以 Java 可以在这里竞争。

        您是使用 STM 或共享内存进行并行化,那么 Java 无法与之竞争。 并行矩阵乘法分析链接:http://www.cs.utexas.edu/users/plapack/papers/ipps98/ipps98.html

        您是否有足够的内存来在内存中进行计算,因此不需要垃圾收集器,并且您是否对垃圾收集器进行了微调以获得最佳性能?那么,Java 可能会具有竞争力。

        您是否使用多核,并且 C++ 是否针对利用此架构进行了优化?那么Java将无法竞争。

        你是在使用多台计算机捆绑在一起,那么 Java 将无法竞争。

        您是否使用这些的任意组合,那么这将取决于特定的实现。

        Java 的设计目的不是为了与手动调优的 C++ 程序竞争,但是,调优所花费的时间,您是否在重要的地方进行了足够的计算? Java 将能够提供一些合理的速度,但工作量比手动调整要少,但与仅编写 C++ 代码相比并没有太大的改进。

        您可能想看看是否对 Haskell 或 Erlang 有改进,例如对 C++ 的改进,因为这些语言更适合此类工作。

        【讨论】:

        • 使用 GPU?如,使用 OpenGL?如果使用 JOGL Java 可以很好地竞争。
        • 你是对的,我已经更正了我的答案,我忘了你可以使用 jogl 进行 GPU 工作。
        【解决方案8】:

        您是否对这些类型的计算感兴趣 - 快速傅里叶变换、Jacobi 连续松弛、蒙特卡洛积分、稀疏矩阵乘法、密集 LU 矩阵分解?

        它们构成了SciMark 2.0 composite benchmark,您可以将其作为小程序在您的计算机上启动。

        还有ANSI C versions的节目,还有Intel document (pdf) on optimizing and recompiling SciMark for C++


        同样,您可以使用The Java Grande Forum Benchmark Suitethe comparison C programs

        【讨论】:

          【解决方案9】:

          Java 使用即时 (JIT) 编译器将字节码转换为本机机器语言 - 因此它第一次运行代码块时速度会较慢,但一旦“预热”该段,性能将是相同的.简而言之 - 数值性能相当不错。

          【讨论】:

          • JIT 很好,但不足以保证良好的数值性能。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-11-09
          • 1970-01-01
          相关资源
          最近更新 更多