From 7b3fd17f73b48ed874ec68ebc7970354c8c74be8 Mon Sep 17 00:00:00 2001 From: overlookmotel Date: Tue, 13 Feb 2024 23:47:47 +0000 Subject: [PATCH] Improve performance of `String::from_str_in` --- benches/benches.rs | 23 +++++++++++++++++++++++ src/collections/string.rs | 15 +++++++++++++-- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/benches/benches.rs b/benches/benches.rs index 00bac02..ed70e97 100644 --- a/benches/benches.rs +++ b/benches/benches.rs @@ -89,6 +89,13 @@ fn format_realloc(bump: &bumpalo::Bump, n: usize) { criterion::black_box(s); } +#[cfg(feature = "collections")] +fn string_from_str_in(bump: &bumpalo::Bump, str: &str) { + let str = criterion::black_box(str); + let s = bumpalo::collections::string::String::from_str_in(str, bump); + criterion::black_box(s); +} + #[cfg(feature = "collections")] fn string_push_str(bump: &bumpalo::Bump, str: &str) { let str = criterion::black_box(str); @@ -210,6 +217,21 @@ fn bench_format_realloc(c: &mut Criterion) { } } +fn bench_string_from_str_in(c: &mut Criterion) { + let len: usize = 16; + + let mut group = c.benchmark_group("alloc"); + group.throughput(Throughput::Elements(len as u64)); + group.bench_function("from_str_in", |b| { + let mut bump = bumpalo::Bump::with_capacity(len); + let str = "x".repeat(len); + b.iter(|| { + bump.reset(); + string_from_str_in(&bump, &*str); + }); + }); +} + fn bench_string_push_str(c: &mut Criterion) { let len: usize = 16 * 1024; // 16 KiB @@ -236,6 +258,7 @@ criterion_group!( bench_try_alloc_try_with, bench_try_alloc_try_with_err, bench_format_realloc, + bench_string_from_str_in, bench_string_push_str ); criterion_main!(benches); diff --git a/src/collections/string.rs b/src/collections/string.rs index 8173eab..8d79211 100644 --- a/src/collections/string.rs +++ b/src/collections/string.rs @@ -680,8 +680,19 @@ impl<'bump> String<'bump> { /// assert_eq!(s, "hello"); /// ``` pub fn from_str_in(s: &str, bump: &'bump Bump) -> String<'bump> { - let mut t = String::with_capacity_in(s.len(), bump); - t.push_str(s); + let len = s.len(); + let mut t = String::with_capacity_in(len, bump); + // SAFETY: + // * `src` is valid for reads of `s.len()` bytes by virtue of being an allocated `&str`. + // * `dst` is valid for writes of `s.len()` bytes as `String::with_capacity_in(s.len(), bump)` + // above guarantees that. + // * Alignment is not relevant as `u8` has no alignment requirements. + // * Source and destination ranges cannot overlap as we just reserved the destination + // range from the bump. + unsafe { ptr::copy_nonoverlapping(s.as_ptr(), t.vec.as_mut_ptr(), len) }; + // SAFETY: We reserved sufficent capacity for the string above. + // The elements at `0..len` were initialized by `copy_nonoverlapping` above. + unsafe { t.vec.set_len(len) }; t }