Java 矩阵数学库的性能?

我们正在计算一些其运行时间受矩阵运算约束的东西。(如有兴趣,请参阅以下资料。)这一经历促使人们提出以下问题:

人们是否对用于矩阵数学的 Java 库的性能有经验(例如,乘法、逆运算等) ? 例如:

我搜查了一遍,什么也没找到。


我们的速度比较详情:

我们正在使用英特尔 FORTRAN (IFORT (IFORT)10.120070913)。我们已经在 Java (1.6)中使用 Apachecommons 数学1.2矩阵操作系统重新实现了它,并且它同意所有数字的准确性。(我们希望在 Java 中使用它是有原因的。)(Java 双精度,Fortran real * 8)。Fortran: 6分钟 Java 33分钟,同一台机器。Jvisalm 分析显示了在 RealMatrixImpl 中花费的大量时间。{ getEntry,isValid坐标}(在未发布的 Apache commons math2.0中似乎已经消失,但是2.0并没有更快)。Fortran 正在使用 Atlas BLAS 例程(dpotrf 等)。

显然,这可能取决于我们每种语言的代码,但我们相信大多数时间是在等价的矩阵运算。

在其他一些不涉及库的计算中,Java 的速度并没有慢多少,有时甚至快得多。

116099 次浏览

你看过 英特尔数学内核库了吗?它声称甚至超过了 地图集。MKL 可以是 用于 Java通过 JNI 包装器。

我不能对特定的库进行评论,但是原则上,在 Java 中这样的操作没有什么理由慢下来。Hotspot 通常会做一些你期望编译器做的事情: 它将 Java 变量上的基本数学操作编译成相应的机器指令(它使用 SSE 指令,但每次操作只有一个) ; 对数组元素的访问被编译成使用你期望的“原始”MOV 指令; 它决定如何在可以的时候将变量分配给寄存器; 它重新排序指令以利用处理器架构... ... 一个可能的例外是,正如我提到的,Hotspot 每条 SSE 指令只执行一次操作; 原则上,你可以有一个非常优化的矩阵库,每条指令执行多次操作,尽管我不知道,比如说,你的特定 FORTRAN 库是否这样做,或。如果是这样,那么 Java (或者至少是 Hotspot)目前还没有办法与之竞争(当然,你可以用这些优化编写自己的本机库来从 Java 调用)。

那么这一切意味着什么呢:

  • 原则上,寻找一个性能更好的库是值得的,尽管不幸的是,我不能推荐一个
  • 如果性能对你来说真的很重要,我会考虑只编写你自己的矩阵运算,因为这样你就可以执行一些库通常不能执行的优化,或者你使用的特定库不能执行的优化(如果你有一个多处理器机器,找出这个库是否实际上是多线程的)

矩阵操作的一个障碍通常是数据局部性问题,当您需要一行一列地遍历数据时会出现这种问题,例如,在矩阵乘法中,因为您必须按照优化一个或另一个的顺序存储数据。但是如果你手写代码,有时候你可以用 合并操作以优化数据位置(例如,如果你用矩阵乘以它的变换,你可以把一个列遍历变成一个行遍历,如果你写一个专用的函数而不是合并两个库函数)。与生活中的通常情况一样,库将为您提供非最优性能,以换取更快的开发; 您需要确定性能对您有多重要。

Linalg 代码严重依赖于奔腾和后来的处理器的矢量计算能力(从 MMX 扩展开始,比如 LAPACK 和现在的 Atlas BLAS)并不是“令人难以置信的优化”,而仅仅是行业标准。要在 Java 中复制这种性能,您将需要本机库。我遇到了你描述的同样的性能问题(主要是为了能够计算 Choleski 分解) ,并且没有发现什么真正有效的东西: Jama 是纯 Java,因为它应该只是一个模板和参考工具包,供实现者遵循... 这从来没有发生过。你知道 Apache 的数学常识... ... 至于 COLT,我仍然需要测试它,但它似乎严重依赖于忍者的改进,其中大部分是通过构建一个特别的 Java 编译器实现的,所以我怀疑它是否有帮助。 在这一点上,我认为我们“只是”需要一个集体的努力来构建一个本地的 Jama 实现..。

根据 Varkhan 的文章,特定于奔腾的本地代码会做得更好:

我们已经使用 COLT 的一些相当大的严重的财务计算,并已与它非常高兴。在我们大量分析的代码中,我们几乎从来不需要用自己的代码替换 COLT 实现。

在他们自己的测试中(显然不是独立的) ,我认为他们声称在英特尔手工优化的汇编程序的2倍之内。好好使用它的诀窍是确保您理解他们的设计哲学,并避免无关的对象分配。

有许多不同的免费的 java 线性代数库。 < a href = “ http://www.ujmp.org/java-Matrix/foundation/”rel = “ nofollow noReferrer”> http://www.ujmp.org/java-matrix/benchmark/ 遗憾的是,基准测试只提供了关于矩阵乘法的信息(换位测试不允许不同的库利用各自的设计特性)。

您应该看看这些线性代数库在计算各种矩阵分解时的执行情况。 Http://ojalgo.org/matrix_compare.html

我发现,如果你创建了很多高维矩阵,你可以让 Jama 快20% 左右,如果你改变它使用一个单维数组,而不是一个二维数组。这是因为 Java 不能有效地支持多维数组。也就是说。它创建一个数组数组。

Colt 已经这样做了,但是我发现它比 Jama 更复杂、更强大,这也许可以解释为什么使用 Colt 时简单函数的速度要慢一些。

答案实际上取决于你正在做什么。Jama 不支持 Colt 所能做的一小部分事情,而这些事情能够产生更大的影响。

之前已经提到过 Matrix Tookit Java (MTJ) ,但是对于其他碰到这个线程的人来说,它可能值得再次提及。对于那些感兴趣的人来说,似乎也有关于让 MTJ 代替 Apache Commons 数学2.0中的 linally 库的讨论,尽管我不确定最近进展如何。

您可能想查看 Jblas项目。这是一个相对较新的 Java 库,它使用 BLAS、 LAPACK 和 ATLAS 进行高性能矩阵操作。

开发商已经发布了一些 基准,其中 jblas 对 MTJ 和 Colt 有利。

还有 UJMP

我是 jblas 的主要作者,我想指出的是,我已经在2009年12月底发布了 Version 1.0。我在打包方面做了很多工作,这意味着你现在可以下载一个带有 ATLAS 和 JNI 库的“胖罐子”,适用于 Windows、 Linux、 Mac OS X、32和64位(Windows 除外)。这样,只需将 jar 文件添加到类路径,就可以获得本机性能。去 http://jblas.org看看!

您应该将 Apache Mahout 添加到您的购物清单中。

我只是想多说两句。我比较了其中的一些库。我试图用一个3000乘以3000的双精度矩阵。结果如下。

使用带有 C/C + + 、 Octave、 Python 和 R 的多线程 ATLAS,所花的时间约为4秒。

通过 Java 使用 Jama,所需时间为50秒。

使用 Colt 和 Java 并行 Colt,所花的时间是150秒!

在 Java 中使用 JBLAS 时,由于 JBLAS 使用多线程 ATLAS,所花费的时间也是4秒左右。

所以对我来说,很明显 Java 库的性能不是很好。但是,如果有人必须用 Java 编写代码,那么最好的选择是 JBLAS。贾马,柯尔特和平行柯尔特并不快。

有一个基准的各种矩阵包可在 java 上 为一些不同的硬件配置设置 http://code.google.com/p/java-matrix-benchmark/ ,但它不能替代自己的基准测试。

性能会随着硬件的类型(CPU、内核、内存、 L1-3缓存、总线速度)、矩阵的大小以及打算使用的算法而变化。不同的库对于不同的算法有不同的并发性,所以没有单一的答案。您还可能会发现,转换到本机库所期望的表单的开销抵消了用例的性能优势(一些 Java 库在矩阵存储方面有更灵活的选项,可以用于进一步的性能优化)。

然而,一般来说,JAMA、 Jampack 和 COLT 已经过时了,它们不能代表当前 Java 中线性代数的性能状态。越来越多的现代库能够更有效地利用多核和 CPU 缓存。JAMA 是一个参考实现,它几乎完全实现了教科书中的算法,而很少考虑性能。COLT 和 IBM Ninja 是第一个表明在 Java 中可以实现性能的 Java 库,即使它们落后于本机库50% 。

我是 La4j(Java 线性代数)库的作者,这是我的观点。我已经在 la4j 上工作了3年(最新版本是0.4.0[2013年6月1日]) ,只有现在我才能开始进行性能分析和优化,因为我刚刚介绍了所需的最小功能。所以,la4j 没有我想要的那么快,但是我花了很多时间来改变它。

我目前正在将新版本的 la4j 移植到 JMatBench平台。我希望新的版本将显示更好的性能比以前的一个,因为有几个改进,我在 la4j 做了更快的内部矩阵格式,不安全的存取器和快速阻塞算法的矩阵乘。

我是 Java Matrix Benchmark (JMatBench)的作者,我将就这个讨论给出我的想法。

Java 库之间存在显著的差异,虽然在整个操作范围内没有明显的赢家,但是在 最新服务表现结果(2013年10月)中可以看到一些明显的领导者。

如果您使用的是“大型”矩阵,并且可以使用本机库,那么使用 系统优化的 netlibMTJ就是明显的赢家(大约快3.5倍)。如果您需要一个纯 Java 解决方案,那么 MTJOjAlgoEJML平行柯尔特是不错的选择。对于小矩阵,EJML 显然是赢家。

我没有提到的库显示了严重的性能问题或缺少关键特性。

我刚把 Apache Commons Math 和 jlapack 进行了比较。

测试: 随机1024x1024矩阵的奇异值分解。

计算机: Intel (R) Core (TM)2 Duo CPU E6750@2.66 GHz,linux x64

八度代码: A = rand (1024) ; tic; [ U,S,V ] = svd (A) ; toc

results                                execution time
---------------------------------------------------------
Octave                                 36.34 sec


JDK 1.7u2 64bit
jlapack dgesvd                     37.78 sec
apache commons math SVD            42.24 sec




JDK 1.6u30 64bit
jlapack dgesvd                     48.68 sec
apache commons math SVD            50.59 sec


Native routines
Lapack* invoked from C:                37.64 sec
Intel MKL                               6.89 sec(!)

我的结论是,从 JDK 1.7调用的 jlapack 非常接近本机 Lapack 的二进制性能。我使用了 linux 发行版附带的 Lapack 二进制库,并调用 dgesvd 例程来获取 U、 S 和 VT 矩阵。所有的测试都是在每次运行的完全相同的矩阵上使用双精度进行的(除了 Octave)。

免责声明-我不是线性代数专家,不隶属于上述任何库,这不是一个严格的基准。 这是一个“自制”的测试,因为我对比较 JDK 1.7和1.6的性能提高以及通用数学 SVD 和 jlapack 很感兴趣。

对于3D 图形应用程序,lwjgl.util 矢量实现的性能比上面提到的 jblas 高出约3倍。

我用4x4矩阵做了一百万次矩阵乘法。

Lwjgl 完成约18毫秒,jblas 需要约60毫秒。

(我假设 JNI 方法不是很适合快速连续应用相对较小的乘法。因为转换/映射可能比实际执行乘法需要更多的时间。)

杰根 https://github.com/hughperkins/jeigen

  • 包装 Eigen C + + 库 http://eigen.tuxfamily.org,这是可用的最快的免费 C + + 库之一
  • 相对简洁的句法(如“ mmul”、“ sub”)
  • 处理稠密矩阵和稀疏矩阵

快速测试,用两个密集矩阵相乘,即:

导入静态 jeigen. MatrixUtil. * ;

int K = 100;
int N = 100000;
DenseMatrix A = rand(N, K);
DenseMatrix B = rand(K, N);
Timer timer = new Timer();
DenseMatrix C = B.mmul(A);
timer.printTimeCheckMilliseconds();

结果:

Jama: 4090 ms
Jblas: 1594 ms
Ojalgo: 2381 ms (using two threads)
Jeigen: 2514 ms
  • 与睡衣相比,一切都更快:-P
  • 与 jblas 相比,Jeigen 的速度没有 jblas 快,但它能处理稀疏矩阵。
  • 与 Ojalgo 相比,Jeigen 占用的运行时间大致相同,但只使用一个内核,因此 Jeigen 占用了总 CPU 的一半。Jeigen 有一个更简洁的语法,即“ mmul”和“ multiplyRight”