Author: Michel Lang
Date: 2018-05-14

# Benchmark

#### Michel Lang

#### 2018-05-14

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
    ##  base64url   408   461.5   853.52   698.0   782.5  20430   100
    ##  base64enc  1199  1367.0  3114.89  1865.0  1983.5 139937   100
    ##    openssl 13188 13536.0 15256.11 13749.5 14081.5 135871   100

## 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
    ##  base64url   423   485.0  1104.57   655.5   923  40442   100
    ##  base64enc  1504  1681.5  2879.92  2225.5  2546  76432   100
    ##    openssl 19066 19643.0 21264.27 19915.5 20281 119636   100

## 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   214.031   222.0655   270.415   260.3725   311.967   441.127
    ##  base64enc  3613.444  3974.8940  4228.395  4058.2495  4157.485 11094.540
    ##    openssl 34166.572 35453.3810 36002.175 35794.9835 36349.533 42507.878
    ##  neval
    ##    100
    ##    100
    ##    100