5 min read

webR performance

The webR project is an innovative version of R that has been compiled for the browser and Node.js using WebAssembly, which is a low-level language used for the web. This means that users can run R code directly in their web browser without requiring an R server to execute the code.

The annoucement demonstrated the basic functionality of webR including the capability to emulate an R REPL in web browser, execute R code, load R packages, and plot data. Further, it also provides simple examples of how to embed webR in web pages. The documentation provides a detailed introduction to the project.

In this post, I would like to do some simple benchmark to get a rough impression of the performance of webR at the moment. These benchmarks are all done on the same machine, a MacBook Pro (2.3 GHz 8-Core Intel Core i9, 16 GB 2667 MHz DDR4, macOS 13.2.1), and webR is running at https://webr.r-wasm.org/latest/ in Firefox 111.0.

To make things simple, I just use system.time() for each example.

Example 1: Basic arithmetic operations

The first example is to do some basic arithmetic operations. The code is as follows:

# Generate two random vectors of length 10^7
set.seed(123)
x <- rnorm(10^7)
y <- rnorm(10^7)

# Benchmark the time it takes to add the two vectors
system.time({
  for (i in 1:100) {
    z <- x + y
  }
})

Native R

   user  system elapsed
  2.117   0.608   2.728

webR

   user  system elapsed 
  0.000   0.000   2.493 

Example 2: Matrix multiplication

The second example is to do matrix multiplication. The code is as follows:

# Generate two random matrices of size 1000 x 1000
set.seed(123)
x <- matrix(rnorm(1000^2), ncol = 1000)
y <- matrix(rnorm(1000^2), ncol = 1000)

# Benchmark the time it takes to multiply the two matrices
system.time(z <- x %*% y)

Native R

   user  system elapsed
  0.557   0.001   0.559

webR

   user  system elapsed 
   0.00    0.00    1.96 

Example 3: Sorting a large vector

The third example is to sort a large vector. The code is as follows:

# Generate a random vector of length 10^7
set.seed(123)
x <- rnorm(10^7)

# Benchmark the time it takes to sort the vector
system.time(sort(x))

Native R

   user  system elapsed
  0.711   0.012   0.726

webR

   user  system elapsed 
  0.000   0.000   1.143 

Example 4: Running a simulation

The fourth example is to run a simulation of coin flips. The code is as follows:

# Define a function to simulate a coin flip
simulate_coin_flip <- function(n_flips) {
  outcomes <- sample(c("heads", "tails"), n_flips, replace = TRUE)
  sum(outcomes == "heads")
}

# Run a simulation with 100 million coin flips
system.time({
  result <- simulate_coin_flip(1e8)
})

Native R

   user  system elapsed
  4.849   0.728   5.854

webR

   user  system elapsed 
   0.00    0.00    7.31 

Example 5: Generating random numbers from a distribution

# Generate 10 million random numbers from a normal distribution
system.time({
  result <- rnorm(1e7)
})

Native R

   user  system elapsed
  0.563   0.021   0.587

webR

   user  system elapsed 
  0.000   0.000   0.613 

Example 6: Merging two large data frames

# Generate two data frames with 1 million rows each
set.seed(123)
df1 <- data.frame(id = 1:1e6, value1 = rnorm(1e6))
df2 <- data.frame(id = 1:1e6, value2 = rnorm(1e6))

# Benchmark the time it takes to merge the data frames
system.time(merge(df1, df2))

Native R

   user  system elapsed
  0.729   0.036   0.768

webR

   user  system elapsed 
  0.000   0.000   1.326 

Example 7: Finding the maximum value in a large vector

# Generate a random vector of length 10^7
set.seed(123)
x <- rnorm(10^7)

# Benchmark the time it takes to find the maximum value
system.time(max(x))

Native R

   user  system elapsed
  0.222   0.001   0.224

webR

   user  system elapsed 
   0.00    0.00    0.23

Example 8: Calculating quantiles of a large vector

# Generate a random vector of length 10^7
set.seed(123)
x <- rnorm(10^7)

# Benchmark the time it takes to find the maximum value
system.time(quantile(x, seq(0, 1, 0.05)))

Native R

   user  system elapsed
  1.039   0.035   1.076

webR

   user  system elapsed
  0.000   0.000   1.143

Example 9: Fitting a linear regression model

# Generate two random vectors of length 10^6
set.seed(123)
x <- rnorm(10^6)
y <- rnorm(10^6)
z <- rnorm(10^6)

# Benchmark the time it takes to fit a linear regression model
system.time(lm(z ~ x + y))

Native R

   user  system elapsed
  0.226   0.055   0.282

webR

   user  system elapsed 
  0.000   0.000   0.268

Example 10: Generating a large list of random numbers

# Generate a list of 1 million items of 10 random numbers
system.time({
  mylist <- lapply(1:1e6, function(x) rnorm(10))
})

Native R

   user  system elapsed
  3.499   0.332   3.854

webR

   user  system elapsed 
  0.000   0.000   9.485

Example 11: Sorting a large vector

# Generate a random vector of length 10^7
set.seed(123)
x <- rnorm(10^7)

# Benchmark the time it takes to sort the vector
system.time(sort(x))

Native R

   user  system elapsed
  0.770   0.084   0.857

webR

   user  system elapsed 
  0.000   0.000   1.158

Example 12: Inverting a large matrix

# Generate a random matrix of size 1000 x 1000
set.seed(123)
x <- matrix(rnorm(1000^2), ncol = 1000)

# Benchmark the time it takes to invert the matrix
system.time(solve(x))

Native R

   user  system elapsed
  1.610   0.010   2.046

webR

   user  system elapsed 
  0.000   0.000   2.321

Conclusion

From the very rough benchmark above, it looks like webR and native R are mostly comparable in terms of performance in most examples above. However, there are some examples where webR is noteably slower than native R. For example, in the example of running a simulation of coin flips, webR is about 1.5 times slower than native R. In the example of generating a large list of random numbers, webR is about 3 times slower than native R. These two examples both involve intensive allocation of R objects, which might be not yet optimized in webR. But, it is already extremely promising given that webR is still in its early development stage.

The webR-quarto-demos and the JupterLite instance with the webR kernel are amazing demos. I’m considering if we could embed webR in vscode-R extension so that we don’t need a native R installation to provide some basic code editing features.

I’m looking forward to seeing more demos and applications of webR.