Benchmark

Michel Lang

2016-10-04

This small benchmark compares the performance of the base64 encoding/decoding in package base64url with the implementations in the packages base64enc and openssl.

Encoding of a single string

library(base64url)
library(base64enc)
library(openssl)
library(microbenchmark)

x = "plain text"
microbenchmark(
  base64url = base64_urlencode(x),
  base64enc = base64encode(charToRaw(x)),
  openssl = base64_encode(x)
)
## Unit: nanoseconds
##       expr  min     lq     mean median     uq    max neval cld
##  base64url  598  719.5  1222.75  861.5  961.5  28170   100 a  
##  base64enc 3252 3409.0  5345.50 3920.0 4218.0 113086   100  b 
##    openssl 8964 9305.5 10912.67 9465.5 9980.0  64796   100   c

Decoding of a single string

x = "N0JBLlRaUTp1bi5KOW4xWStNWEJoLHRQaDZ3"
microbenchmark(
  base64url = base64_urldecode(x),
  base64enc = rawToChar(base64decode(x)),
  openssl = rawToChar(base64_decode(x))
)
## Unit: nanoseconds
##       expr   min      lq     mean  median      uq    max neval cld
##  base64url   725   833.0  1365.00  1115.5  1310.5  15764   100 a  
##  base64enc  4804  5177.0  7419.04  5835.0  6234.5  64962   100  b 
##    openssl 18412 18933.5 21839.90 19340.5 20524.5 125189   100   c

Encoding and decoding of character vectors

Here, the task has changed from encoding/decoding a single string to processing multiple strings stored inside a character vector. First, we create a small utility function which returns n random strings with a random number of characters (between 1 and 32) each.

rand = function(n, min = 1, max = 32) {
  chars = c(letters, LETTERS, as.character(0:9), c(".", ":", ",", "+", "-", "*", "/"))
  replicate(n, paste0(sample(chars, sample(min:max, 1), replace = TRUE), collapse = ""))
}
set.seed(1)
rand(10)
##  [1] "zN.n9+TRe"                     "mVA1IX/"                      
##  [3] "1,oSisAaA8xHP"                 "m5U2hXC4S2MK2bGY"             
##  [5] "G7EqegvJTC.uFwSrH0f8x5x"       "G97A1-DXBw0"                  
##  [7] "XiqjqeS"                       "13FC3PTys/RoiG:P*YyDkaXhES/IH"
##  [9] "0FJopP"                        "fcS,PMK*JVPqrYFmZh7"

Only base64url is vectorized for string input, the alternative implementations need wrappers to process character vectors:

base64enc_encode = function(x) {
  vapply(x, function(x) base64encode(charToRaw(x)), NA_character_, USE.NAMES = FALSE)
}

openssl_encode = function(x) {
  vapply(x, function(x) base64_encode(x), NA_character_, USE.NAMES = FALSE)
}

base64enc_decode = function(x) {
  vapply(x, function(x) rawToChar(base64decode(x)), NA_character_, USE.NAMES = FALSE)
}

openssl_decode = function(x) {
  vapply(x, function(x) rawToChar(base64_decode(x)), NA_character_, USE.NAMES = FALSE)
}

The following benchmark measures the runtime to encode 1000 random strings and then decode them again:

set.seed(1)
x = rand(1000)
microbenchmark(
  base64url = base64_urldecode(base64_urlencode(x)),
  base64enc = base64enc_decode(base64enc_encode(x)),
  openssl = openssl_decode(openssl_encode(x))
)
## Unit: microseconds
##       expr       min         lq       mean     median        uq       max
##  base64url   215.029   222.9475   244.0447   238.1795   257.464   358.599
##  base64enc  7185.749  7349.0650  7951.7801  7927.6675  8348.159 10157.637
##    openssl 21620.231 22093.2525 23687.5476 22828.9190 23330.632 86346.942
##  neval cld
##    100 a  
##    100  b 
##    100   c