前情提要
众所周知, 在python的世界, 矩阵库最好用的莫过于numpy, 类似于Matlab的语法, 让很多人从Matlab迁移过来毫无压力.
然而, 在Java的世界, 虽然以Spring为主的Web框架非常强大, 但是科学计算库的支持相比之下就相当感人. 倒并不是缺乏支持, 而是你很难找到一个足够好用效率又高支持又全的矩阵库.
当然实际上, 对于矩阵库的使用还是以效率为主, 所以今天要介绍的是Java世界中效率最高的矩阵库之一matrix-toolkits-java, 虽然作者已经不再维护, 但是性能经过实测依然吊打其他所有.
这个库性能高的主要原因在于它使用了Native库, 相比同样使用Native库的JBLAS, 这个库因为能够支持Intel MKL和Nvidia CUDA, 使其性能更高, 相关的Java矩阵库比较可以在Java Matrix Benchmark这里看到详细的比较.
正片开始
MTJ配置使用
MTJ在Maven中心仓库里, 所以如果使用Maven来管理依赖的话, 那么只要简单的添加即可:
<dependency>
<groupId>io.github.andreas-solti.matrix-toolkits-java</groupId>
<artifactId>mtj</artifactId>
<version>1.0.7</version>
</dependency>
这里用的是另外一个Fork, 似乎是修正了原作者版本的一些问题, 不过没仔细看. 关于MTJ的文档可以参考这个: http://www.javadoc.io/doc/io.github.andreas-solti.matrix-toolkits-java/mtj/1.0.7.
使用这个库务必需要注意的是很多操作都是不复制而是在原矩阵上修改的, 例如:
public Matrix add(double alpha, Matrix B) {
checkSize(B);
if (alpha != 0)
for (MatrixEntry e : B)
add(e.row(), e.column(), alpha * e.get());
return this;
}
public void add(int row, int column, double value) {
set(row, column, value + get(row, column));
}
所以使用时如果要符合一般的使用习惯请务必注意:
/**
* Simplify Some DenseMatrix Operation
*/
private static class DenseMatrixUtil{
// return C=A+B
public static DenseMatrix add(DenseMatrix A, DenseMatrix B){
return (DenseMatrix)A.copy().add(B);
}
// return A=A+B
public static DenseMatrix addi(DenseMatrix A, DenseMatrix B){
return (DenseMatrix)A.add(B);
}
// return C=A-B
public static DenseMatrix sub(DenseMatrix A, DenseMatrix B){
return (DenseMatrix)A.copy().add(-1, B);
}
// return A=A-B
public static DenseMatrix subi(DenseMatrix A, DenseMatrix B){
return (DenseMatrix)A.add(-1, B);
}
// return C=s*A
public static DenseMatrix mul(DenseMatrix A, double s){
return (DenseMatrix)A.copy().scale(s);
}
// return A=s*A
public static DenseMatrix muli(DenseMatrix A, double s){
return (DenseMatrix)A.scale(s);
}
// return C=A*B
private static DenseMatrix mmul(DenseMatrix A, DenseMatrix B){
DenseMatrix C = new DenseMatrix(A.numRows(), B.numColumns()); // C.rows = A.rows, C.cols = B.cols
return (DenseMatrix)A.mult(B, C);
}
// Compute \sum{alpha[i] * matrix[i]}
private static DenseMatrix linear(double[] alphas, DenseMatrix[] matrixs){
if(alphas.length != matrixs.length){
throw new IllegalArgumentException("alpha's length should equal matrix's length");
}
DenseMatrix result = (DenseMatrix)new DenseMatrix(matrixs[0].numRows(), matrixs[0].numColumns()).zero();
for(int i = 0; i < alphas.length; i++){
result.add(alphas[i], matrixs[i]); // result = alpha * matrix + result
}
return result;
}
}
与Intel MKL整合
Intel MKL全称是Intel® Math Kernel Library, 是Intel为其处理器设计的一套高度优化的Native矩阵与线性运算函数库, 相比OpenBLAS有着高得多的性能. 至于AMD处理器能不能使用, 这个我就不知道了, 但是我还是要喊出 AMD YES! (逃
Intel MKL安装
在Linux下, 只要直接从源里直接安装即可, 不同发行版不一样搜索一下即可, 以ArchLinux为例:
pacman -S intel-mkl
Arch的官方源里是没有MKL的, 如果需要的话, 请使用ArchlinuxCN或者Arch4Edu源.
在Windows下, 只能从官网注册开发者然后申请, 几天之后会给你发邮件然后提供下载连接下载Windows版的安装包.
这个安装包是包含devel部分的开发包, 事实上我们只需要redist部分即可, 为了方便使用我打包了2018.3.210的redist包(x86/x64). 事实上, Anaconda里面也是带Intel MKL Redist的, 完全可以从里面扒出来...
Intel MKL配置
在安装好Intel MKL以后还需要做两件事情:
- 将
mkl\mkl_rt.dll
软链接或者复制为libblas3.dll
或liblapack3.dll
- 将
mkl
与compiler
两个文件夹添加到系统的PATH
环境变量中
这样, 才能够被MTJ-N所识别并使用.
如果不使用Intel MKL, MTJ似乎会自动使用OpenBLAS的实现, 其输出是下面这样的:
一月 14, 2019 11:31:15 上午 com.github.fommil.netlib.BLAS <clinit>
警告: Failed to load implementation from: com.github.fommil.netlib.NativeSystemBLAS
一月 14, 2019 11:31:15 上午 com.github.fommil.jni.JniLoader liberalLoad
信息: successfully loaded C:\Users\$USER\AppData\Local\Temp\jniloader7616017108689444760netlib-native_ref-win-x86_64.dll
一月 14, 2019 11:31:33 上午 com.github.fommil.netlib.LAPACK <clinit>
警告: Failed to load implementation from: com.github.fommil.netlib.NativeSystemLAPACK
一月 14, 2019 11:31:33 上午 com.github.fommil.jni.JniLoader load
信息: already loaded netlib-native_ref-win-x86_64.dll
性能虽然会比纯Java实现要好, 但是与MKL相比之下就要逊色很多了.
如果正确识别到MKL的话, 应该会类似的输出:
一月 14, 2019 11:38:25 上午 com.github.fommil.jni.JniLoader liberalLoad
信息: successfully loaded C:\Users\$USER\AppData\Local\Temp\jniloader3275505562389233726netlib-native_system-win-x86_64.dll
一月 14, 2019 11:38:26 上午 com.github.fommil.jni.JniLoader load
信息: already loaded netlib-native_system-win-x86_64.dll
微不足道的总结
如果要做科学计算方面的工作的话, 还是:
人生苦短, 快用Python (并不