ANSI function benchmarks
Gábor Csárdi
2026-10-01
Source:vignettes/ansi-benchmark.Rmd
ansi-benchmark.Rmd$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: 0x55e7a213de18> <environment: 0x55e7a2c6d568>
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.
ansi <- format_inline(
"{col_green(symbol$tick)} {.code print(x)} {.emph emphasised}"
)
plain <- ansi_strip(ansi)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 44.8µs 48.4µs 20001. 99.2KB 21.0
#> 2 plain 44.5µs 48.4µs 20014. 0B 22.1
#> 3 base 11.4µs 12.4µs 78594. 48.6KB 15.7
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 47µs 50.5µs 19168. 0B 21.3
#> 2 plain 46.7µs 49.8µs 19401. 0B 23.3
#> 3 base 13.2µs 14.5µs 66612. 0B 26.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 110.71µs 117.48µs 8228. 76.09KB 16.8
#> 2 plain 88.14µs 92.86µs 10371. 8.73KB 14.6
#> 3 base 1.86µs 1.99µs 479327. 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 337µs 360µs 2713. 33.16KB 21.3
#> 2 plain 333µs 358µs 2749. 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.8µs 6.39µs 151049. 9.19KB 30.2
#> 2 fansi_ansi 30.94µs 33.9µs 28555. 4.18KB 25.7
#> 3 cli_plain 5.84µs 6.35µs 151067. 0B 30.2
#> 4 fansi_plain 30.4µs 32.66µs 29048. 688B 14.5
#> 5 cli_vec_ansi 7.2µs 7.67µs 124844. 448B 12.5
#> 6 fansi_vec_ansi 40.66µs 43.17µs 22289. 5.02KB 8.92
#> 7 cli_vec_plain 7.78µs 8.22µs 118410. 448B 11.8
#> 8 fansi_vec_plain 38.38µs 40.89µs 23777. 5.02KB 9.51
#> 9 cli_txt_ansi 5.77µs 6.17µs 157513. 0B 15.8
#> 10 fansi_txt_ansi 30.32µs 32.67µs 29709. 688B 14.9
#> 11 cli_txt_plain 6.63µs 7.08µs 136298. 0B 13.6
#> 12 fansi_txt_plain 38.44µs 41.29µs 22826. 5.02KB 9.13
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 56.5µs 58.4µs 16752. 22.6KB 4.05
#> 2 fansi 118.2µs 124.1µs 7831. 55.3KB 4.06
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.77µs 7.37µs 128915. 0B 12.9
#> 2 fansi_ansi 93.06µs 97.88µs 9886. 38.84KB 10.3
#> 3 base_ansi 901.99ns 961.01ns 973447. 0B 0
#> 4 cli_plain 6.83µs 7.42µs 130990. 0B 13.1
#> 5 fansi_plain 91.92µs 96.99µs 9907. 688B 8.20
#> 6 base_plain 811.07ns 862.05ns 1068054. 0B 0
#> 7 cli_vec_ansi 29.14µs 30.09µs 32495. 448B 3.25
#> 8 fansi_vec_ansi 113.63µs 119µs 8105. 5.02KB 8.26
#> 9 base_vec_ansi 18.47µs 18.57µs 52348. 448B 0
#> 10 cli_vec_plain 27.48µs 28.29µs 34501. 448B 3.45
#> 11 fansi_vec_plain 104µs 108.76µs 8896. 5.02KB 8.28
#> 12 base_vec_plain 10.81µs 10.89µs 90305. 448B 0
#> 13 cli_txt_ansi 28.71µs 29.48µs 33141. 0B 3.31
#> 14 fansi_txt_ansi 105.37µs 110.38µs 8524. 688B 8.21
#> 15 base_txt_ansi 18.21µs 18.28µs 53911. 0B 0
#> 16 cli_txt_plain 27.07µs 27.78µs 35268. 0B 3.53
#> 17 fansi_txt_plain 95.19µs 99.99µs 9676. 688B 8.21
#> 18 base_txt_plain 10.58µs 11.11µs 88607. 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.47µs 9.24µs 104490. 0B 20.9
#> 2 fansi_ansi 92.85µs 98.3µs 9839. 688B 8.20
#> 3 base_ansi 1.23µs 1.28µs 741283. 0B 0
#> 4 cli_plain 8.46µs 9.23µs 104960. 0B 10.5
#> 5 fansi_plain 92.62µs 97.78µs 9887. 688B 10.3
#> 6 base_plain 1.01µs 1.07µs 876857. 0B 0
#> 7 cli_vec_ansi 34.49µs 35.43µs 27349. 448B 2.74
#> 8 fansi_vec_ansi 116.22µs 122.12µs 7916. 5.02KB 6.16
#> 9 base_vec_ansi 44.08µs 44.53µs 21987. 448B 2.20
#> 10 cli_vec_plain 32.89µs 33.8µs 28839. 448B 2.88
#> 11 fansi_vec_plain 106.79µs 112.4µs 8573. 5.02KB 8.29
#> 12 base_vec_plain 22.97µs 23.25µs 42269. 448B 0
#> 13 cli_txt_ansi 34.16µs 34.98µs 27675. 0B 2.77
#> 14 fansi_txt_ansi 107.15µs 112.64µs 8578. 688B 8.21
#> 15 base_txt_ansi 46.46µs 47.19µs 20951. 0B 0
#> 16 cli_txt_plain 32.44µs 33.22µs 29510. 0B 2.95
#> 17 fansi_txt_plain 96.83µs 102.16µs 9483. 688B 8.21
#> 18 base_txt_plain 24.45µs 25.33µs 38999. 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.75µs 7.29µs 132931. 0B 13.3
#> 2 cli_plain 6.34µs 6.87µs 140931. 0B 0
#> 3 cli_vec_ansi 31.27µs 32.44µs 30281. 848B 3.03
#> 4 cli_vec_plain 10.3µs 10.92µs 89364. 848B 8.94
#> 5 cli_txt_ansi 30.65µs 31.85µs 30800. 0B 3.08
#> 6 cli_txt_plain 7.18µs 7.7µs 126288. 0B 12.6
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.7µs 35093. 0B 17.6
#> 2 fansi_ansi 28.5µs 30.6µs 31572. 7.24KB 12.6
#> 3 cli_plain 25.8µs 27.4µs 35442. 0B 14.2
#> 4 fansi_plain 28µs 29.8µs 32537. 688B 13.0
#> 5 cli_vec_ansi 35.1µs 36.9µs 26435. 848B 13.2
#> 6 fansi_vec_ansi 55.4µs 59µs 16512. 5.41KB 6.18
#> 7 cli_vec_plain 28.5µs 29.9µs 32513. 848B 16.3
#> 8 fansi_vec_plain 37.1µs 38.8µs 24980. 4.59KB 10.00
#> 9 cli_txt_ansi 34.1µs 35.5µs 27520. 0B 11.0
#> 10 fansi_txt_ansi 43.8µs 45.5µs 21350. 5.12KB 8.54
#> 11 cli_txt_plain 26.3µs 27.6µs 35072. 0B 17.5
#> 12 fansi_txt_plain 28.9µs 30.6µs 31664. 688B 12.7
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 163.69µs 170.82µs 5692. 104.31KB 10.3
#> 2 fansi_ansi 128.34µs 136µs 7151. 106.35KB 10.4
#> 3 base_ansi 4.04µs 4.39µs 221070. 224B 0
#> 4 cli_plain 162.98µs 170.15µs 5689. 8.09KB 10.3
#> 5 fansi_plain 126.38µs 134.24µs 7245. 9.62KB 12.5
#> 6 base_plain 3.67µs 3.92µs 247283. 0B 0
#> 7 cli_vec_ansi 7.54ms 7.73ms 129. 823.77KB 13.6
#> 8 fansi_vec_ansi 1.06ms 1.09ms 882. 846.81KB 17.2
#> 9 base_vec_ansi 155.82µs 162.5µs 6001. 22.7KB 2.04
#> 10 cli_vec_plain 7.56ms 7.69ms 130. 823.77KB 11.4
#> 11 fansi_vec_plain 982.95µs 1.02ms 978. 845.98KB 19.6
#> 12 base_vec_plain 106.59µs 110.42µs 8717. 848B 4.06
#> 13 cli_txt_ansi 3.41ms 3.45ms 289. 63.6KB 0
#> 14 fansi_txt_ansi 1.57ms 1.59ms 626. 35.05KB 0
#> 15 base_txt_ansi 133.61µs 143.19µs 6769. 18.47KB 2.02
#> 16 cli_txt_plain 2.44ms 2.46ms 405. 63.6KB 2.02
#> 17 fansi_txt_plain 520.97µs 558.47µs 1796. 30.6KB 2.02
#> 18 base_txt_plain 87.23µs 89.89µs 10713. 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 147.08µs 154.64µs 6282. 33.81KB 12.5
#> 2 fansi_ansi 54.91µs 58.49µs 16534. 31.42KB 12.5
#> 3 base_ansi 1.05µs 1.1µs 863976. 4.2KB 0
#> 4 cli_plain 144.72µs 151.54µs 6418. 0B 12.4
#> 5 fansi_plain 54.16µs 57.85µs 16707. 872B 12.7
#> 6 base_plain 981.03ns 1.02µs 909381. 0B 0
#> 7 cli_vec_ansi 272.53µs 283.56µs 3461. 16.73KB 6.16
#> 8 fansi_vec_ansi 115.58µs 120µs 8104. 5.59KB 6.16
#> 9 base_vec_ansi 36.79µs 37.66µs 26280. 848B 0
#> 10 cli_vec_plain 232.43µs 241.95µs 4035. 16.73KB 8.29
#> 11 fansi_vec_plain 108.4µs 112.78µs 8630. 5.59KB 6.17
#> 12 base_vec_plain 30.34µs 31.19µs 31628. 848B 0
#> 13 cli_txt_ansi 155.3µs 162.72µs 5974. 0B 12.5
#> 14 fansi_txt_ansi 53.81µs 56.01µs 17199. 872B 12.1
#> 15 base_txt_ansi 1.08µs 1.13µs 841489. 0B 0
#> 16 cli_txt_plain 145.48µs 151.23µs 6253. 0B 14.6
#> 17 fansi_txt_plain 53.61µs 56.2µs 17182. 872B 12.5
#> 18 base_txt_plain 992.09ns 1.04µs 915830. 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 410.94µs 437.28µs 2272. 0B 10.5
#> 2 fansi_ansi 97.94µs 105.26µs 9211. 97.33KB 10.3
#> 3 base_ansi 38.58µs 40.98µs 23565. 0B 11.8
#> 4 cli_plain 276.95µs 288.83µs 3373. 0B 10.3
#> 5 fansi_plain 97.4µs 104.03µs 9272. 872B 10.3
#> 6 base_plain 31.41µs 33.47µs 28595. 0B 11.4
#> 7 cli_vec_ansi 42.75ms 43.06ms 23.2 2.48KB 23.2
#> 8 fansi_vec_ansi 238.7µs 248.17µs 3956. 7.25KB 6.15
#> 9 base_vec_ansi 2.29ms 2.37ms 420. 48.18KB 13.0
#> 10 cli_vec_plain 29.06ms 29.41ms 34.0 2.48KB 14.1
#> 11 fansi_vec_plain 191.58µs 200.33µs 4841. 6.42KB 8.26
#> 12 base_vec_plain 1.65ms 1.72ms 581. 47.4KB 10.5
#> 13 cli_txt_ansi 25.97ms 26.16ms 38.0 507.59KB 7.12
#> 14 fansi_txt_ansi 226.23µs 234.86µs 4171. 6.77KB 6.15
#> 15 base_txt_ansi 1.28ms 1.32ms 749. 582.06KB 8.73
#> 16 cli_txt_plain 1.3ms 1.34ms 739. 369.84KB 10.8
#> 17 fansi_txt_plain 178.83µs 186.26µs 5237. 2.51KB 6.13
#> 18 base_txt_plain 868.77µs 909.57µs 1083. 367.31KB 8.68
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.89µs 7.58µs 127019. 24.81KB 12.7
#> 2 fansi_ansi 79.24µs 85.09µs 11398. 28.48KB 10.5
#> 3 base_ansi 1.04µs 1.1µs 850099. 0B 0
#> 4 cli_plain 6.76µs 7.4µs 129124. 0B 25.8
#> 5 fansi_plain 79.66µs 84.54µs 11446. 1.98KB 10.4
#> 6 base_plain 991.98ns 1.05µs 892080. 0B 0
#> 7 cli_vec_ansi 27.86µs 28.87µs 33897. 1.7KB 3.39
#> 8 fansi_vec_ansi 117.14µs 122.55µs 7918. 8.86KB 8.35
#> 9 base_vec_ansi 6.34µs 6.67µs 146477. 848B 0
#> 10 cli_vec_plain 23.84µs 25.19µs 38768. 1.7KB 7.76
#> 11 fansi_vec_plain 111.29µs 116.41µs 8312. 8.86KB 8.31
#> 12 base_vec_plain 6.05µs 6.38µs 152723. 848B 0
#> 13 cli_txt_ansi 6.63µs 7.41µs 131127. 0B 13.1
#> 14 fansi_txt_ansi 78.14µs 81.54µs 11894. 1.98KB 10.3
#> 15 base_txt_ansi 6.46µs 6.54µs 145925. 0B 14.6
#> 16 cli_txt_plain 7.43µs 7.96µs 118451. 0B 11.8
#> 17 fansi_txt_plain 77.82µs 81.28µs 11937. 1.98KB 10.3
#> 18 base_txt_plain 4.11µs 4.17µs 233979. 0B 23.4
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 104.86µs 108.98µs 8855. 11.85KB 10.3
#> 2 base_ansi 1.33µs 1.38µs 706958. 0B 0
#> 3 cli_plain 84.08µs 88.04µs 10957. 8.73KB 8.31
#> 4 base_plain 1.02µs 1.07µs 899147. 0B 0
#> 5 cli_vec_ansi 4.15ms 4.26ms 233. 838.77KB 13.1
#> 6 base_vec_ansi 75.43µs 76.18µs 12968. 848B 0
#> 7 cli_vec_plain 2.33ms 2.37ms 419. 816.9KB 15.1
#> 8 base_vec_plain 45.55µs 46.51µs 21232. 848B 0
#> 9 cli_txt_ansi 14.9ms 15ms 66.5 114.42KB 4.29
#> 10 base_txt_ansi 75.14µs 76.54µs 12894. 0B 0
#> 11 cli_txt_plain 265.66µs 275.04µs 3565. 18.16KB 2.01
#> 12 base_txt_plain 42.86µs 43.81µs 22691. 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.6µs 114.1µs 8491. 0B 10.3
#> 2 base_ansi 16.9µs 18.1µs 53204. 0B 16.0
#> 3 cli_plain 108.2µs 112.8µs 8557. 0B 10.3
#> 4 base_plain 16.7µs 17.9µs 53835. 0B 16.2
#> 5 cli_vec_ansi 212.4µs 221.3µs 4400. 7.2KB 6.16
#> 6 base_vec_ansi 59.7µs 66.9µs 14620. 1.66KB 2.02
#> 7 cli_vec_plain 198.5µs 207µs 4703. 7.2KB 6.15
#> 8 base_vec_plain 53.2µs 60µs 16367. 1.66KB 4.11
#> 9 cli_txt_ansi 183.4µs 189.8µs 5126. 0B 8.20
#> 10 base_txt_ansi 41.4µs 42.6µs 22876. 0B 4.58
#> 11 cli_txt_plain 168µs 173.6µs 5601. 0B 8.19
#> 12 base_txt_plain 35.7µs 36.9µs 26413. 0B 5.28
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.18µs 8.85µs 109426. 0B 10.9
#> 2 base 862.17ns 921.08ns 987175. 0B 0
#> 3 cli_vec 23.7µs 24.59µs 39711. 448B 3.97
#> 4 base_vec 11.47µs 11.7µs 83426. 448B 8.34
#> 5 cli_txt 23.86µs 24.56µs 39461. 0B 3.95
#> 6 base_txt 12.5µs 12.6µs 77928. 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.09µs 8.78µs 109967. 0B 11.0
#> 2 base 1.3µs 1.35µs 695860. 0B 0
#> 3 cli_vec 28.92µs 29.99µs 32511. 448B 6.50
#> 4 base_vec 50.44µs 50.88µs 19376. 448B 0
#> 5 cli_txt 29.46µs 30.24µs 32283. 0B 3.23
#> 6 base_txt 86.54µs 87.39µs 11254. 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.7µs 9.39µs 103007. 0B 20.6
#> 2 base 881ns 940.99ns 1001519. 0B 0
#> 3 cli_vec 19.6µs 20.49µs 47668. 448B 4.77
#> 4 base_vec 11.5µs 11.68µs 84038. 448B 0
#> 5 cli_txt 20.4µs 21.26µs 45966. 0B 9.19
#> 6 base_txt 12.5µs 12.59µs 78183. 0B 0
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.53µs 7.1µs 136583. 22.1KB 13.7
#> 2 base 1.06µs 1.12µs 839201. 0B 0
#> 3 cli_vec 29.38µs 30.26µs 32389. 1.7KB 6.48
#> 4 base_vec 8.11µs 8.38µs 117459. 848B 0
#> 5 cli_txt 6.36µs 6.95µs 139425. 0B 13.9
#> 6 base_txt 5.48µs 5.55µs 175894. 0B 0
Session info
sessioninfo::session_info()#> ─ Session info ──────────────────────────────────────────────────────
#> setting value
#> version R version 4.6.1 (2026-06-24)
#> os Ubuntu 24.04.5 LTS
#> system x86_64, linux-gnu
#> ui X11
#> language en
#> collate C.UTF-8
#> ctype C.UTF-8
#> tz UTC
#> date 2026-10-01
#> pandoc 3.8.3 @ /opt/hostedtoolcache/pandoc/3.8.3/x64/ (via rmarkdown)
#> quarto NA
#>
#> ─ Packages ──────────────────────────────────────────────────────────
#> package * version date (UTC) lib source
#> bench 1.1.4 2025-01-16 [1] RSPM
#> bslib 0.12.0 2026-08-04 [1] RSPM
#> cachem 1.1.0 2024-05-16 [1] RSPM
#> cli * 3.6.6 2026-10-01 [1] local
#> codetools 0.2-20 2024-03-31 [3] CRAN (R 4.6.1)
#> desc 1.4.3 2023-12-10 [1] RSPM
#> digest 0.6.39 2025-11-19 [1] RSPM
#> evaluate 1.0.5 2025-08-27 [1] RSPM
#> fansi * 1.0.7 2025-11-19 [1] RSPM
#> fastmap 1.2.0 2024-05-15 [1] RSPM
#> fs 2.1.0 2026-04-18 [1] RSPM
#> glue 1.8.1 2026-04-17 [1] RSPM
#> htmltools 0.5.9 2025-12-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.52 2026-09-06 [1] RSPM
#> lifecycle 1.0.5 2026-01-08 [1] RSPM
#> magrittr 2.0.5 2026-04-04 [1] RSPM
#> otel 0.2.0 2025-08-29 [1] RSPM
#> pillar 1.11.1 2025-09-17 [1] RSPM
#> pkgconfig 2.0.3 2019-09-22 [1] RSPM
#> pkgdown 2.2.1 2026-07-07 [1] any (@2.2.1)
#> profmem 0.7.0 2025-05-02 [1] RSPM
#> R6 2.6.1 2025-02-15 [1] RSPM
#> ragg 1.5.2 2026-03-23 [1] RSPM
#> rlang 1.3.0 2026-07-05 [1] RSPM
#> rmarkdown 2.32 2026-09-01 [1] RSPM
#> sass 0.4.10 2025-04-11 [1] RSPM
#> sessioninfo 1.2.4 2026-06-04 [1] any (@1.2.4)
#> systemfonts 1.3.2 2026-03-05 [1] RSPM
#> textshaping 1.0.5 2026-03-06 [1] RSPM
#> tibble 3.3.1 2026-01-11 [1] RSPM
#> utf8 1.2.6 2025-06-08 [1] RSPM
#> vctrs 0.7.3 2026-04-11 [1] RSPM
#> xfun 0.61 2026-09-16 [1] RSPM
#> yaml 2.3.12 2025-12-10 [1] RSPM
#>
#> [1] /home/runner/work/_temp/Library
#> [2] /opt/R/4.6.1/lib/R/site-library
#> [3] /opt/R/4.6.1/lib/R/library
#> * ── Packages attached to the search path.
#>
#> ─────────────────────────────────────────────────────────────────────