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: 0x55dd16d75940> <environment: 0x55dd177d6c50>

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         45.7µs   49.5µs    19549.    99.3KB     18.9
#> 2 plain        46.5µs   49.8µs    19434.        0B     17.5
#> 3 base         11.5µs   12.8µs    76033.    48.4KB     22.8
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   51.8µs    18683.        0B     21.3
#> 2 plain        47.9µs   51.5µs    18764.        0B     21.4
#> 3 base         13.6µs   14.9µs    65582.        0B     19.7

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.91µs 120.47µs     8078.   75.07KB     14.6
#> 2 plain       89.17µs  95.06µs    10205.    8.73KB     14.6
#> 3 base         1.92µs   2.06µs   465839.        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          344µs    370µs     2674.   33.17KB     19.1
#> 2 plain         344µs    370µs     2684.    1.09KB     19.2

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.9µs    6.4µs   150979.     9.2KB     30.2
#>  2 fansi_ansi       30.89µs  34.28µs    28295.    4.18KB     22.7
#>  3 cli_plain         5.93µs   6.44µs   150453.        0B     30.1
#>  4 fansi_plain      31.04µs  33.84µs    28655.      688B     22.9
#>  5 cli_vec_ansi      7.38µs   7.79µs   125449.      448B     25.1
#>  6 fansi_vec_ansi    39.9µs  42.03µs    23108.    5.02KB     18.5
#>  7 cli_vec_plain     7.89µs   8.29µs   118013.      448B     23.6
#>  8 fansi_vec_plain  38.81µs  40.68µs    23880.    5.02KB     19.1
#>  9 cli_txt_ansi      5.89µs   6.28µs   153486.        0B     30.7
#> 10 fansi_txt_ansi   30.93µs  32.82µs    29382.      688B     23.5
#> 11 cli_txt_plain      6.7µs   7.05µs   138388.        0B     27.7
#> 12 fansi_txt_plain  38.68µs  41.08µs    23650.    5.02KB     18.9

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          61.9µs   63.8µs    15384.    22.7KB     8.18
#> 2 fansi       115.1µs  118.5µs     8247.    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.88µs   7.41µs   130902.        0B    26.2 
#>  2 fansi_ansi       93.36µs  98.22µs     9864.   38.83KB    14.6 
#>  3 base_ansi       921.08ns 981.03ns   959418.        0B    96.0 
#>  4 cli_plain         6.81µs   7.39µs   131384.        0B    26.3 
#>  5 fansi_plain      92.32µs  97.83µs     9918.      688B    14.6 
#>  6 base_plain      840.98ns 901.17ns  1030581.        0B   103.  
#>  7 cli_vec_ansi     28.77µs  29.48µs    33312.      448B     3.33
#>  8 fansi_vec_ansi  113.83µs 119.09µs     8162.    5.02KB    14.7 
#>  9 base_vec_ansi    17.26µs  17.35µs    56953.      448B     0   
#> 10 cli_vec_plain    26.81µs  27.53µs    35615.      448B     7.12
#> 11 fansi_vec_plain 103.56µs 109.17µs     8878.    5.02KB    14.7 
#> 12 base_vec_plain    10.2µs  10.28µs    95962.      448B     0   
#> 13 cli_txt_ansi        28µs  28.76µs    34069.        0B     6.82
#> 14 fansi_txt_ansi  105.74µs 110.85µs     8755.      688B    14.6 
#> 15 base_txt_ansi    16.91µs  16.99µs    58081.        0B     0   
#> 16 cli_txt_plain    26.24µs  26.88µs    36540.        0B     7.31
#> 17 fansi_txt_plain  94.92µs 100.45µs     9654.      688B    14.6 
#> 18 base_txt_plain    9.89µs  10.41µs    94360.        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.59µs   9.19µs   105846.        0B    21.2 
#>  2 fansi_ansi        94.3µs  99.72µs     9705.      688B    16.8 
#>  3 base_ansi         1.23µs   1.29µs   739023.        0B     0   
#>  4 cli_plain         8.58µs   9.21µs   105792.        0B    21.2 
#>  5 fansi_plain      93.64µs  99.11µs     9773.      688B    18.1 
#>  6 base_plain        1.01µs   1.07µs   892680.        0B     0   
#>  7 cli_vec_ansi     35.05µs  35.82µs    27466.      448B     5.49
#>  8 fansi_vec_ansi  122.53µs 127.95µs     7594.    5.02KB    12.5 
#>  9 base_vec_ansi    40.89µs  41.21µs    23956.      448B     0   
#> 10 cli_vec_plain    33.26µs  34.05µs    28531.      448B     8.56
#> 11 fansi_vec_plain 113.35µs 118.61µs     8152.    5.02KB    12.5 
#> 12 base_vec_plain   21.61µs  21.79µs    45326.      448B     0   
#> 13 cli_txt_ansi     34.98µs  35.62µs    27579.        0B     8.28
#> 14 fansi_txt_ansi  115.03µs 120.56µs     7902.      688B    12.4 
#> 15 base_txt_ansi    42.93µs  44.11µs    22422.        0B     0   
#> 16 cli_txt_plain     32.9µs  33.62µs    29126.        0B     5.83
#> 17 fansi_txt_plain 105.37µs 110.69µs     8778.      688B    14.5 
#> 18 base_txt_plain   23.19µs  23.72µs    41541.        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.85µs   7.42µs   130993.        0B    13.1 
#> 2 cli_plain       6.42µs   6.94µs   139934.        0B    28.0 
#> 3 cli_vec_ansi   33.04µs  34.24µs    28681.      848B     5.74
#> 4 cli_vec_plain  10.36µs  10.97µs    88698.      848B     8.87
#> 5 cli_txt_ansi   32.72µs  33.65µs    28710.        0B     5.74
#> 6 cli_txt_plain   7.27µs   7.79µs   121180.        0B    24.2

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µs   27.6µs    34431.        0B     27.6
#>  2 fansi_ansi        28.6µs   30.5µs    31326.    7.24KB     25.1
#>  3 cli_plain           26µs   27.5µs    34427.        0B     27.6
#>  4 fansi_plain       28.4µs   30.2µs    32131.      688B     22.5
#>  5 cli_vec_ansi      35.2µs     37µs    25193.      848B     20.2
#>  6 fansi_vec_ansi    54.8µs   56.8µs    17169.    5.41KB     14.7
#>  7 cli_vec_plain     29.1µs   30.5µs    31861.      848B     25.5
#>  8 fansi_vec_plain     37µs   39.2µs    24819.    4.59KB     17.4
#>  9 cli_txt_ansi        35µs   36.5µs    26693.        0B     21.4
#> 10 fansi_txt_ansi    44.7µs   46.6µs    20928.    5.12KB     16.8
#> 11 cli_txt_plain       27µs   28.3µs    34149.        0B     27.3
#> 12 fansi_txt_plain   29.3µs   30.9µs    31352.      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.19µs 176.51µs     5535.  104.34KB    19.0 
#>  2 fansi_ansi      128.54µs 135.36µs     7208.  106.35KB    18.9 
#>  3 base_ansi         4.13µs   4.44µs   220482.      224B     0   
#>  4 cli_plain       167.16µs 175.91µs     5531.    8.09KB    18.9 
#>  5 fansi_plain     128.15µs 135.92µs     7161.    9.62KB    19.0 
#>  6 base_plain        3.71µs   3.93µs   247865.        0B    24.8 
#>  7 cli_vec_ansi      7.68ms   7.83ms      124.  823.77KB    24.8 
#>  8 fansi_vec_ansi    1.07ms   1.11ms      869.  846.81KB    17.3 
#>  9 base_vec_ansi   158.72µs 165.26µs     5920.    22.7KB     4.09
#> 10 cli_vec_plain     7.68ms   7.85ms      123.  823.77KB    22.6 
#> 11 fansi_vec_plain   1.03ms   1.07ms      913.  845.98KB    19.9 
#> 12 base_vec_plain  109.31µs  112.2µs     8751.      848B     2.02
#> 13 cli_txt_ansi      3.45ms    3.5ms      285.    63.6KB     0   
#> 14 fansi_txt_ansi    1.56ms   1.59ms      626.   35.05KB     2.02
#> 15 base_txt_ansi   138.69µs 146.92µs     6755.   18.47KB     2.02
#> 16 cli_txt_plain     2.38ms   2.42ms      413.    63.6KB     0   
#> 17 fansi_txt_plain 529.26µs 549.79µs     1803.    30.6KB     6.16
#> 18 base_txt_plain   89.53µs  92.19µs    10677.   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        150.59µs 160.89µs     6014.   33.84KB     23.6
#>  2 fansi_ansi       55.09µs  58.68µs    16434.   31.43KB     21.1
#>  3 base_ansi         1.09µs   1.13µs   822731.     4.2KB      0  
#>  4 cli_plain       147.34µs 157.32µs     6165.        0B     24.8
#>  5 fansi_plain      54.31µs  57.34µs    16919.      872B     21.1
#>  6 base_plain        1.01µs   1.06µs   894861.        0B      0  
#>  7 cli_vec_ansi    281.63µs 291.67µs     3379.   16.73KB     12.5
#>  8 fansi_vec_ansi  122.65µs 127.19µs     7691.    5.59KB     12.5
#>  9 base_vec_ansi    36.29µs  37.74µs    26396.      848B      0  
#> 10 cli_vec_plain   233.06µs 242.89µs     4046.   16.73KB     14.7
#> 11 fansi_vec_plain 119.63µs 124.25µs     7874.    5.59KB     12.5
#> 12 base_vec_plain   31.15µs  31.62µs    31257.      848B      0  
#> 13 cli_txt_ansi    158.99µs 166.59µs     5863.        0B     21.2
#> 14 fansi_txt_ansi   54.98µs  58.68µs    16578.      872B     23.4
#> 15 base_txt_ansi     1.12µs   1.17µs   808569.        0B      0  
#> 16 cli_txt_plain   150.37µs 158.55µs     6146.        0B     23.5
#> 17 fansi_txt_plain  54.95µs  58.52µs    16627.      872B     21.2
#> 18 base_txt_plain    1.03µs   1.08µs   867024.        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        413.41µs 440.44µs    2255.         0B    19.1 
#>  2 fansi_ansi       99.44µs 106.33µs    9138.    97.33KB    21.3 
#>  3 base_ansi        39.61µs     42µs   22931.         0B    18.4 
#>  4 cli_plain       279.95µs 295.93µs    3328.         0B    21.0 
#>  5 fansi_plain       96.8µs 103.26µs    9438.       872B    19.5 
#>  6 base_plain        32.6µs  34.36µs   28142.         0B    19.7 
#>  7 cli_vec_ansi     43.58ms  43.58ms      22.9    2.48KB   229.  
#>  8 fansi_vec_ansi  245.73µs 254.81µs    3860.     7.25KB    10.3 
#>  9 base_vec_ansi     2.32ms   2.38ms     419.    48.18KB    22.3 
#> 10 cli_vec_plain    29.55ms  29.72ms      33.6    2.48KB    56.1 
#> 11 fansi_vec_plain 204.85µs 213.97µs    4593.     6.42KB    12.5 
#> 12 base_vec_plain    1.68ms   1.72ms     577.     47.4KB    22.0 
#> 13 cli_txt_ansi     25.92ms  26.05ms      38.3  507.59KB    13.7 
#> 14 fansi_txt_ansi  233.66µs 243.59µs    4051.     6.77KB     8.20
#> 15 base_txt_ansi     1.27ms   1.31ms     750.   582.06KB    20.7 
#> 16 cli_txt_plain      1.3ms   1.34ms     735.   369.84KB    15.4 
#> 17 fansi_txt_plain 184.72µs  192.8µs    5057.     2.51KB    12.5 
#> 18 base_txt_plain  869.72µs 905.42µs    1082.   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          7.04µs   7.71µs   125952.   24.83KB    25.2 
#>  2 fansi_ansi       79.38µs  84.66µs    11425.   28.48KB    19.0 
#>  3 base_ansi         1.06µs   1.13µs   824443.        0B    82.5 
#>  4 cli_plain         6.98µs   7.57µs   128263.        0B    25.7 
#>  5 fansi_plain      78.99µs  83.58µs    11610.    1.98KB    21.9 
#>  6 base_plain        1.02µs   1.07µs   893793.        0B     0   
#>  7 cli_vec_ansi      28.1µs  28.87µs    33991.     1.7KB     6.80
#>  8 fansi_vec_ansi  113.65µs 118.64µs     8231.    8.86KB    14.7 
#>  9 base_vec_ansi      6.1µs   6.32µs   155370.      848B     0   
#> 10 cli_vec_plain    23.17µs  24.52µs    40113.     1.7KB    12.0 
#> 11 fansi_vec_plain 108.98µs 113.09µs     8632.    8.86KB    14.7 
#> 12 base_vec_plain    5.91µs   6.02µs   163364.      848B     0   
#> 13 cli_txt_ansi      6.94µs   7.51µs   128582.        0B    38.6 
#> 14 fansi_txt_ansi   78.69µs  83.26µs    11696.    1.98KB    19.0 
#> 15 base_txt_ansi      6.5µs   6.56µs   149634.        0B    15.0 
#> 16 cli_txt_plain     7.75µs    8.3µs   117904.        0B    23.6 
#> 17 fansi_txt_plain  77.79µs  82.35µs    11814.    1.98KB    21.1 
#> 18 base_txt_plain    4.13µs    4.2µs   232850.        0B     0

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       105.81µs 111.16µs    8748.    11.88KB    16.7 
#>  2 base_ansi        1.37µs   1.41µs  686492.         0B     0   
#>  3 cli_plain       85.88µs  89.91µs   10780.     8.73KB    14.5 
#>  4 base_plain       1.03µs   1.08µs  881693.         0B     0   
#>  5 cli_vec_ansi     4.13ms   4.22ms     232.   838.77KB    29.0 
#>  6 base_vec_ansi   77.33µs  77.64µs   12417.       848B     0   
#>  7 cli_vec_plain    2.32ms   2.38ms     418.    816.9KB    29.9 
#>  8 base_vec_plain  45.62µs  46.57µs   21245.       848B     0   
#>  9 cli_txt_ansi    14.34ms  14.41ms      69.2  114.42KB     6.70
#> 10 base_txt_ansi   81.05µs   81.5µs   12130.         0B     0   
#> 11 cli_txt_plain  268.27µs 278.35µs    3517.    18.16KB     4.05
#> 12 base_txt_plain  43.47µs  43.93µs   22450.         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        111.5µs  118.9µs     8141.        0B    21.1 
#>  2 base_ansi        16.7µs   17.9µs    54004.        0B    21.6 
#>  3 cli_plain       110.3µs  116.6µs     8335.        0B    21.0 
#>  4 base_plain       16.7µs   17.8µs    54527.        0B    21.8 
#>  5 cli_vec_ansi    208.8µs  218.2µs     4500.     7.2KB    12.5 
#>  6 base_vec_ansi    56.7µs   62.6µs    15650.    1.66KB     4.68
#>  7 cli_vec_plain     192µs  202.5µs     4855.     7.2KB    12.4 
#>  8 base_vec_plain   50.5µs   56.1µs    17448.    1.66KB     6.11
#>  9 cli_txt_ansi    182.8µs  190.3µs     5145.        0B    14.5 
#> 10 base_txt_ansi    41.1µs   42.4µs    23089.        0B     9.24
#> 11 cli_txt_plain   167.2µs  174.7µs     5600.        0B    14.5 
#> 12 base_txt_plain   35.3µs   36.7µs    26615.        0B     7.99

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.21µs   8.78µs   110318.        0B    22.1 
#> 2 base       892.09ns 962.06ns   980173.        0B     0   
#> 3 cli_vec     23.24µs  23.98µs    40873.      448B    12.3 
#> 4 base_vec    11.74µs  11.88µs    82976.      448B     0   
#> 5 cli_txt     23.43µs  24.07µs    40779.        0B     8.16
#> 6 base_txt    12.66µs  12.74µs    77298.        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.28µs   8.84µs   110028.        0B    22.0 
#> 2 base         1.32µs   1.39µs   688330.        0B     0   
#> 3 cli_vec     28.78µs  29.69µs    33087.      448B     6.62
#> 4 base_vec    50.66µs  51.26µs    19295.      448B     2.01
#> 5 cli_txt      29.3µs  30.05µs    32692.        0B     6.54
#> 6 base_txt    86.98µs   87.8µs    11284.        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.3µs   103958.        0B    31.2 
#> 2 base       911.07ns  982.1ns   966932.        0B     0   
#> 3 cli_vec      19.9µs   20.6µs    47552.      448B    14.3 
#> 4 base_vec    11.75µs   11.9µs    83050.      448B     0   
#> 5 cli_txt     20.54µs   21.2µs    45146.        0B     9.03
#> 6 base_txt    12.65µs   12.7µs    71656.        0B     7.17

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.52µs   7.01µs   137633.    22.1KB    27.5 
#> 2 base         1.08µs   1.15µs   813548.        0B    81.4 
#> 3 cli_vec     28.85µs   29.7µs    33097.     1.7KB     6.62
#> 4 base_vec     8.27µs   8.55µs   115237.      848B     0   
#> 5 cli_txt      6.57µs   7.06µs   136798.        0B    27.4 
#> 6 base_txt     5.74µs   5.81µs   168407.        0B    16.8

Session info

sessioninfo::session_info()
#> ─ Session info ──────────────────────────────────────────────────────
#>  setting  value
#>  version  R version 4.5.1 (2025-06-13)
#>  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-08-15
#>  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-08-15 [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.4      2025-06-18 [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.11.0     2025-07-04 [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.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] RSPM
#>  systemfonts   1.2.3      2025-04-30 [1] RSPM
#>  textshaping   1.0.1      2025-05-01 [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.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.1/lib/R/site-library
#>  [3] /opt/R/4.5.1/lib/R/library
#>  * ── Packages attached to the search path.
#> 
#> ─────────────────────────────────────────────────────────────────────