在for循环RcppArmadillo中优化矩阵乘法

时间:2020-06-20 13:07:36

标签: r matrix-multiplication rcpparmadillo nmf

目标是在R中实现正交投影非负矩阵分解(opnmf)的快速版本。我正在翻译可用的here的matlab代码。

我实现了Vanilla R版本,但是它比20因子解决方案在我的数据上的Matlab实现(〜225000 x 150)要慢得多(慢5.5倍)。

因此,我认为使用c ++可能会加快速度,但其速度与R相似。我认为这可以进行优化,但不确定如何成为c ++的新手。 Here是讨论类似问题的主题。

这是我的RcppArmadillo实现。

// [[Rcpp::export]]
Rcpp::List arma_opnmf(const arma::mat & X, const arma::mat & W0, double tol=0.00001, int maxiter=10000, double eps=1e-16) {
  arma::mat W = W0;
  arma::mat Wold = W;
  arma::mat XXW = X * (X.t()*W);
  double diffW = 9999999999.9;
  
  Rcout << "The value of maxiter : " << maxiter << "\n";
  Rcout << "The value of tol : " << tol << "\n";
  
  int i;
  for (i = 0; i < maxiter; i++) {
    XXW = X * (X.t()*W);
    W = W % XXW / (W  * (W.t() * XXW));
    //W = W % (X*(X.t()*W)) / (W*((W.t()*X)*(X.t()*W)));
    
    arma::uvec idx = find(W < eps);
    W.elem(idx).fill(eps);
    W = W / norm(W,2);
    diffW = norm(Wold-W, "fro") / norm(Wold, "fro");
    if(diffW < tol) {
      break;
    } else {
      Wold = W;
    }
    
    if(i % 10 == 0) {
      Rcpp::checkUserInterrupt();
    }
    
  }
  return Rcpp::List::create(Rcpp::Named("W")=W,
                            Rcpp::Named("iter")=i,
                            Rcpp::Named("diffW")=diffW);
}

suggested issue证实了matlab相当快,因此使用R / c ++时没有希望吗?

测试是在Windows 10和Ubuntu 16. R版本4.0.0上进行的。

编辑

在下面的答案中有趣的评论之后。我要发布其他详细信息。我在带有R 3.5.3的Windows 10计算机上进行了测试(这就是Microsoft提供的功能),比较结果表明,带有Microsoft R的RcppArmadillo最快。

R

   user  system elapsed 
 213.76    7.36  221.42 

R与RcppArmadillo

   user  system elapsed 
 179.88    3.44  183.43 

Microsoft的Open R

   user  system elapsed 
 167.33    9.96   45.94 

微软与RcppArmadillo的公开赛

    user  system elapsed 
  85.47    4.66   23.56 

1 个答案:

答案 0 :(得分:3)

您是否知道这段代码是由一对称为LAPACK和BLAS的库“最终”执行的?

您是否知道Matlab附带高度优化的产品?您是否知道在R上运行的所有系统上,您都可以更改所使用的LAPACK / BLAS。

差异非常重要。就在今天早上,一个朋友发布了此tweet,对比了在同一Windows计算机上但在两种不同R环境中运行的相同R代码。 快6倍一个“简单”地使用并行LAPACK / BLAS实现。

在这里,您甚至没有告诉我们您所使用的操作系统。您可以为运行R的所有操作系统获取OpenBLAS(使用并行性)。您甚至可以在某些操作系统上相当容易地获得Intel MKL(MIRC也使用了IIRC)。对于Ubuntu / Debian,我发布了a script on GitHub,只需一步即可完成。

最后,很多年前,我“继承”了在一台(当时很大的)Windows计算机上在Matlab中运行的快速程序。我使用RcppArmadillo重写了C ++中的Matlab部分(仔细而缓慢地努力),这导致了一些改进因素-并且因为我们可以在同一台计算机上与R并行地运行该代码(现在为开源),还有其他一些因素。总的来说,数量级的变化将​​一天的模拟变成了几分钟的运行。所以“是的,你可以。”

编辑:您可以访问Ubuntu,可以通过一个命令从基本的LAPACK / BLAS切换到OpenBLAS,尽管我对Ubuntu 16.04不再那么熟悉(因为我自己运行20.04) )。

编辑2:Josef's tweet进行比较,我也是维护者(作为r-base的一部分)的Docker Rocker Project容器可以使用OpenBLAS。 [1]因此,一旦我们通过 apt-get install libopenblas-dev添加了它们,例如 ,一个简单的重复矩阵叉积的时间就从

root@0eb44b1fcc06:/# Rscript -e 'v <- matrix(1:1e6,1e3); system.time(replicate(10, crossprod(v,v)))'
   user  system elapsed 
  9.289   0.084   9.373 
root@0eb44b1fcc06:/# 

root@67bd334f53d4:/# Rscript -e 'v <- matrix(1:1e6,1e3); system.time(replicate(10, crossprod(v,v)))'
   user  system elapsed 
  2.259   2.370   0.447 
root@67bd334f53d4:/#

这很重要。