Skip to content

Commit 97c5b4b

Browse files
committed
Auto merge of rust-lang#136264 - GuillaumeGomez:optimize-integers-to-string, r=<try>
Optimize `ToString` implementation for integers Part of rust-lang#135543. Follow-up of rust-lang#133247 and rust-lang#128204. Rather than writing pretty bad benchers like I did last time, `@workingjubilee:` do you have a suggestion on how to check the impact on performance for this PR? Thanks in advance! r? `@workingjubilee`
2 parents e6f12c8 + 83dc76e commit 97c5b4b

File tree

8 files changed

+78
-19
lines changed

8 files changed

+78
-19
lines changed

library/alloc/src/string.rs

+45
Original file line numberDiff line numberDiff line change
@@ -2783,7 +2783,51 @@ impl SpecToString for bool {
27832783
}
27842784
}
27852785

2786+
macro_rules! impl_to_string {
2787+
($($signed:ident, $unsigned:ident,)*) => {
2788+
$(
2789+
#[cfg(not(no_global_oom_handling))]
2790+
#[cfg(not(feature = "optimize_for_size"))]
2791+
impl SpecToString for $signed {
2792+
#[inline]
2793+
fn spec_to_string(&self) -> String {
2794+
const SIZE: usize = $signed::MAX.ilog(10) as usize + 1;
2795+
let mut buf = [core::mem::MaybeUninit::<u8>::uninit(); SIZE];
2796+
// Only difference between signed and unsigned are these 3 lines.
2797+
let mut out = String::with_capacity(SIZE + 1);
2798+
if *self < 0 {
2799+
out.push('-');
2800+
}
2801+
2802+
out.push_str(self.unsigned_abs()._fmt(&mut buf, SIZE));
2803+
out
2804+
}
2805+
}
2806+
#[cfg(not(no_global_oom_handling))]
2807+
#[cfg(not(feature = "optimize_for_size"))]
2808+
impl SpecToString for $unsigned {
2809+
#[inline]
2810+
fn spec_to_string(&self) -> String {
2811+
const SIZE: usize = $unsigned::MAX.ilog(10) as usize + 1;
2812+
let mut buf = [core::mem::MaybeUninit::<u8>::uninit(); SIZE];
2813+
2814+
self._fmt(&mut buf, SIZE).to_string()
2815+
}
2816+
}
2817+
)*
2818+
}
2819+
}
2820+
2821+
impl_to_string! {
2822+
i8, u8,
2823+
i16, u16,
2824+
i32, u32,
2825+
i64, u64,
2826+
isize, usize,
2827+
}
2828+
27862829
#[cfg(not(no_global_oom_handling))]
2830+
#[cfg(feature = "optimize_for_size")]
27872831
impl SpecToString for u8 {
27882832
#[inline]
27892833
fn spec_to_string(&self) -> String {
@@ -2803,6 +2847,7 @@ impl SpecToString for u8 {
28032847
}
28042848

28052849
#[cfg(not(no_global_oom_handling))]
2850+
#[cfg(feature = "optimize_for_size")]
28062851
impl SpecToString for i8 {
28072852
#[inline]
28082853
fn spec_to_string(&self) -> String {

library/core/src/fmt/num.rs

+18-10
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,10 @@ macro_rules! impl_Display {
207207
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
208208
#[cfg(not(feature = "optimize_for_size"))]
209209
{
210-
self._fmt(true, f)
210+
const SIZE: usize = $unsigned::MAX.ilog(10) as usize + 1;
211+
let mut buf = [MaybeUninit::<u8>::uninit(); SIZE];
212+
213+
f.pad_integral(true, "", self._fmt(&mut buf, SIZE))
211214
}
212215
#[cfg(feature = "optimize_for_size")]
213216
{
@@ -221,7 +224,10 @@ macro_rules! impl_Display {
221224
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
222225
#[cfg(not(feature = "optimize_for_size"))]
223226
{
224-
return self.unsigned_abs()._fmt(*self >= 0, f);
227+
const SIZE: usize = $signed::MAX.ilog(10) as usize + 1;
228+
let mut buf = [MaybeUninit::<u8>::uninit(); SIZE];
229+
230+
f.pad_integral(*self >= 0, "", self.unsigned_abs()._fmt(&mut buf, SIZE))
225231
}
226232
#[cfg(feature = "optimize_for_size")]
227233
{
@@ -232,11 +238,14 @@ macro_rules! impl_Display {
232238

233239
#[cfg(not(feature = "optimize_for_size"))]
234240
impl $unsigned {
235-
fn _fmt(mut self, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236-
const SIZE: usize = $unsigned::MAX.ilog(10) as usize + 1;
237-
let mut buf = [MaybeUninit::<u8>::uninit(); SIZE];
238-
let mut curr = SIZE;
239-
let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf);
241+
#[doc(hidden)]
242+
#[unstable(
243+
feature = "fmt_internals",
244+
reason = "internal routines only exposed for testing",
245+
issue = "none"
246+
)]
247+
pub fn _fmt<'a>(mut self, buf: &'a mut [MaybeUninit::<u8>], mut curr: usize) -> &'a str {
248+
let buf_ptr = MaybeUninit::slice_as_mut_ptr(buf);
240249
let lut_ptr = DEC_DIGITS_LUT.as_ptr();
241250

242251
// SAFETY: Since `d1` and `d2` are always less than or equal to `198`, we
@@ -296,11 +305,10 @@ macro_rules! impl_Display {
296305

297306
// SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid
298307
// UTF-8 since `DEC_DIGITS_LUT` is
299-
let buf_slice = unsafe {
308+
unsafe {
300309
str::from_utf8_unchecked(
301310
slice::from_raw_parts(buf_ptr.add(curr), buf.len() - curr))
302-
};
303-
f.pad_integral(is_nonnegative, "", buf_slice)
311+
}
304312
}
305313
})*
306314

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

+3-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ fn main() {
2424
&v as *const _ as usize
2525
};
2626
assert_eq!(a.to_string(), b.to_string());
27-
assert_eq!(format!("{}", a == b), "true");
28-
assert_eq!(format!("{}", cmp_in(a, b)), "true");
27+
// Changed after https://github.com/rust-lang/rust/pull/136264
28+
assert_eq!(format!("{}", a != b), "true");
29+
assert_eq!(format!("{}", cmp_in(a, b)), "false");
2930
assert_eq!(format!("{}", cmp(a, b)), "true");
3031
}

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ fn main() {
2323
// But it looks like zero...
2424
assert_eq!(i.to_string(), "0");
2525
// ...and now it *is* zero?
26-
assert_eq!(i, 0);
26+
// Not anymore: https://github.com/rust-lang/rust/pull/136264
27+
assert_ne!(i, 0);
2728
// So `a` and `b` are equal after all?
2829
}

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

+3-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ fn main() {
2626
ptr::from_ref(&v).expose_provenance()
2727
};
2828
assert_eq!(a.to_string(), b.to_string());
29-
assert_eq!(format!("{}", a == b), "true");
30-
assert_eq!(format!("{}", cmp_in(a, b)), "true");
29+
// Changed after https://github.com/rust-lang/rust/pull/136264
30+
assert_eq!(format!("{}", a != b), "true");
31+
assert_eq!(format!("{}", cmp_in(a, b)), "false");
3132
assert_eq!(format!("{}", cmp(a, b)), "true");
3233
}

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ fn main() {
2525
// But it looks like zero...
2626
assert_eq!(i.to_string(), "0");
2727
// ...and now it *is* zero?
28-
assert_eq!(i, 0);
28+
// Not anymore since https://github.com/rust-lang/rust/pull/136264
29+
assert_ne!(i, 0);
2930
// So `a` and `b` are equal after all?
3031
}

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

+3-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ fn main() {
2626
ptr::from_ref(&v).addr()
2727
};
2828
assert_eq!(a.to_string(), b.to_string());
29-
assert_eq!(format!("{}", a == b), "true");
30-
assert_eq!(format!("{}", cmp_in(a, b)), "true");
29+
// Changed after https://github.com/rust-lang/rust/pull/136264
30+
assert_eq!(format!("{}", a != b), "true");
31+
assert_eq!(format!("{}", cmp_in(a, b)), "false");
3132
assert_eq!(format!("{}", cmp(a, b)), "true");
3233
}

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ fn main() {
2525
// But it looks like zero...
2626
assert_eq!(i.to_string(), "0");
2727
// ...and now it *is* zero?
28-
assert_eq!(i, 0);
28+
// Not anymore since https://github.com/rust-lang/rust/pull/136264
29+
assert_ne!(i, 0);
2930
// So `a` and `b` are equal after all?
3031
}

0 commit comments

Comments
 (0)