Skip to content

$output function (x, options) { if (class == “output” && output_asis(x, options)) return(x) hook.t(x, options[[paste0(“attr.”, class)]], options[[paste0(“class.”, class)]]) } <bytecode: 0x556c5d494780> <environment: 0x556c5def23b0>

Introduction

Often we can use the corresponding base R function as a baseline. We also compare to the fansi package, where it is possible.

Data

In cli the typical use case is short string scalars, but we run some benchmarks longer strings and string vectors as well.

library(cli)
library(fansi)
options(cli.unicode = TRUE)
options(cli.num_colors = 256)
ansi <- format_inline(
  "{col_green(symbol$tick)} {.code print(x)} {.emph emphasised}"
)
plain <- ansi_strip(ansi)
vec_plain <- rep(plain, 100)
vec_ansi <- rep(ansi, 100)
vec_plain6 <- rep(plain, 6)
vec_ansi6 <- rep(plain, 6)
txt_plain <- paste(vec_plain, collapse = " ")
txt_ansi <- paste(vec_ansi, collapse = " ")
uni <- paste(
  "\U0001f477\u200d\u2640\ufe0f",
  "\U0001f477\U0001f3fb",
  "\U0001f477\u200d\u2640\ufe0f",
  "\U0001f477\U0001f3fb",
  "\U0001f477\U0001f3ff\u200d\u2640\ufe0f"
)
vec_uni <- rep(uni, 100)
txt_uni <- paste(vec_uni, collapse = " ")

ANSI functions

ansi_align()

bench::mark(
  ansi  = ansi_align(ansi, width = 20),
  plain = ansi_align(plain, width = 20), 
  base  = format(plain, width = 20),
  check = FALSE
)
#> # A tibble: 3 × 6
#>   expression      min   median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#> 1 ansi         46.1µs   49.8µs    19526.    99.3KB     18.9
#> 2 plain        46.3µs   49.9µs    19557.        0B     19.4
#> 3 base         11.3µs   12.5µs    77942.    48.4KB     15.6
bench::mark(
  ansi  = ansi_align(ansi, width = 20, align = "right"),
  plain = ansi_align(plain, width = 20, align = "right"), 
  base  = format(plain, width = 20, justify = "right"),
  check = FALSE
)
#> # A tibble: 3 × 6
#>   expression      min   median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#> 1 ansi         48.2µs     52µs    18730.        0B     21.1
#> 2 plain        47.9µs   51.3µs    18937.        0B     21.2
#> 3 base         13.3µs   14.3µs    68127.        0B     20.4

ansi_chartr()

bench::mark(
  ansi  = ansi_chartr("abc", "XYZ", ansi),
  plain = ansi_chartr("abc", "XYZ", plain),
  base  = chartr("abc", "XYZ", plain),
  check = FALSE
)
#> # A tibble: 3 × 6
#>   expression      min   median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#> 1 ansi        112.9µs 119.35µs     8173.   75.07KB     16.8
#> 2 plain        89.2µs  94.22µs    10285.    8.73KB     12.4
#> 3 base          1.9µs   2.03µs   474480.        0B     47.5

ansi_columns()

bench::mark(
  ansi  = ansi_columns(vec_ansi6, width = 120),
  plain = ansi_columns(vec_plain6, width = 120),
  check = FALSE
)
#> # A tibble: 2 × 6
#>   expression      min   median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#> 1 ansi          340µs    363µs     2727.   33.17KB     19.1
#> 2 plain         342µs    364µs     2714.    1.09KB     19.1

ansi_has_any()

bench::mark(
  cli_ansi        = ansi_has_any(ansi),
  fansi_ansi      = has_sgr(ansi),
  cli_plain       = ansi_has_any(plain),
  fansi_plain     = has_sgr(plain),
  cli_vec_ansi    = ansi_has_any(vec_ansi),
  fansi_vec_ansi  = has_sgr(vec_ansi),
  cli_vec_plain   = ansi_has_any(vec_plain),
  fansi_vec_plain = has_sgr(vec_plain),
  cli_txt_ansi    = ansi_has_any(txt_ansi),
  fansi_txt_ansi  = has_sgr(txt_ansi),
  cli_txt_plain   = ansi_has_any(txt_plain),
  fansi_txt_plain = has_sgr(vec_plain),
  check = FALSE
)
#> # A tibble: 12 × 6
#>    expression           min   median `itr/sec` mem_alloc `gc/sec`
#>    <bch:expr>      <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#>  1 cli_ansi          5.95µs    6.5µs   148698.     9.2KB     29.7
#>  2 fansi_ansi       30.89µs  33.59µs    28794.    4.18KB     23.1
#>  3 cli_plain         5.91µs   6.36µs   152801.        0B     30.6
#>  4 fansi_plain       30.2µs  32.75µs    29857.      688B     23.9
#>  5 cli_vec_ansi      7.23µs   7.68µs   127459.      448B     25.5
#>  6 fansi_vec_ansi   39.09µs  40.97µs    23698.    5.02KB     19.0
#>  7 cli_vec_plain     7.92µs   8.33µs   116928.      448B     23.4
#>  8 fansi_vec_plain  37.96µs  39.82µs    24501.    5.02KB     19.6
#>  9 cli_txt_ansi      5.78µs   6.25µs   155952.        0B     31.2
#> 10 fansi_txt_ansi   30.39µs  31.97µs    30537.      688B     24.4
#> 11 cli_txt_plain     6.75µs   7.11µs   136738.        0B     27.4
#> 12 fansi_txt_plain  37.86µs  39.69µs    24592.    5.02KB     19.7

ansi_html()

This is typically used with longer text.

bench::mark(
  cli   = ansi_html(txt_ansi),
  fansi = sgr_to_html(txt_ansi, classes = TRUE),
  check = FALSE
)
#> # A tibble: 2 × 6
#>   expression      min   median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#> 1 cli            59µs   60.4µs    15886.    22.7KB     8.17
#> 2 fansi         116µs  118.3µs     8301.    55.3KB    10.3

ansi_nchar()

bench::mark(
  cli_ansi        = ansi_nchar(ansi),
  fansi_ansi      = nchar_sgr(ansi),
  base_ansi       = nchar(ansi),
  cli_plain       = ansi_nchar(plain),
  fansi_plain     = nchar_sgr(plain),
  base_plain      = nchar(plain),
  cli_vec_ansi    = ansi_nchar(vec_ansi),
  fansi_vec_ansi  = nchar_sgr(vec_ansi),
  base_vec_ansi   = nchar(vec_ansi),
  cli_vec_plain   = ansi_nchar(vec_plain),
  fansi_vec_plain = nchar_sgr(vec_plain),
  base_vec_plain  = nchar(vec_plain),
  cli_txt_ansi    = ansi_nchar(txt_ansi),
  fansi_txt_ansi  = nchar_sgr(txt_ansi),
  base_txt_ansi   = nchar(txt_ansi),
  cli_txt_plain   = ansi_nchar(txt_plain),
  fansi_txt_plain = nchar_sgr(txt_plain),
  base_txt_plain  = nchar(txt_plain),
  check = FALSE
)
#> # A tibble: 18 × 6
#>    expression           min   median `itr/sec` mem_alloc `gc/sec`
#>    <bch:expr>      <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#>  1 cli_ansi          6.67µs   7.21µs   132919.        0B    26.6 
#>  2 fansi_ansi       92.27µs  96.67µs    10077.   38.83KB    16.7 
#>  3 base_ansi       911.07ns 952.04ns   997869.        0B     0   
#>  4 cli_plain          6.8µs   7.31µs   133299.        0B    26.7 
#>  5 fansi_plain      91.69µs  95.84µs    10078.      688B    16.7 
#>  6 base_plain      831.09ns 872.07ns  1090322.        0B     0   
#>  7 cli_vec_ansi     28.57µs  29.33µs    33542.      448B     6.71
#>  8 fansi_vec_ansi  112.54µs 116.78µs     8321.    5.02KB    12.5 
#>  9 base_vec_ansi    16.03µs  16.09µs    60781.      448B     0   
#> 10 cli_vec_plain    26.72µs  27.37µs    35931.      448B     7.19
#> 11 fansi_vec_plain 102.24µs 106.89µs     9119.    5.02KB    14.6 
#> 12 base_vec_plain    9.55µs   9.62µs   102174.      448B    10.2 
#> 13 cli_txt_ansi     27.96µs   28.7µs    34326.        0B     3.43
#> 14 fansi_txt_ansi   104.2µs 108.56µs     8888.      688B    14.5 
#> 15 base_txt_ansi    15.65µs   15.7µs    62868.        0B     6.29
#> 16 cli_txt_plain     26.2µs  26.83µs    36616.        0B     3.66
#> 17 fansi_txt_plain  93.23µs  98.43µs     9906.      688B    16.7 
#> 18 base_txt_plain     9.2µs   9.25µs   106643.        0B     0
bench::mark(
  cli_ansi        = ansi_nchar(ansi, type = "width"),
  fansi_ansi      = nchar_sgr(ansi, type = "width"),
  base_ansi       = nchar(ansi, "width"),
  cli_plain       = ansi_nchar(plain, type = "width"),
  fansi_plain     = nchar_sgr(plain, type = "width"),
  base_plain      = nchar(plain, "width"),
  cli_vec_ansi    = ansi_nchar(vec_ansi, type = "width"),
  fansi_vec_ansi  = nchar_sgr(vec_ansi, type = "width"),
  base_vec_ansi   = nchar(vec_ansi, "width"),
  cli_vec_plain   = ansi_nchar(vec_plain, type = "width"),
  fansi_vec_plain = nchar_sgr(vec_plain, type = "width"),
  base_vec_plain  = nchar(vec_plain, "width"),
  cli_txt_ansi    = ansi_nchar(txt_ansi, type = "width"),
  fansi_txt_ansi  = nchar_sgr(txt_ansi, type = "width"),
  base_txt_ansi   = nchar(txt_ansi, "width"),
  cli_txt_plain   = ansi_nchar(txt_plain, type = "width"),
  fansi_txt_plain = nchar_sgr(txt_plain, type = "width"),
  base_txt_plain  = nchar(txt_plain, type = "width"),
  check = FALSE
)
#> # A tibble: 18 × 6
#>    expression           min   median `itr/sec` mem_alloc `gc/sec`
#>    <bch:expr>      <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#>  1 cli_ansi          8.38µs   9.01µs   108066.        0B    21.6 
#>  2 fansi_ansi       93.27µs  97.34µs    10007.      688B    16.7 
#>  3 base_ansi         1.23µs   1.28µs   745904.        0B     0   
#>  4 cli_plain         8.25µs    8.9µs   107138.        0B    32.2 
#>  5 fansi_plain      92.59µs  96.98µs    10043.      688B    16.7 
#>  6 base_plain        1.01µs   1.06µs   911836.        0B     0   
#>  7 cli_vec_ansi     34.88µs  35.61µs    27657.      448B     5.53
#>  8 fansi_vec_ansi  120.04µs 124.59µs     7826.    5.02KB    12.5 
#>  9 base_vec_ansi    41.42µs  41.73µs    23733.      448B     0   
#> 10 cli_vec_plain    33.29µs  33.93µs    28973.      448B     8.69
#> 11 fansi_vec_plain 109.54µs 114.07µs     8552.    5.02KB    12.4 
#> 12 base_vec_plain   21.58µs  21.89µs    44559.      448B     4.46
#> 13 cli_txt_ansi     34.78µs  35.48µs    27709.        0B     5.54
#> 14 fansi_txt_ansi  112.12µs 116.47µs     8372.      688B    14.5 
#> 15 base_txt_ansi    43.86µs  44.45µs    22256.        0B     0   
#> 16 cli_txt_plain    32.89µs  33.52µs    29388.        0B     5.88
#> 17 fansi_txt_plain 100.82µs 105.34µs     9266.      688B    14.5 
#> 18 base_txt_plain   23.09µs  23.84µs    41480.        0B     4.15

ansi_simplify()

Nothing to compare here.

bench::mark(
  cli_ansi      = ansi_simplify(ansi),
  cli_plain     = ansi_simplify(plain),
  cli_vec_ansi  = ansi_simplify(vec_ansi),
  cli_vec_plain = ansi_simplify(vec_plain),
  cli_txt_ansi  = ansi_simplify(txt_ansi),
  cli_txt_plain = ansi_simplify(txt_plain),
  check = FALSE
)
#> # A tibble: 6 × 6
#>   expression         min   median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr>    <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#> 1 cli_ansi        6.82µs   7.31µs   133678.        0B    26.7 
#> 2 cli_plain       6.35µs    6.8µs   143791.        0B    14.4 
#> 3 cli_vec_ansi   33.64µs  34.52µs    28351.      848B     5.67
#> 4 cli_vec_plain  10.24µs  10.83µs    90259.      848B    18.1 
#> 5 cli_txt_ansi   32.79µs   33.8µs    28990.        0B     2.90
#> 6 cli_txt_plain   7.19µs   7.64µs   127629.        0B    25.5

ansi_strip()

bench::mark(
  cli_ansi        = ansi_strip(ansi),
  fansi_ansi      = strip_sgr(ansi),
  cli_plain       = ansi_strip(plain),
  fansi_plain     = strip_sgr(plain),
  cli_vec_ansi    = ansi_strip(vec_ansi),
  fansi_vec_ansi  = strip_sgr(vec_ansi),
  cli_vec_plain   = ansi_strip(vec_plain),
  fansi_vec_plain = strip_sgr(vec_plain),
  cli_txt_ansi    = ansi_strip(txt_ansi),
  fansi_txt_ansi  = strip_sgr(txt_ansi),
  cli_txt_plain   = ansi_strip(txt_plain),
  fansi_txt_plain = strip_sgr(txt_plain),
  check = FALSE
)
#> # A tibble: 12 × 6
#>    expression           min   median `itr/sec` mem_alloc `gc/sec`
#>    <bch:expr>      <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#>  1 cli_ansi          26.2µs   27.5µs    35348.        0B     28.3
#>  2 fansi_ansi        28.2µs   29.8µs    32640.    7.24KB     26.1
#>  3 cli_plain         25.6µs     27µs    36205.        0B     29.0
#>  4 fansi_plain       27.6µs   29.2µs    33336.      688B     26.7
#>  5 cli_vec_ansi      35.1µs   36.7µs    26524.      848B     18.6
#>  6 fansi_vec_ansi    53.6µs   55.5µs    17639.    5.41KB     14.7
#>  7 cli_vec_plain     28.8µs   30.3µs    32182.      848B     25.8
#>  8 fansi_vec_plain   36.6µs   38.7µs    25171.    4.59KB     20.2
#>  9 cli_txt_ansi      34.8µs   36.8µs    26535.        0B     21.2
#> 10 fansi_txt_ansi    44.8µs   46.5µs    21007.    5.12KB     16.6
#> 11 cli_txt_plain     26.8µs   28.2µs    34587.        0B     24.2
#> 12 fansi_txt_plain   28.8µs   30.5µs    31348.      688B     25.1

ansi_strsplit()

bench::mark(
  cli_ansi        = ansi_strsplit(ansi, "i"),
  fansi_ansi      = strsplit_sgr(ansi, "i"),
  base_ansi       = strsplit(ansi, "i"),
  cli_plain       = ansi_strsplit(plain, "i"),
  fansi_plain     = strsplit_sgr(plain, "i"),
  base_plain      = strsplit(plain, "i"),
  cli_vec_ansi    = ansi_strsplit(vec_ansi, "i"),
  fansi_vec_ansi  = strsplit_sgr(vec_ansi, "i"),
  base_vec_ansi   = strsplit(vec_ansi, "i"),
  cli_vec_plain   = ansi_strsplit(vec_plain, "i"),
  fansi_vec_plain = strsplit_sgr(vec_plain, "i"),
  base_vec_plain  = strsplit(vec_plain, "i"),
  cli_txt_ansi    = ansi_strsplit(txt_ansi, "i"),
  fansi_txt_ansi  = strsplit_sgr(txt_ansi, "i"),
  base_txt_ansi   = strsplit(txt_ansi, "i"),
  cli_txt_plain   = ansi_strsplit(txt_plain, "i"),
  fansi_txt_plain = strsplit_sgr(txt_plain, "i"),
  base_txt_plain  = strsplit(txt_plain, "i"),
  check = FALSE
)
#> # A tibble: 18 × 6
#>    expression           min   median `itr/sec` mem_alloc `gc/sec`
#>    <bch:expr>      <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#>  1 cli_ansi        166.12µs 174.72µs     5541.  104.34KB    19.1 
#>  2 fansi_ansi      127.72µs 134.59µs     7211.  106.35KB    19.1 
#>  3 base_ansi          4.2µs   4.55µs   214611.      224B    21.5 
#>  4 cli_plain       164.95µs 172.27µs     5654.    8.09KB    19.0 
#>  5 fansi_plain     126.45µs 132.22µs     7354.    9.62KB    19.1 
#>  6 base_plain        3.68µs   3.95µs   246360.        0B    24.6 
#>  7 cli_vec_ansi      7.56ms   7.74ms      129.  823.77KB    25.3 
#>  8 fansi_vec_ansi    1.05ms   1.09ms      891.  846.81KB    17.4 
#>  9 base_vec_ansi   157.37µs 163.64µs     5974.    22.7KB     4.11
#> 10 cli_vec_plain     7.47ms   7.62ms      131.  823.77KB    25.1 
#> 11 fansi_vec_plain 990.83µs   1.03ms      952.  845.98KB    19.8 
#> 12 base_vec_plain  106.96µs 112.02µs     8703.      848B     4.05
#> 13 cli_txt_ansi      3.43ms   3.47ms      288.    63.6KB     0   
#> 14 fansi_txt_ansi    1.55ms   1.58ms      632.   35.05KB     2.02
#> 15 base_txt_ansi   137.85µs  146.3µs     6778.   18.47KB     2.02
#> 16 cli_txt_plain     2.37ms   2.41ms      414.    63.6KB     0   
#> 17 fansi_txt_plain 519.69µs 535.79µs     1851.    30.6KB     6.17
#> 18 base_txt_plain   87.86µs  90.69µs    10768.   11.05KB     2.02

ansi_strtrim()

bench::mark(
  cli_ansi        = ansi_strtrim(ansi, 10),
  fansi_ansi      = strtrim_sgr(ansi, 10),
  base_ansi       = strtrim(ansi, 10),
  cli_plain       = ansi_strtrim(plain, 10),
  fansi_plain     = strtrim_sgr(plain, 10),
  base_plain      = strtrim(plain, 10),
  cli_vec_ansi    = ansi_strtrim(vec_ansi, 10),
  fansi_vec_ansi  = strtrim_sgr(vec_ansi, 10),
  base_vec_ansi   = strtrim(vec_ansi, 10),
  cli_vec_plain   = ansi_strtrim(vec_plain, 10),
  fansi_vec_plain = strtrim_sgr(vec_plain, 10),
  base_vec_plain  = strtrim(vec_plain, 10),
  cli_txt_ansi    = ansi_strtrim(txt_ansi, 10),
  fansi_txt_ansi  = strtrim_sgr(txt_ansi, 10),
  base_txt_ansi   = strtrim(txt_ansi, 10),
  cli_txt_plain   = ansi_strtrim(txt_plain, 10),
  fansi_txt_plain = strtrim_sgr(txt_plain, 10),
  base_txt_plain  = strtrim(txt_plain, 10),
  check = FALSE
)
#> # A tibble: 18 × 6
#>    expression           min   median `itr/sec` mem_alloc `gc/sec`
#>    <bch:expr>      <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#>  1 cli_ansi        149.71µs 158.04µs     6155.   33.84KB    23.6 
#>  2 fansi_ansi        54.8µs  57.93µs    16711.   31.43KB    23.2 
#>  3 base_ansi         1.05µs   1.12µs   848618.     4.2KB     0   
#>  4 cli_plain        146.5µs  152.4µs     6418.        0B    24.1 
#>  5 fansi_plain      54.41µs     57µs    17090.      872B    21.1 
#>  6 base_plain           1µs   1.04µs   912149.        0B    91.2 
#>  7 cli_vec_ansi    274.18µs 284.95µs     3457.   16.73KB    12.6 
#>  8 fansi_vec_ansi  122.59µs 126.57µs     7734.    5.59KB    10.3 
#>  9 base_vec_ansi    35.99µs  36.37µs    27232.      848B     0   
#> 10 cli_vec_plain   229.28µs 238.16µs     4113.   16.73KB    16.9 
#> 11 fansi_vec_plain 119.08µs 123.34µs     7943.    5.59KB    10.3 
#> 12 base_vec_plain   30.08µs  30.54µs    32285.      848B     3.23
#> 13 cli_txt_ansi    156.32µs 163.16µs     5973.        0B    21.0 
#> 14 fansi_txt_ansi   54.51µs  57.73µs    16904.      872B    23.4 
#> 15 base_txt_ansi     1.12µs   1.16µs   837015.        0B     0   
#> 16 cli_txt_plain   146.18µs 152.05µs     6437.        0B    23.1 
#> 17 fansi_txt_plain     54µs  57.66µs    16885.      872B    23.3 
#> 18 base_txt_plain    1.02µs   1.06µs   908614.        0B     0

ansi_strwrap()

This function is most useful for longer text, but it is often called for short text in cli, so it makes sense to benchmark that as well.

bench::mark(
  cli_ansi        = ansi_strwrap(ansi, 30),
  fansi_ansi      = strwrap_sgr(ansi, 30),
  base_ansi       = strwrap(ansi, 30),
  cli_plain       = ansi_strwrap(plain, 30),
  fansi_plain     = strwrap_sgr(plain, 30),
  base_plain      = strwrap(plain, 30),
  cli_vec_ansi    = ansi_strwrap(vec_ansi, 30),
  fansi_vec_ansi  = strwrap_sgr(vec_ansi, 30),
  base_vec_ansi   = strwrap(vec_ansi, 30),
  cli_vec_plain   = ansi_strwrap(vec_plain, 30),
  fansi_vec_plain = strwrap_sgr(vec_plain, 30),
  base_vec_plain  = strwrap(vec_plain, 30),
  cli_txt_ansi    = ansi_strwrap(txt_ansi, 30),
  fansi_txt_ansi  = strwrap_sgr(txt_ansi, 30),
  base_txt_ansi   = strwrap(txt_ansi, 30),
  cli_txt_plain   = ansi_strwrap(txt_plain, 30),
  fansi_txt_plain = strwrap_sgr(txt_plain, 30),
  base_txt_plain  = strwrap(txt_plain, 30),
  check = FALSE
)
#> # A tibble: 18 × 6
#>    expression           min   median `itr/sec` mem_alloc `gc/sec`
#>    <bch:expr>      <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#>  1 cli_ansi         400.9µs 423.16µs    2343.         0B     21.2
#>  2 fansi_ansi       97.31µs  102.5µs    9474.    97.33KB     21.3
#>  3 base_ansi        38.58µs  40.91µs   23111.         0B     20.8
#>  4 cli_plain       270.48µs 281.74µs    3486.         0B     19.2
#>  5 fansi_plain      94.55µs  99.93µs    9745.       872B     21.1
#>  6 base_plain       32.18µs   33.6µs   28979.         0B     20.3
#>  7 cli_vec_ansi     41.78ms  41.78ms      23.9    2.48KB    263. 
#>  8 fansi_vec_ansi  244.45µs 252.26µs    3912.     7.25KB     10.3
#>  9 base_vec_ansi     2.23ms   2.29ms     432.    48.18KB     24.5
#> 10 cli_vec_plain    28.79ms  28.87ms      34.5    2.48KB     49.3
#> 11 fansi_vec_plain 203.21µs 210.49µs    4620.     6.42KB     12.5
#> 12 base_vec_plain    1.63ms   1.68ms     592.     47.4KB     22.0
#> 13 cli_txt_ansi     24.01ms   24.5ms      40.9  507.59KB     13.6
#> 14 fansi_txt_ansi  233.88µs 242.38µs    4061.     6.77KB     10.3
#> 15 base_txt_ansi     1.25ms   1.28ms     763.   582.06KB     18.1
#> 16 cli_txt_plain     1.27ms    1.3ms     749.   369.84KB     17.9
#> 17 fansi_txt_plain 184.72µs 191.93µs    5084.     2.51KB     12.5
#> 18 base_txt_plain  854.05µs 883.33µs    1090.   367.31KB     17.9

ansi_substr()

bench::mark(
  cli_ansi        = ansi_substr(ansi, 2, 10),
  fansi_ansi      = substr_sgr(ansi, 2, 10),
  base_ansi       = substr(ansi, 2, 10),
  cli_plain       = ansi_substr(plain, 2, 10),
  fansi_plain     = substr_sgr(plain, 2, 10),
  base_plain      = substr(plain, 2, 10),
  cli_vec_ansi    = ansi_substr(vec_ansi, 2, 10),
  fansi_vec_ansi  = substr_sgr(vec_ansi, 2, 10),
  base_vec_ansi   = substr(vec_ansi, 2, 10),
  cli_vec_plain   = ansi_substr(vec_plain, 2, 10),
  fansi_vec_plain = substr_sgr(vec_plain, 2, 10),
  base_vec_plain  = substr(vec_plain, 2, 10),
  cli_txt_ansi    = ansi_substr(txt_ansi, 2, 10),
  fansi_txt_ansi  = substr_sgr(txt_ansi, 2, 10),
  base_txt_ansi   = substr(txt_ansi, 2, 10),
  cli_txt_plain   = ansi_substr(txt_plain, 2, 10),
  fansi_txt_plain = substr_sgr(txt_plain, 2, 10),
  base_txt_plain  = substr(txt_plain, 2, 10),
  check = FALSE
)
#> # A tibble: 18 × 6
#>    expression           min   median `itr/sec` mem_alloc `gc/sec`
#>    <bch:expr>      <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#>  1 cli_ansi          6.73µs   7.51µs   126308.   24.83KB    25.3 
#>  2 fansi_ansi        77.8µs  82.81µs    11742.   28.48KB    21.6 
#>  3 base_ansi         1.03µs   1.08µs   890795.        0B     0   
#>  4 cli_plain         6.86µs   7.35µs   133007.        0B    26.6 
#>  5 fansi_plain      76.79µs  80.65µs    12020.    1.98KB    21.2 
#>  6 base_plain      962.06ns   1.04µs   909218.        0B     0   
#>  7 cli_vec_ansi     27.18µs   28.1µs    35058.     1.7KB    10.5 
#>  8 fansi_vec_ansi  113.44µs 117.98µs     8274.    8.86KB    14.8 
#>  9 base_vec_ansi     5.95µs   6.12µs   159279.      848B     0   
#> 10 cli_vec_plain    23.14µs  24.09µs    40811.     1.7KB     8.16
#> 11 fansi_vec_plain  109.1µs  113.6µs     8578.    8.86KB    14.8 
#> 12 base_vec_plain    5.74µs   5.88µs   158542.      848B    15.9 
#> 13 cli_txt_ansi      6.92µs    7.5µs   130189.        0B    26.0 
#> 14 fansi_txt_ansi   78.65µs  82.29µs    11789.    1.98KB    21.2 
#> 15 base_txt_ansi      5.2µs   5.25µs   186429.        0B     0   
#> 16 cli_txt_plain     7.65µs   8.36µs   116680.        0B    23.3 
#> 17 fansi_txt_plain  77.88µs  82.04µs    11912.    1.98KB    21.2 
#> 18 base_txt_plain    3.42µs   3.48µs   280490.        0B    28.1

ansi_tolower() , ansi_toupper()

bench::mark(
  cli_ansi        = ansi_tolower(ansi),
  base_ansi       = tolower(ansi),
  cli_plain       = ansi_tolower(plain),
  base_plain      = tolower(plain),
  cli_vec_ansi    = ansi_tolower(vec_ansi),
  base_vec_ansi   = tolower(vec_ansi),
  cli_vec_plain   = ansi_tolower(vec_plain),
  base_vec_plain  = tolower(vec_plain),
  cli_txt_ansi    = ansi_tolower(txt_ansi),
  base_txt_ansi   = tolower(txt_ansi),
  cli_txt_plain   = ansi_tolower(txt_plain),
  base_txt_plain  = tolower(txt_plain),
  check = FALSE
)
#> # A tibble: 12 × 6
#>    expression          min   median `itr/sec` mem_alloc `gc/sec`
#>    <bch:expr>     <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#>  1 cli_ansi       108.04µs 112.77µs    8586.    11.88KB    14.7 
#>  2 base_ansi        1.34µs   1.37µs  699487.         0B     0   
#>  3 cli_plain       85.59µs   89.4µs   10869.     8.73KB    16.8 
#>  4 base_plain       1.02µs   1.06µs  914029.         0B     0   
#>  5 cli_vec_ansi     4.08ms   4.19ms     237.   838.77KB    28.7 
#>  6 base_vec_ansi   74.19µs  74.64µs   13265.       848B     0   
#>  7 cli_vec_plain    2.31ms   2.36ms     419.    816.9KB    27.5 
#>  8 base_vec_plain  43.94µs  44.66µs   22188.       848B     0   
#>  9 cli_txt_ansi    13.55ms  13.66ms      73.2  114.42KB     6.66
#> 10 base_txt_ansi   74.93µs  76.27µs   12989.         0B     0   
#> 11 cli_txt_plain   270.8µs 278.86µs    3521.    18.16KB     6.12
#> 12 base_txt_plain  41.55µs  42.93µs   23100.         0B     0

ansi_trimws()

bench::mark(
  cli_ansi        = ansi_trimws(ansi),
  base_ansi       = trimws(ansi),
  cli_plain       = ansi_trimws(plain),
  base_plain      = trimws(plain),
  cli_vec_ansi    = ansi_trimws(vec_ansi),
  base_vec_ansi   = trimws(vec_ansi),
  cli_vec_plain   = ansi_trimws(vec_plain),
  base_vec_plain  = trimws(vec_plain),
  cli_txt_ansi    = ansi_trimws(txt_ansi),
  base_txt_ansi   = trimws(txt_ansi),
  cli_txt_plain   = ansi_trimws(txt_plain),
  base_txt_plain  = trimws(txt_plain),
  check = FALSE
)
#> # A tibble: 12 × 6
#>    expression          min   median `itr/sec` mem_alloc `gc/sec`
#>    <bch:expr>     <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#>  1 cli_ansi          109µs  114.7µs     8467.        0B    23.5 
#>  2 base_ansi        16.2µs   17.2µs    56451.        0B    22.6 
#>  3 cli_plain       108.2µs  113.5µs     8566.        0B    21.7 
#>  4 base_plain       16.1µs   16.9µs    57400.        0B    23.0 
#>  5 cli_vec_ansi    200.1µs    210µs     4680.     7.2KB    12.4 
#>  6 base_vec_ansi    55.7µs     63µs    15618.    1.66KB     6.08
#>  7 cli_vec_plain   186.3µs  196.6µs     5000.     7.2KB    12.4 
#>  8 base_vec_plain   49.5µs   57.1µs    17206.    1.66KB     6.14
#>  9 cli_txt_ansi    177.3µs  182.7µs     5368.        0B    14.5 
#> 10 base_txt_ansi    38.3µs   39.4µs    24904.        0B     7.47
#> 11 cli_txt_plain   160.1µs  165.5µs     5927.        0B    16.6 
#> 12 base_txt_plain   33.9µs   34.9µs    27969.        0B    11.2

UTF-8 functions

utf8_nchar()

bench::mark(
  cli        = utf8_nchar(uni, type = "chars"),
  base       = nchar(uni, "chars"),
  cli_vec    = utf8_nchar(vec_uni, type = "chars"),
  base_vec   = nchar(vec_uni, "chars"),
  cli_txt    = utf8_nchar(txt_uni, type = "chars"),
  base_txt   = nchar(txt_uni, "chars"),
  check = FALSE
)
#> # A tibble: 6 × 6
#>   expression      min   median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#> 1 cli          8.16µs   8.68µs   112420.        0B    22.5 
#> 2 base       901.05ns 952.04ns   992857.        0B     0   
#> 3 cli_vec     23.14µs  23.77µs    41352.      448B    12.4 
#> 4 base_vec    11.57µs  11.87µs    83350.      448B     0   
#> 5 cli_txt     23.27µs  23.93µs    41031.        0B     8.21
#> 6 base_txt    12.41µs  12.72µs    77727.        0B     0
bench::mark(
  cli        = utf8_nchar(uni, type = "width"),
  base       = nchar(uni, "width"),
  cli_vec    = utf8_nchar(vec_uni, type = "width"),
  base_vec   = nchar(vec_uni, "width"),
  cli_txt    = utf8_nchar(txt_uni, type = "width"),
  base_txt   = nchar(txt_uni, "width"),
  check = FALSE
)
#> # A tibble: 6 × 6
#>   expression      min   median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#> 1 cli          8.12µs   8.63µs   113156.        0B    22.6 
#> 2 base         1.33µs   1.38µs   691122.        0B     0   
#> 3 cli_vec     28.58µs   29.3µs    33578.      448B     6.72
#> 4 base_vec    51.19µs  51.73µs    18978.      448B     2.01
#> 5 cli_txt     28.93µs  29.61µs    33222.        0B     6.65
#> 6 base_txt    87.24µs  87.93µs    11268.        0B     0
bench::mark(
  cli        = utf8_nchar(uni, type = "codepoints"),
  base       = nchar(uni, "chars"),
  cli_vec    = utf8_nchar(vec_uni, type = "codepoints"),
  base_vec   = nchar(vec_uni, "chars"),
  cli_txt    = utf8_nchar(txt_uni, type = "codepoints"),
  base_txt   = nchar(txt_uni, "chars"),
  check = FALSE
)
#> # A tibble: 6 × 6
#>   expression      min   median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#> 1 cli          8.63µs   9.18µs   106313.        0B    31.9 
#> 2 base       862.17ns 942.03ns  1006026.        0B     0   
#> 3 cli_vec     19.58µs  20.35µs    48243.      448B    14.5 
#> 4 base_vec     11.6µs  11.83µs    83465.      448B     0   
#> 5 cli_txt     20.19µs  20.94µs    46867.        0B     9.38
#> 6 base_txt    12.42µs  12.72µs    77884.        0B     7.79

utf8_substr()

bench::mark(
  cli        = utf8_substr(uni, 2, 10),
  base       = substr(uni, 2, 10),
  cli_vec    = utf8_substr(vec_uni, 2, 10),
  base_vec   = substr(vec_uni, 2, 10),
  cli_txt    = utf8_substr(txt_uni, 2, 10),
  base_txt   = substr(txt_uni, 2, 10),
  check = FALSE
)
#> # A tibble: 6 × 6
#>   expression      min   median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#> 1 cli          6.47µs   6.97µs   139681.    22.1KB    27.9 
#> 2 base         1.07µs   1.11µs   844423.        0B    84.5 
#> 3 cli_vec      29.2µs  29.96µs    32806.     1.7KB     6.56
#> 4 base_vec     7.88µs   8.28µs   119035.      848B     0   
#> 5 cli_txt       6.4µs   6.99µs   138737.        0B    41.6 
#> 6 base_txt     5.46µs   5.53µs   177410.        0B     0

Session info

sessioninfo::session_info()
#> ─ Session info ──────────────────────────────────────────────────────
#>  setting  value
#>  version  R version 4.5.0 (2025-04-11)
#>  os       Ubuntu 24.04.2 LTS
#>  system   x86_64, linux-gnu
#>  ui       X11
#>  language en
#>  collate  C.UTF-8
#>  ctype    C.UTF-8
#>  tz       UTC
#>  date     2025-04-29
#>  pandoc   3.1.11 @ /opt/hostedtoolcache/pandoc/3.1.11/x64/ (via rmarkdown)
#>  quarto   NA
#> 
#> ─ Packages ──────────────────────────────────────────────────────────
#>  package     * version    date (UTC) lib source
#>  bench         1.1.4      2025-01-16 [1] RSPM
#>  bslib         0.9.0      2025-01-30 [1] RSPM
#>  cachem        1.1.0      2024-05-16 [1] RSPM
#>  cli         * 3.6.5.9000 2025-04-29 [1] local
#>  codetools     0.2-20     2024-03-31 [3] CRAN (R 4.5.0)
#>  desc          1.4.3      2023-12-10 [1] RSPM
#>  digest        0.6.37     2024-08-19 [1] RSPM
#>  evaluate      1.0.3      2025-01-10 [1] RSPM
#>  fansi       * 1.0.6      2023-12-08 [1] RSPM
#>  fastmap       1.2.0      2024-05-15 [1] RSPM
#>  fs            1.6.6      2025-04-12 [1] RSPM
#>  glue          1.8.0      2024-09-30 [1] RSPM
#>  htmltools     0.5.8.1    2024-04-04 [1] RSPM
#>  htmlwidgets   1.6.4      2023-12-06 [1] RSPM
#>  jquerylib     0.1.4      2021-04-26 [1] RSPM
#>  jsonlite      2.0.0      2025-03-27 [1] RSPM
#>  knitr         1.50       2025-03-16 [1] RSPM
#>  lifecycle     1.0.4      2023-11-07 [1] RSPM
#>  magrittr      2.0.3      2022-03-30 [1] RSPM
#>  pillar        1.10.2     2025-04-05 [1] RSPM
#>  pkgconfig     2.0.3      2019-09-22 [1] RSPM
#>  pkgdown       2.1.2      2025-04-28 [1] any (@2.1.2)
#>  profmem       0.6.0      2020-12-13 [1] RSPM
#>  R6            2.6.1      2025-02-15 [1] RSPM
#>  ragg          1.4.0      2025-04-10 [1] RSPM
#>  rlang         1.1.6      2025-04-11 [1] RSPM
#>  rmarkdown     2.29       2024-11-04 [1] RSPM
#>  sass          0.4.10     2025-04-11 [1] RSPM
#>  sessioninfo   1.2.3      2025-02-05 [1] any (@1.2.3)
#>  systemfonts   1.2.2      2025-04-04 [1] RSPM
#>  textshaping   1.0.0      2025-01-20 [1] RSPM
#>  tibble        3.2.1      2023-03-20 [1] RSPM
#>  utf8          1.2.4      2023-10-22 [1] RSPM
#>  vctrs         0.6.5      2023-12-01 [1] RSPM
#>  xfun          0.52       2025-04-02 [1] RSPM
#>  yaml          2.3.10     2024-07-26 [1] RSPM
#> 
#>  [1] /home/runner/work/_temp/Library
#>  [2] /opt/R/4.5.0/lib/R/site-library
#>  [3] /opt/R/4.5.0/lib/R/library
#>  * ── Packages attached to the search path.
#> 
#> ─────────────────────────────────────────────────────────────────────