Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit d97326e

Browse files
committed
Auto merge of #136264 - GuillaumeGomez:optimize-integers-to-string, r=Amanieu
Optimize `ToString` implementation for integers Part of #135543. Follow-up of #133247 and #128204. The benchmark results are: | name| 1.87.0-nightly (3ea711f 2025年03月09日) | With this PR | diff | |-|-|-|-| | bench_i16 | 32.06 ns/iter (+/- 0.12) | 17.62 ns/iter (+/- 0.03) | -45% | | bench_i32 | 31.61 ns/iter (+/- 0.04) | 15.10 ns/iter (+/- 0.06) | -52% | | bench_i64 | 31.71 ns/iter (+/- 0.07) | 15.02 ns/iter (+/- 0.20) | -52% | | bench_i8 | 13.21 ns/iter (+/- 0.14) | 14.93 ns/iter (+/- 0.16) | +13% | | bench_u16 | 31.20 ns/iter (+/- 0.06) | 16.14 ns/iter (+/- 0.11) | -48% | | bench_u32 | 33.27 ns/iter (+/- 0.05) | 16.18 ns/iter (+/- 0.10) | -51% | | bench_u64 | 31.44 ns/iter (+/- 0.06) | 16.62 ns/iter (+/- 0.21) | -47% | | bench_u8 | 10.57 ns/iter (+/- 0.30) | 13.00 ns/iter (+/- 0.43) | +22% | More information about it in [the original comment](#136264 (comment)). r? `@workingjubilee`
2 parents c4e05e5 + 1ef7585 commit d97326e

File tree

8 files changed

+73
-15
lines changed

8 files changed

+73
-15
lines changed

‎library/alloc/src/string.rs‎

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2826,7 +2826,54 @@ impl SpecToString for bool {
28262826
}
28272827
}
28282828

2829+
macro_rules! impl_to_string {
2830+
($($signed:ident, $unsigned:ident,)*) => {
2831+
$(
2832+
#[cfg(not(no_global_oom_handling))]
2833+
#[cfg(not(feature = "optimize_for_size"))]
2834+
impl SpecToString for $signed {
2835+
#[inline]
2836+
fn spec_to_string(&self) -> String {
2837+
const SIZE: usize = $signed::MAX.ilog(10) as usize + 1;
2838+
let mut buf = [core::mem::MaybeUninit::<u8>::uninit(); SIZE];
2839+
// Only difference between signed and unsigned are these 8 lines.
2840+
let mut out;
2841+
if *self < 0 {
2842+
out = String::with_capacity(SIZE + 1);
2843+
out.push('-');
2844+
} else {
2845+
out = String::with_capacity(SIZE);
2846+
}
2847+
2848+
out.push_str(self.unsigned_abs()._fmt(&mut buf));
2849+
out
2850+
}
2851+
}
2852+
#[cfg(not(no_global_oom_handling))]
2853+
#[cfg(not(feature = "optimize_for_size"))]
2854+
impl SpecToString for $unsigned {
2855+
#[inline]
2856+
fn spec_to_string(&self) -> String {
2857+
const SIZE: usize = $unsigned::MAX.ilog(10) as usize + 1;
2858+
let mut buf = [core::mem::MaybeUninit::<u8>::uninit(); SIZE];
2859+
2860+
self._fmt(&mut buf).to_string()
2861+
}
2862+
}
2863+
)*
2864+
}
2865+
}
2866+
2867+
impl_to_string! {
2868+
i8, u8,
2869+
i16, u16,
2870+
i32, u32,
2871+
i64, u64,
2872+
isize, usize,
2873+
}
2874+
28292875
#[cfg(not(no_global_oom_handling))]
2876+
#[cfg(feature = "optimize_for_size")]
28302877
impl SpecToString for u8 {
28312878
#[inline]
28322879
fn spec_to_string(&self) -> String {
@@ -2846,6 +2893,7 @@ impl SpecToString for u8 {
28462893
}
28472894

28482895
#[cfg(not(no_global_oom_handling))]
2896+
#[cfg(feature = "optimize_for_size")]
28492897
impl SpecToString for i8 {
28502898
#[inline]
28512899
fn spec_to_string(&self) -> String {

‎library/core/src/fmt/num.rs‎

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,11 @@ macro_rules! impl_Display {
208208
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
209209
#[cfg(not(feature = "optimize_for_size"))]
210210
{
211-
self._fmt(true, f)
211+
const MAX_DEC_N: usize = $unsigned::MAX.ilog(10) as usize + 1;
212+
// Buffer decimals for $unsigned with right alignment.
213+
let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DEC_N];
214+
215+
f.pad_integral(true, "", self._fmt(&mut buf))
212216
}
213217
#[cfg(feature = "optimize_for_size")]
214218
{
@@ -222,7 +226,11 @@ macro_rules! impl_Display {
222226
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223227
#[cfg(not(feature = "optimize_for_size"))]
224228
{
225-
return self.unsigned_abs()._fmt(*self >= 0, f);
229+
const MAX_DEC_N: usize = $unsigned::MAX.ilog(10) as usize + 1;
230+
// Buffer decimals for $unsigned with right alignment.
231+
let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DEC_N];
232+
233+
f.pad_integral(*self >= 0, "", self.unsigned_abs()._fmt(&mut buf))
226234
}
227235
#[cfg(feature = "optimize_for_size")]
228236
{
@@ -233,10 +241,13 @@ macro_rules! impl_Display {
233241

234242
#[cfg(not(feature = "optimize_for_size"))]
235243
impl $unsigned {
236-
fn _fmt(self, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
237-
const MAX_DEC_N: usize = $unsigned::MAX.ilog(10) as usize + 1;
238-
// Buffer decimals for $unsigned with right alignment.
239-
let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DEC_N];
244+
#[doc(hidden)]
245+
#[unstable(
246+
feature = "fmt_internals",
247+
reason = "specialized method meant to only be used by `SpecToString` implementation",
248+
issue = "none"
249+
)]
250+
pub fn _fmt<'a>(self, buf: &'a mut [MaybeUninit::<u8>]) -> &'a str {
240251
// Count the number of bytes in buf that are not initialized.
241252
let mut offset = buf.len();
242253
// Consume the least-significant decimals from a working copy.
@@ -301,13 +312,12 @@ macro_rules! impl_Display {
301312
// SAFETY: All buf content since offset is set.
302313
let written = unsafe { buf.get_unchecked(offset..) };
303314
// SAFETY: Writes use ASCII from the lookup table exclusively.
304-
let as_str = unsafe {
315+
unsafe {
305316
str::from_utf8_unchecked(slice::from_raw_parts(
306317
MaybeUninit::slice_as_ptr(written),
307318
written.len(),
308319
))
309-
};
310-
f.pad_integral(is_nonnegative, "", as_str)
320+
}
311321
}
312322
})*
313323

‎tests/ui/codegen/equal-pointers-unequal/as-cast/inline2.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ fn main() {
2323
let v = 0;
2424
&v as *const _ as usize
2525
};
26-
assert_eq!(a.to_string(), b.to_string());
26+
assert_eq!(format!("{a}"), format!("{b}"));
2727
assert_eq!(format!("{}", a == b), "true");
2828
assert_eq!(format!("{}", cmp_in(a, b)), "true");
2929
assert_eq!(format!("{}", cmp(a, b)), "true");

‎tests/ui/codegen/equal-pointers-unequal/as-cast/zero.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ fn main() {
2121
// It's not zero, which means `a` and `b` are not equal.
2222
assert_ne!(i, 0);
2323
// But it looks like zero...
24-
assert_eq!(i.to_string(), "0");
24+
assert_eq!(format!("{i}"), "0");
2525
// ...and now it *is* zero?
2626
assert_eq!(i, 0);
2727
// So `a` and `b` are equal after all?

‎tests/ui/codegen/equal-pointers-unequal/exposed-provenance/inline2.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ fn main() {
2525
let v = 0;
2626
ptr::from_ref(&v).expose_provenance()
2727
};
28-
assert_eq!(a.to_string(), b.to_string());
28+
assert_eq!(format!("{a}"), format!("{b}"));
2929
assert_eq!(format!("{}", a == b), "true");
3030
assert_eq!(format!("{}", cmp_in(a, b)), "true");
3131
assert_eq!(format!("{}", cmp(a, b)), "true");

‎tests/ui/codegen/equal-pointers-unequal/exposed-provenance/zero.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ fn main() {
2323
// It's not zero, which means `a` and `b` are not equal.
2424
assert_ne!(i, 0);
2525
// But it looks like zero...
26-
assert_eq!(i.to_string(), "0");
26+
assert_eq!(format!("{i}"), "0");
2727
// ...and now it *is* zero?
2828
assert_eq!(i, 0);
2929
// So `a` and `b` are equal after all?

‎tests/ui/codegen/equal-pointers-unequal/strict-provenance/inline2.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ fn main() {
2525
let v = 0;
2626
ptr::from_ref(&v).addr()
2727
};
28-
assert_eq!(a.to_string(), b.to_string());
28+
assert_eq!(format!("{a}"), format!("{b}"));
2929
assert_eq!(format!("{}", a == b), "true");
3030
assert_eq!(format!("{}", cmp_in(a, b)), "true");
3131
assert_eq!(format!("{}", cmp(a, b)), "true");

‎tests/ui/codegen/equal-pointers-unequal/strict-provenance/zero.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ fn main() {
2323
// It's not zero, which means `a` and `b` are not equal.
2424
assert_ne!(i, 0);
2525
// But it looks like zero...
26-
assert_eq!(i.to_string(), "0");
26+
assert_eq!(format!("{i}"), "0");
2727
// ...and now it *is* zero?
2828
assert_eq!(i, 0);
2929
// So `a` and `b` are equal after all?

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /