Quicker Elementwise Matrix-Multiplications

How much quicker do you need the element-wise multiplication to be?

On my system it takes on the order of 1/8 of a second to do this operation,

n <- 3.5e5
p <- 120
set.seed(123) 
x <- matrix(runif(n * p), nrow = n)
y <- matrix(runif(n * p), nrow = n)
format(object.size(x), "MB")
#> [1] "320.4 Mb"
library(microbenchmark)
mbr <- microbenchmark(
  R   = x * y
)
mbr
#> Unit: milliseconds
#>  expr      min       lq     mean median       uq     max neval
#>     R 106.9622 108.3356 124.0534 123.71 137.5286 153.561   100

Created on 2020-09-12 by the reprex package (v0.3.0)

How many times are you doing this element-wise matrix multiplication?

Even if the average time to do a single multiplication was 1 ns, doing 42 million of these multiplications would take 42 milliseconds a three-fold increase in speed over the {base} solution.

I do have C++ code which is a little faster than {base} at the expense of overwriting one of the input matrices. I'm on mobile right now, so I'll update this post with that code when I get home.

EDIT: Here is the faster C++ code,

library(microbenchmark)
n <- 3.5e5
p <- 120
set.seed(123) 
.x <-  matrix(runif(n * p), nrow = n)
.y <-  matrix(runif(n * p), nrow = n)

This code is adapted from a solution found here:
https://stackoverflow.com/a/19920823
I've removed the creation of a new storage object to facilitate speeding up the function call.

Rcpp::cppFunction("NumericVector multMat(NumericMatrix m1, NumericVector m2) {
  // modified from code found at:
  // https://stackoverflow.com/a/19920823
  m2 = m1 * m2;
  return m2;
}")

(mbr <- microbenchmark(
  R     = x * y,
  RCPP  = multMat(x, y),
  times = 100,
  setup =  y <- .y
))
#> Unit: milliseconds
#>  expr      min        lq     mean    median        uq      max neval
#>     R 106.8269 108.56660 124.9005 125.45150 138.30920 169.7719   100
#>  RCPP  58.9489  59.62685  60.8673  60.10355  61.40645  73.7025   100

Created on 2020-09-12 by the reprex package (v0.3.0)
A word of warning, the y object will be overwritten by the output object using this code. So it is only useful if you no longer need the original matrix after performing the multiplication. I had to include the setup = argument in the microbenchmark() call to reset the y object to its original state.

1 Like