上周工作时所用到的矩阵相关API,均是extends MLlib中相关分布式矩阵API,但是很快便遇到了问题,由于我需要重写原先IndexedRow的toString方法,所以写了一个类IndexRow extends IndexedRow,由于MLlib中IndexedRowMatrix是对IndexedRow的RDD封装,即RDD[IndexedRow],我们自己的矩阵类IndexMatrix 原先也是extends IndexedRowMatrix,所以其成员rows也是RDD[IndexedRow] 。但是由于RDD的不可变特性,当我试图用RDD[IndexRow]来初始化IndexMatrix时,会出现编译不通过的错误,见下图:
关于这个问题,Spark的jira上有过讨论Make RDDs Covariant,结论是不会fix这个问题。另外,mllib调用了breeze去进行矩阵运算,但是相关toBreeze等函数被写成了private [mllib] 私有的,无法从其他类访问到,综上两点我的替代方案就是直接将mllib中的相关源码拷贝过来,对其进行重写。
受限制于JVM的先天不足,基于Java的矩阵运算速度远远比不上本地fortran和C代码实现的矩阵库,比如BLAS,Lapack这种,而MLlib则采用了下面的结构调用关系
注意右边,用scala实现的Breeze去调用Netlib-java,而Netlb-java再去调用本地的BLAS和Lapack,以获得接近本地Native code运算矩阵的效果。所以我们这里也与MLlib一样使用Breeze。
不过可惜的是,由于集群的环境有点旧,而Netlib-java其Jar包编译时用的gfortran版本是1.4,系统的gfortran版本比较低,导致Netlib-java无法调用本地的BLAS,所以实际上目前我们的本地乘法仍然是用作为备用策略的java实现。
目前我已经完成并提供矩阵操作的基本API,其列表如下:
注意 获取给定范围行和列时,上下界参数都是包含关系
在保证程序运行正确的基础上,我对比了单机breeze和我的程序运行时间,在对两个分别是860MB的文本矩阵文件(每个均是1万乘1万的float型)进行乘法时,前者从读文件到计算写文件需要4个小时左右,而我们的程序在11分钟30秒左右,下面是一次运行的stage监控情况
其中耗时9分钟的stage是执行乘法的阶段,下一步当解决调用本地库进行矩阵运算后,其速度会大大加快。