R中的快速大矩阵乘法

时间:2016-03-10 17:52:54

标签: r matrix

我在R中有两个矩阵我想要乘法:

a = matrix(rnorm(20*10000, mean=0, sd=5), 20, 10000)
b = matrix(rnorm(20*10000, mean=0, sd=5), 20, 10000)
t(a)%*%b

鉴于此矩阵乘法中较大的维数需要花费大量时间,是否有一种特定的方法可以更快地进行计算? R中是否有任何内置功能可以使这种乘法更快?

2 个答案:

答案 0 :(得分:19)

根据您的代码,工作量和硬件,有很多方法可以解决这个问题。

  1. 使用' best'工作职能
  2. 最简单的方法是使用与crossprod相同的t(a)%*% b(注意 - 速度只会略微增加)

    crossprod(a,b) 
    
    1. 使用Rcpp(可能RcppEigen / RcppArmadillo)。
    2. C ++可能会更大程度地提高代码的速度。使用线性代数库可能也会进一步帮助(因此Eigen和Armadillo)。但是,假设您愿意编写一些C ++。

      1. 使用更好的后端
      2. 在此之后,您正在查看BLAS后端,例如OpenBLAS,Atlas等。根据您的操作系统,将这些连接到R会有所不同。如果你使用像Ubuntu这样的Debian系统,这很容易。您可以找到演示here。 Armadillo和Eigen等图书馆有时可以进一步利用这些资源。

        1. GPU计算
        2. 如果你有GPU(例如AMD,NVIDIA等),你可以利用其中的许多内核来大大加快你的计算速度。有一些可能有用,包括gpuRgputoolsgmatrix

          编辑 - 致电@jenesaiquoi关于Rcpp利益的评论

          TEST.CPP

          // [[Rcpp::depends(RcppArmadillo, RcppEigen)]]
          
          #include <RcppArmadillo.h>
          #include <RcppEigen.h>
          
          // [[Rcpp::export]]
          SEXP armaMatMult(arma::mat A, arma::mat B){
              arma::mat C = A * B;
          
              return Rcpp::wrap(C);
          }
          
          // [[Rcpp::export]]
          SEXP eigenMatMult(Eigen::MatrixXd A, Eigen::MatrixXd B){
              Eigen::MatrixXd C = A * B;
          
              return Rcpp::wrap(C);
          }
          
          // [[Rcpp::export]]
          SEXP eigenMapMatMult(const Eigen::Map<Eigen::MatrixXd> A, Eigen::Map<Eigen::MatrixXd> B){
              Eigen::MatrixXd C = A * B;
          
              return Rcpp::wrap(C);
          }
          

          test.R

          library(Rcpp)
          
          A <- matrix(rnorm(10000), 100, 100)
          B <- matrix(rnorm(10000), 100, 100)
          
          library(microbenchmark)
          sourceCpp("test.cpp")
          microbenchmark(A%*%B, armaMatMult(A, B), eigenMatMult(A, B), eigenMapMatMult(A, B))
          
          Unit: microseconds
                            expr     min       lq     mean   median       uq      max neval
                         A %*% B 885.846 892.1035 933.7457 901.1010 938.9255 1411.647   100
               armaMatMult(A, B) 846.688 857.6320 915.0717 866.2265 893.7790 1421.557   100
              eigenMatMult(A, B) 205.978 208.1295 233.1882 217.0310 229.4730  369.369   100
           eigenMapMatMult(A, B) 192.366 194.9835 207.1035 197.5405 205.2550  366.945   100
          

答案 1 :(得分:1)

添加到 cdeerman 的答案中: 您可以将 eigen 的构建并行化用于密集矩阵产品。为此,您需要在激活 open mp 的情况下进行编译。

// [[Rcpp::depends(RcppArmadillo, RcppEigen)]]
// [[Rcpp::plugins(openmp)]]

#include <omp.h>
#include <RcppArmadillo.h>
#include <RcppEigen.h>

// [[Rcpp::export]]
SEXP armaMatMult(arma::mat A, arma::mat B){
  arma::mat C = A * B;
  
  return Rcpp::wrap(C);
}

// [[Rcpp::export]]
SEXP eigenMatMult(Eigen::MatrixXd A, 
                  Eigen::MatrixXd B, 
                  int n_cores){
  
  Eigen::setNbThreads(n_cores);
  //qDebug()  << Eigen::nbThreads( );
  Eigen::MatrixXd C = A * B;
  
  return Rcpp::wrap(C);
}

// [[Rcpp::export]]
SEXP eigenMapMatMult2(const Eigen::Map<Eigen::MatrixXd> A,
                      Eigen::Map<Eigen::MatrixXd> B, 
                      int n_cores){
  
  Eigen::setNbThreads(n_cores);
  Eigen::MatrixXd C = A * B;
  return Rcpp::wrap(C);
}

以下是一些基准:

请注意,如果 N = k = 100,并行化不一定会提高性能。 如果矩阵维度变大,并行化开始产生影响 (N = k = 1000):

library(microbenchmark)

# Benchmark 1: N = k = 100

N <- 100
k <- 100

A <- matrix(rnorm(N*k), N, k)
B <- matrix(rnorm(N*k), k, N)

microbenchmark(A%*%B, 
               armaMatMult2(A, B),
               eigenMatMult2(A, B, n_cores = 1),
               eigenMatMult2(A, B, n_cores = 2),
               eigenMatMult2(A, B, n_cores = 4),
               eigenMapMatMult2(A, B, n_cores = 1),
               eigenMapMatMult2(A, B, n_cores = 2),
               eigenMapMatMult2(A, B, n_cores = 4), 
               times = 100

# Unit: microseconds
#                                 expr   min     lq    mean median     uq   max neval
#                              A %*% B 535.6 540.75 552.594 551.25 554.50 650.2   100
#                   armaMatMult2(A, B) 542.0 549.10 560.975 556.35 560.25 738.1   100
#     eigenMatMult2(A, B, n_cores = 1) 147.1 152.65 159.165 159.65 162.90 180.5   100
#     eigenMatMult2(A, B, n_cores = 2)  97.1 109.90 124.496 119.60 127.50 391.8   100
#     eigenMatMult2(A, B, n_cores = 4)  71.7  88.15 155.220 115.55 216.95 507.3   100
#  eigenMapMatMult2(A, B, n_cores = 1) 139.1 150.10 154.889 154.20 158.35 244.3   100
#  eigenMapMatMult2(A, B, n_cores = 2)  93.4 105.70 116.808 113.55 120.40 323.7   100
#  eigenMapMatMult2(A, B, n_cores = 4)  66.8  82.60 161.516 196.25 210.40 598.9   100
)

# Benchmark 2: N = k = 1000

N <- 1000
k <- 1000

A <- matrix(rnorm(N*k), N, k)
B <- matrix(rnorm(N*k), k, N)

microbenchmark(A%*%B, 
                armaMatMult2(A, B),
                eigenMatMult2(A, B, n_cores = 1),
                eigenMatMult2(A, B, n_cores = 2),
                eigenMatMult2(A, B, n_cores = 4),
               eigenMapMatMult2(A, B, n_cores = 1),
               eigenMapMatMult2(A, B, n_cores = 2),
               eigenMapMatMult2(A, B, n_cores = 4), 
               times = 100
)


Unit: milliseconds
                                expr      min        lq      mean    median        uq
                             A %*% B 597.1293 605.56840 814.52389 665.86650 1025.5896
                  armaMatMult2(A, B) 603.3894 620.25675 830.98947 693.22355 1078.4853
    eigenMatMult2(A, B, n_cores = 1) 131.4696 135.22475 186.69826 193.37870  219.8727
    eigenMatMult2(A, B, n_cores = 2)  67.8948  71.71355 114.52759  74.17380  173.3060
    eigenMatMult2(A, B, n_cores = 4)  41.8564  48.87075  79.55535  72.00705  106.8572
 eigenMapMatMult2(A, B, n_cores = 1) 125.3890 129.26125 175.09933 177.23655  213.0536
 eigenMapMatMult2(A, B, n_cores = 2)  62.2866  65.78785 115.74248  79.92470  167.0217
 eigenMapMatMult2(A, B, n_cores = 4)  35.2977  40.42480  68.21669  63.13655   97.2571
       max neval
 1217.6475   100
 1446.5127   100
  419.2043   100
  217.9513   100
  139.9629   100
  298.2859   100
  230.6307   100
  118.2553   100