我们正在计算运行时受矩阵运算约束的东西.(如果感兴趣,请参阅下面的一些细节.)这次经历提出了以下问题
民间有没有使用Java库的矩阵数学性能经验(例如,乘法,逆等)?例如:
JAMA
小马
Apache公共数学
我搜查了一下,一无所获.
我们的速度比较详情:
我们使用的是英特尔FORTRAN(ifort(IFORT)10.1 20070913).我们使用Apache commons math 1.2矩阵运算在Java(1.6)中重新实现了它,并且它同意所有的精度数字.(我们有理由在Java中想要它.)(Java双打,Fortran真*8).Fortran:6分钟,Java 33分钟,同样的机器.jvisualm profiling显示了在RealMatrixImpl中花费了大量时间.{getEntry,isValidCoordinate}(在未发布的Apache commons math 2.0中似乎已经消失了,但2.0并不快).Fortran正在使用Atlas BLAS例程(dpotrf等).
显然这可能取决于我们在每种语言中的代码,但我们相信大部分时间都在等效的矩阵运算中.
在其他几个不涉及库的计算中,Java并没有慢得多,有时甚至更快.
我是Java Matrix Benchmark(JMatBench)的作者,我将对这个讨论给出我的看法.
Java库之间存在显着差异,虽然在整个运营范围内没有明显的赢家,但在最新的绩效结果(2013年10月)中可以看到一些明显的领导者.
如果您正在使用"大型"矩阵并且可以使用本机库,那么明确的赢家(大约快3.5倍)是具有系统优化的netlib的MTJ.如果您需要纯Java解决方案,那么MTJ,OjAlgo,EJML和Parallel Colt都是不错的选择.对于小型矩阵,EJML是明显的赢家.
我没有提到的库显示出重大的性能问题或缺少关键功能.
只是加我2美分.我比较了其中一些库.我试图矩阵乘以3000乘3000矩阵的双精度.结果如下.
使用带有C/C++,Octave,Python和R的多线程ATLAS,所花费的时间大约为4秒.
使用Jama with Java,所用时间为50秒.
使用Colt和Parallel Colt与Java一起使用时间为150秒!
使用JBLAS和Java,由于JBLAS使用多线程ATLAS,所以花费的时间大约为4秒.
所以对我来说很明显,Java库的表现并不好.但是,如果有人必须用Java编写代码,那么最好的选择是JBLAS.贾马,科尔特和平行柯尔特并不快.
我是jblas的主要作者,并想指出我在2009年12月底发布了1.0版.我在包装上做了很多工作,这意味着你现在可以下载一个带有ATLAS和JNI库的"胖罐子"适用于Windows,Linux,Mac OS X,32位和64位(Windows除外).这样,只需将jar文件添加到类路径中,即可获得本机性能.请访问http://jblas.org查看!
我不能真正评论特定的库,但原则上这些操作在Java中没有什么理由变慢.Hotspot通常会执行您希望编译器执行的操作:它将Java变量的基本数学运算编译为相应的机器指令(它使用SSE指令,但每个操作只有一个); 对数组元素的访问被编译为使用"原始"MOV指令,如您所期望的那样; 它可以决定如何在变量寄存器中分配变量; 它重新命令指令以利用处理器架构......可能的例外是,正如我所提到的,Hotspot每个SSE指令只执行一次操作; 原则上你可以有一个奇妙优化的矩阵库,每个指令执行多个操作,虽然我不 或者知道,例如,您的特定FORTRAN库是否这样做,或者是否存在这样的库.如果确实如此,那么Java(或者至少是Hotspot)目前无法与之竞争(尽管您当然可以编写自己的本机库以及从Java调用的优化).
那么,这意味着什么?好:
原则上,值得寻找性能更好的图书馆,但不幸的是我不能推荐一个
如果性能对你来说真的很重要,我会考虑编写你自己的矩阵操作,因为你可能会执行某些库通常不能的优化,或者你使用的特定库没有(如果你有的话)多处理器机器,找出库是否实际上是多线程的)
矩阵操作的障碍通常是当您需要逐行和逐列遍历时出现的数据局部性问题,例如在矩阵乘法中,因为您必须以优化其中一个的顺序存储数据.但是如果你手写代码,你有时可以结合操作来优化数据局部性(例如,如果你将矩阵乘以它的变换,如果你编写一个专用函数而不是组合,你可以将列遍历转换为行遍历两个库函数).像往常一样,图书馆会给你非最佳表现以换取更快的发展; 你需要决定性能对你有多重要.
我只是将Apache Commons Math与jlapack进行了比较.
测试:随机1024x1024矩阵的奇异值分解.
机器:Intel(R)Core(TM)2 Duo CPU E6750 @ 2.66GHz,linux x64
八度代码:A = rand(1024); 抽动; [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.
Jeigen https://github.com/hughperkins/jeigen
包装Eigen C++库http://eigen.tuxfamily.org,这是最快的免费C++库之一
相对简洁的语法,例如'mmul','sub'
处理密集和稀疏矩阵
快速测试,通过乘以两个密集矩阵,即:
import static 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
与jama相比,一切都更快:-P
与jblas相比,Jeigen不是那么快,但它处理稀疏矩阵.
与ojalgo相比,Jeigen需要大约相同的经过时间,但只使用一个核心,因此Jeigen使用了总CPU的一半.Jeigen有一个更简洁的语法,即'mmul'与'multiplyRight'
在http://code.google.com/p/java-matrix-benchmark/上有一些可用于java的各种矩阵包的基准测试, 用于一些不同的硬件配置.但它不能替代做自己的基准测试.
性能将随着您所拥有的硬件类型(CPU,内核,内存,L1-3缓存,总线速度),矩阵的大小以及您打算使用的算法而变化.不同的库对不同的算法有不同的并发性,所以没有单一的答案.您可能还会发现,转换为本机库所期望的表单的开销会削弱用例的性能优势(某些Java库具有更灵活的矩阵存储选项,可用于进一步的性能优化).
一般来说,JAMA,Jampack和COLT正在变老,并不代表Java中线性代数的当前性能状态.更现代的库可以更有效地使用多个内核和cpu缓存.JAMA是一个参考实现,几乎实现了教科书算法,而不考虑性能.COLT和IBM Ninja是第一个展示java可能性能的Java库,即使它们落后于本机库的50%.