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: 0x5622ffc0c610> <environment: 0x56230066bfb0>

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.2µs   49.6µs    19559.    99.3KB     18.9
#> 2 plain          46µs   49.7µs    19535.        0B     17.4
#> 3 base         11.3µs   12.5µs    77300.    48.4KB     23.2
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µs   51.6µs    18777.        0B     21.2
#> 2 plain        47.9µs   51.6µs    18818.        0B     21.3
#> 3 base         13.5µs   14.7µs    66060.        0B     26.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        113.2µs  120.1µs     8066.   75.07KB     14.6
#> 2 plain        89.8µs  94.93µs    10216.    8.73KB     14.6
#> 3 base          1.9µs   2.04µs   471001.        0B      0

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          346µs    368µs     2699.   33.17KB     19.0
#> 2 plain         346µs    368µs     2700.    1.09KB     16.9

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.94µs   6.53µs   147971.     9.2KB     29.6
#>  2 fansi_ansi       31.71µs  34.77µs    27952.    4.18KB     22.4
#>  3 cli_plain         5.92µs   6.52µs   146674.        0B     29.3
#>  4 fansi_plain      30.52µs  34.09µs    28594.      688B     22.9
#>  5 cli_vec_ansi      7.35µs   7.83µs   124621.      448B     24.9
#>  6 fansi_vec_ansi   39.65µs  41.83µs    23296.    5.02KB     18.7
#>  7 cli_vec_plain     7.91µs   8.29µs   117920.      448B     23.6
#>  8 fansi_vec_plain  38.34µs  40.75µs    23851.    5.02KB     19.1
#>  9 cli_txt_ansi       5.9µs   6.35µs   152358.        0B     30.5
#> 10 fansi_txt_ansi   31.07µs  33.13µs    29297.      688B     23.5
#> 11 cli_txt_plain     6.74µs   7.16µs   136061.        0B     13.6
#> 12 fansi_txt_plain  38.55µs  40.81µs    23875.    5.02KB     21.5

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          60.7µs   62.6µs    15579.    22.7KB     8.18
#> 2 fansi       114.7µs  117.7µs     8325.    55.3KB     8.20

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.8µs   7.39µs   130690.        0B    26.1 
#>  2 fansi_ansi       92.87µs  97.54µs     9971.   38.83KB    14.6 
#>  3 base_ansi       911.07ns    951ns   981688.        0B    98.2 
#>  4 cli_plain         6.79µs   7.39µs   132170.        0B    13.2 
#>  5 fansi_plain      92.25µs  97.48µs     9951.      688B    16.8 
#>  6 base_plain      830.97ns 872.07ns  1061460.        0B     0   
#>  7 cli_vec_ansi      28.7µs  29.47µs    33354.      448B     6.67
#>  8 fansi_vec_ansi  113.55µs 118.83µs     8150.    5.02KB    14.7 
#>  9 base_vec_ansi    17.25µs  17.34µs    56867.      448B     0   
#> 10 cli_vec_plain    26.76µs  27.45µs    35781.      448B     7.16
#> 11 fansi_vec_plain 102.89µs 108.66µs     8911.    5.02KB    14.7 
#> 12 base_vec_plain   10.15µs  10.24µs    96082.      448B     0   
#> 13 cli_txt_ansi        28µs  28.79µs    34125.        0B     6.83
#> 14 fansi_txt_ansi     105µs 110.02µs     8826.      688B    14.6 
#> 15 base_txt_ansi    16.91µs  16.97µs    58212.        0B     0   
#> 16 cli_txt_plain    26.35µs     27µs    36395.        0B     7.28
#> 17 fansi_txt_plain  94.25µs  99.82µs     9719.      688B    14.6 
#> 18 base_txt_plain    9.87µs  10.38µs    96017.        0B     9.60
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.31µs   9.11µs   104995.        0B    21.0 
#>  2 fansi_ansi       93.17µs  98.13µs     9876.      688B    16.7 
#>  3 base_ansi         1.22µs   1.27µs   740837.        0B     0   
#>  4 cli_plain         8.56µs   9.27µs   104513.        0B    31.4 
#>  5 fansi_plain      92.97µs  97.79µs     9920.      688B    14.6 
#>  6 base_plain           1µs   1.04µs   901852.        0B    90.2 
#>  7 cli_vec_ansi     34.97µs  35.84µs    27402.      448B     5.48
#>  8 fansi_vec_ansi  122.06µs 127.41µs     7583.    5.02KB    12.5 
#>  9 base_vec_ansi    40.85µs  41.23µs    23973.      448B     0   
#> 10 cli_vec_plain    33.63µs  34.62µs    28391.      448B     5.68
#> 11 fansi_vec_plain 113.35µs  117.4µs     8250.    5.02KB    14.7 
#> 12 base_vec_plain   21.54µs  21.76µs    45311.      448B     0   
#> 13 cli_txt_ansi     34.99µs  35.66µs    27601.        0B     5.52
#> 14 fansi_txt_ansi  114.35µs 119.74µs     8142.      688B    14.6 
#> 15 base_txt_ansi    42.89µs  43.92µs    22478.        0B     0   
#> 16 cli_txt_plain    33.15µs  33.99µs    28236.        0B     5.65
#> 17 fansi_txt_plain 104.81µs 110.29µs     8778.      688B    14.5 
#> 18 base_txt_plain   22.99µs  23.73µs    41618.        0B     0

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.69µs   7.18µs   135409.        0B    27.1 
#> 2 cli_plain        6.3µs   6.78µs   142363.        0B    14.2 
#> 3 cli_vec_ansi   31.35µs  32.29µs    30407.      848B     6.08
#> 4 cli_vec_plain  10.21µs  10.79µs    90402.      848B     9.04
#> 5 cli_txt_ansi   30.82µs  31.65µs    31025.        0B     6.21
#> 6 cli_txt_plain   7.15µs   7.67µs   126731.        0B    25.4

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.3µs     28µs    34677.        0B     27.8
#>  2 fansi_ansi        28.6µs   30.6µs    31662.    7.24KB     25.3
#>  3 cli_plain         26.3µs   27.9µs    34780.        0B     27.8
#>  4 fansi_plain       28.1µs   30.1µs    32082.      688B     22.5
#>  5 cli_vec_ansi      35.7µs   37.4µs    25932.      848B     20.8
#>  6 fansi_vec_ansi    54.2µs   56.5µs    17194.    5.41KB     14.8
#>  7 cli_vec_plain     29.2µs   30.7µs    31623.      848B     25.3
#>  8 fansi_vec_plain   37.1µs   39.3µs    24738.    4.59KB     17.3
#>  9 cli_txt_ansi      35.2µs   36.7µs    26530.        0B     21.2
#> 10 fansi_txt_ansi    44.8µs   46.9µs    20768.    5.12KB     16.8
#> 11 cli_txt_plain     26.6µs   28.5µs    33707.        0B     27.0
#> 12 fansi_txt_plain   29.4µs     31µs    31289.      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         167.2µs 175.08µs     5569.  104.34KB    19.0 
#>  2 fansi_ansi       130.1µs 136.29µs     7138.  106.35KB    19.0 
#>  3 base_ansi         4.13µs   4.43µs   220267.      224B     0   
#>  4 cli_plain       167.25µs 173.96µs     5595.    8.09KB    18.9 
#>  5 fansi_plain     128.09µs 135.91µs     7142.    9.62KB    19.0 
#>  6 base_plain        3.62µs   3.95µs   244260.        0B    24.4 
#>  7 cli_vec_ansi      7.78ms   7.96ms      126.  823.77KB    25.6 
#>  8 fansi_vec_ansi    1.08ms   1.13ms      860.  846.81KB    17.4 
#>  9 base_vec_ansi   158.37µs 164.69µs     5982.    22.7KB     2.03
#> 10 cli_vec_plain     7.75ms    7.9ms      126.  823.77KB    26.3 
#> 11 fansi_vec_plain   1.02ms   1.06ms      922.  845.98KB    20.0 
#> 12 base_vec_plain  109.89µs 113.03µs     8645.      848B     2.01
#> 13 cli_txt_ansi      3.41ms   3.47ms      286.    63.6KB     2.02
#> 14 fansi_txt_ansi    1.55ms   1.58ms      632.   35.05KB     2.02
#> 15 base_txt_ansi   138.46µs 149.52µs     6675.   18.47KB     2.01
#> 16 cli_txt_plain     2.36ms   2.39ms      417.    63.6KB     0   
#> 17 fansi_txt_plain  525.5µs  545.6µs     1814.    30.6KB     6.16
#> 18 base_txt_plain   90.07µs  91.89µs    10716.   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        154.23µs 162.39µs     5924.   33.84KB     21.5
#>  2 fansi_ansi        54.7µs  58.85µs    16177.   31.43KB     23.5
#>  3 base_ansi         1.07µs   1.12µs   852987.     4.2KB      0  
#>  4 cli_plain       147.69µs 157.11µs     6180.        0B     22.2
#>  5 fansi_plain      54.23µs  57.04µs    17040.      872B     23.4
#>  6 base_plain        1.01µs   1.04µs   916622.        0B      0  
#>  7 cli_vec_ansi    276.14µs  285.7µs     3448.   16.73KB     12.5
#>  8 fansi_vec_ansi   122.3µs 126.61µs     7731.    5.59KB     12.6
#>  9 base_vec_ansi    36.41µs  37.57µs    26475.      848B      0  
#> 10 cli_vec_plain   233.64µs 243.56µs     4039.   16.73KB     14.7
#> 11 fansi_vec_plain 120.21µs 124.51µs     7858.    5.59KB     12.5
#> 12 base_vec_plain   31.03µs  31.86µs    31041.      848B      0  
#> 13 cli_txt_ansi    157.26µs 164.99µs     5927.        0B     21.1
#> 14 fansi_txt_ansi   54.59µs  58.17µs    16748.      872B     23.4
#> 15 base_txt_ansi     1.11µs   1.15µs   828988.        0B      0  
#> 16 cli_txt_plain   150.28µs 157.45µs     6201.        0B     23.4
#> 17 fansi_txt_plain  54.31µs  57.96µs    16839.      872B     21.2
#> 18 base_txt_plain    1.03µs   1.07µs   873356.        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        409.49µs 435.38µs    2288.         0B    19.0 
#>  2 fansi_ansi       99.11µs 105.62µs    9183.    97.33KB    21.4 
#>  3 base_ansi        40.22µs  42.87µs   22296.         0B    20.1 
#>  4 cli_plain       281.57µs 297.84µs    3286.         0B    19.0 
#>  5 fansi_plain      96.15µs 103.24µs    9446.       872B    12.1 
#>  6 base_plain       32.91µs  34.88µs   27867.         0B    11.2 
#>  7 cli_vec_ansi     42.97ms  43.33ms      22.9    2.48KB    22.9 
#>  8 fansi_vec_ansi  245.66µs 254.82µs    3870.     7.25KB     6.14
#>  9 base_vec_ansi      2.3ms    2.4ms     415.    48.18KB    13.0 
#> 10 cli_vec_plain    29.21ms   29.4ms      33.9    2.48KB    14.1 
#> 11 fansi_vec_plain 203.66µs    212µs    4646.     6.42KB     6.13
#> 12 base_vec_plain    1.68ms   1.74ms     573.     47.4KB    12.7 
#> 13 cli_txt_ansi     25.91ms  26.23ms      38.1  507.59KB     7.15
#> 14 fansi_txt_ansi  232.37µs 240.17µs    4104.     6.77KB     4.05
#> 15 base_txt_ansi     1.25ms   1.28ms     770.   582.06KB    11.1 
#> 16 cli_txt_plain     1.28ms   1.32ms     752.   369.84KB     8.62
#> 17 fansi_txt_plain 184.54µs 191.63µs    5118.     2.51KB     8.28
#> 18 base_txt_plain  855.34µs 886.01µs    1117.   367.31KB     8.66

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.91µs   7.53µs   128850.   24.83KB    12.9 
#>  2 fansi_ansi       79.36µs  83.98µs    11598.   28.48KB    12.6 
#>  3 base_ansi         1.04µs    1.1µs   852701.        0B     0   
#>  4 cli_plain         6.81µs   7.37µs   131068.        0B    13.1 
#>  5 fansi_plain      78.32µs  82.63µs    11823.    1.98KB    12.5 
#>  6 base_plain        1.01µs   1.06µs   885976.        0B     0   
#>  7 cli_vec_ansi      27.1µs  28.03µs    35117.     1.7KB     3.51
#>  8 fansi_vec_ansi  114.27µs 118.99µs     8226.    8.86KB     8.32
#>  9 base_vec_ansi     6.06µs   6.33µs   154472.      848B     0   
#> 10 cli_vec_plain    23.17µs  24.32µs    40420.     1.7KB     4.04
#> 11 fansi_vec_plain 109.26µs 114.35µs     8496.    8.86KB     8.32
#> 12 base_vec_plain    5.72µs   6.04µs   161796.      848B    16.2 
#> 13 cli_txt_ansi      6.89µs   7.47µs   130090.        0B    13.0 
#> 14 fansi_txt_ansi   78.11µs  82.98µs    11760.    1.98KB    10.3 
#> 15 base_txt_ansi     6.47µs   6.56µs   148517.        0B    14.9 
#> 16 cli_txt_plain     7.69µs   8.27µs   117837.        0B    11.8 
#> 17 fansi_txt_plain  78.56µs  82.94µs    11702.    1.98KB    10.4 
#> 18 base_txt_plain    4.12µs   4.19µs   232004.        0B    23.2

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       107.26µs 112.31µs    8679.    11.88KB     8.20
#>  2 base_ansi        1.34µs   1.39µs  680641.         0B    68.1 
#>  3 cli_plain       85.99µs  90.57µs   10702.     8.73KB     6.17
#>  4 base_plain       1.03µs   1.08µs  872957.         0B    87.3 
#>  5 cli_vec_ansi     4.17ms   4.35ms     229.   838.77KB    13.2 
#>  6 base_vec_ansi   77.81µs  78.15µs   12672.       848B     0   
#>  7 cli_vec_plain    2.35ms   2.42ms     412.    816.9KB    15.2 
#>  8 base_vec_plain  46.64µs  47.26µs   20931.       848B     0   
#>  9 cli_txt_ansi    14.39ms  14.56ms      68.8  114.42KB     4.30
#> 10 base_txt_ansi    78.2µs  78.95µs   12527.         0B     0   
#> 11 cli_txt_plain  268.13µs 276.86µs    3557.    18.16KB     2.01
#> 12 base_txt_plain  43.56µs  45.08µs   22005.         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.9µs  115.7µs     8416.        0B    12.3 
#>  2 base_ansi        16.3µs   17.3µs    55846.        0B    11.2 
#>  3 cli_plain       109.9µs    114µs     8520.        0B    12.4 
#>  4 base_plain       16.3µs   17.2µs    56698.        0B    11.3 
#>  5 cli_vec_ansi    206.6µs  215.4µs     4557.     7.2KB     8.25
#>  6 base_vec_ansi    57.9µs   62.6µs    15607.    1.66KB     2.02
#>  7 cli_vec_plain   192.8µs  201.9µs     4849.     7.2KB     8.35
#>  8 base_vec_plain   50.5µs   55.7µs    17578.    1.66KB     2.02
#>  9 cli_txt_ansi      183µs  189.3µs     5172.        0B     8.20
#> 10 base_txt_ansi    40.7µs   41.9µs    23303.        0B     4.66
#> 11 cli_txt_plain   167.1µs  172.8µs     5663.        0B     8.18
#> 12 base_txt_plain   35.1µs   36.4µs    26918.        0B     5.38

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.28µs   8.89µs   109729.        0B    22.0 
#> 2 base       901.05ns 951.11ns   988402.        0B     0   
#> 3 cli_vec     23.29µs  24.12µs    40733.      448B     4.07
#> 4 base_vec    11.68µs  11.87µs    82705.      448B     0   
#> 5 cli_txt     23.48µs  24.18µs    40643.        0B     4.06
#> 6 base_txt    12.65µs  12.76µs    77052.        0B     7.71
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.21µs   8.87µs   110038.        0B    11.0 
#> 2 base         1.31µs   1.37µs   692387.        0B     0   
#> 3 cli_vec     28.65µs  29.64µs    33182.      448B     3.32
#> 4 base_vec    50.51µs  51.19µs    19332.      448B     0   
#> 5 cli_txt     29.11µs  29.89µs    32765.        0B     6.55
#> 6 base_txt    86.07µs  86.81µs    11407.        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.69µs   9.39µs   103925.        0B    10.4 
#> 2 base       891.97ns 942.03ns   992747.        0B     0   
#> 3 cli_vec     19.82µs  20.74µs    47311.      448B     9.46
#> 4 base_vec    11.69µs   11.9µs    82594.      448B     0   
#> 5 cli_txt      20.5µs  21.28µs    46112.        0B     4.61
#> 6 base_txt    12.64µs  12.73µs    77294.        0B     7.73

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.39µs   6.97µs   139403.    22.1KB    13.9 
#> 2 base         1.05µs   1.11µs   839698.        0B     0   
#> 3 cli_vec     28.86µs   29.8µs    33036.     1.7KB     3.30
#> 4 base_vec     8.34µs   8.74µs   112235.      848B    11.2 
#> 5 cli_txt      6.46µs   7.02µs   139042.        0B    13.9 
#> 6 base_txt     5.72µs    5.8µs   168448.        0B     0

Session info

sessioninfo::session_info()
#> ─ Session info ──────────────────────────────────────────────────────
#>  setting  value
#>  version  R version 4.5.1 (2025-06-13)
#>  os       Ubuntu 24.04.3 LTS
#>  system   x86_64, linux-gnu
#>  ui       X11
#>  language en
#>  collate  C.UTF-8
#>  ctype    C.UTF-8
#>  tz       UTC
#>  date     2025-10-21
#>  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-10-21 [1] local
#>  codetools     0.2-20     2024-03-31 [3] CRAN (R 4.5.1)
#>  desc          1.4.3      2023-12-10 [1] RSPM
#>  digest        0.6.37     2024-08-19 [1] RSPM
#>  evaluate      1.0.5      2025-08-27 [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.4      2025-09-12 [1] RSPM
#>  pillar        1.11.1     2025-09-17 [1] RSPM
#>  pkgconfig     2.0.3      2019-09-22 [1] RSPM
#>  pkgdown       2.1.3      2025-05-25 [1] any (@2.1.3)
#>  profmem       0.7.0      2025-05-02 [1] RSPM
#>  R6            2.6.1      2025-02-15 [1] RSPM
#>  ragg          1.5.0      2025-09-02 [1] RSPM
#>  rlang         1.1.6      2025-04-11 [1] RSPM
#>  rmarkdown     2.30       2025-09-28 [1] RSPM
#>  sass          0.4.10     2025-04-11 [1] RSPM
#>  sessioninfo   1.2.3      2025-02-05 [1] RSPM
#>  systemfonts   1.3.1      2025-10-01 [1] RSPM
#>  textshaping   1.0.4      2025-10-10 [1] RSPM
#>  tibble        3.3.0      2025-06-08 [1] RSPM
#>  utf8          1.2.6      2025-06-08 [1] RSPM
#>  vctrs         0.6.5      2023-12-01 [1] RSPM
#>  xfun          0.53       2025-08-19 [1] RSPM
#>  yaml          2.3.10     2024-07-26 [1] RSPM
#> 
#>  [1] /home/runner/work/_temp/Library
#>  [2] /opt/R/4.5.1/lib/R/site-library
#>  [3] /opt/R/4.5.1/lib/R/library
#>  * ── Packages attached to the search path.
#> 
#> ─────────────────────────────────────────────────────────────────────