ANSI function benchmarks
Gábor Csárdi
2025-10-21
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: 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.
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 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.
#>
#> ─────────────────────────────────────────────────────────────────────