diff options
| author | Jubilee <46493976+workingjubilee@users.noreply.github.com> | 2024-06-06 21:10:08 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-06-06 21:10:08 -0700 |
| commit | c6cdd457eb4be60bfef27298511ebfb29555d3c0 (patch) | |
| tree | 17d4da08a8be5cd97998a2387959691803a996cf | |
| parent | 76e7a0849c07d73e4d9afde8036ee8c450127cc8 (diff) | |
| parent | d32d1c1a2ef2451f2fd7a061bba933173a195938 (diff) | |
| download | rust-c6cdd457eb4be60bfef27298511ebfb29555d3c0.tar.gz rust-c6cdd457eb4be60bfef27298511ebfb29555d3c0.zip | |
Rollup merge of #125606 - diondokter:opt-size-int-fmt, r=cuviper
Size optimize int formatting Let's use the new feature flag! This uses a simpler algorithm to format integers. It is slower, but also smaller. It also saves having to import the 200 byte rodata lookup table. In a test of mine this saves ~300 bytes total of a cortex-m binary that does integer formatting. For a 16KB device, that's almost 2%. Note though that for opt-level 3 the text size actually grows by 116 bytes. Still a win in total. I'm not sure why the generated code is bigger than the more fancy algo. Maybe the smaller algo lends itself more to inlining and duplicating?
| -rw-r--r-- | library/core/src/fmt/num.rs | 33 |
1 files changed, 33 insertions, 0 deletions
diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index ab2158394bf..3a5a5af8bf5 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -212,6 +212,7 @@ static DEC_DIGITS_LUT: &[u8; 200] = b"0001020304050607080910111213141516171819\ macro_rules! impl_Display { ($($t:ident),* as $u:ident via $conv_fn:ident named $name:ident) => { + #[cfg(not(feature = "optimize_for_size"))] fn $name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { // 2^128 is about 3*10^38, so 39 gives an extra byte of space let mut buf = [MaybeUninit::<u8>::uninit(); 39]; @@ -277,6 +278,38 @@ macro_rules! impl_Display { f.pad_integral(is_nonnegative, "", buf_slice) } + #[cfg(feature = "optimize_for_size")] + fn $name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // 2^128 is about 3*10^38, so 39 gives an extra byte of space + let mut buf = [MaybeUninit::<u8>::uninit(); 39]; + let mut curr = buf.len(); + let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf); + + // SAFETY: To show that it's OK to copy into `buf_ptr`, notice that at the beginning + // `curr == buf.len() == 39 > log(n)` since `n < 2^128 < 10^39`, and at + // each step this is kept the same as `n` is divided. Since `n` is always + // non-negative, this means that `curr > 0` so `buf_ptr[curr..curr + 1]` + // is safe to access. + unsafe { + loop { + curr -= 1; + buf_ptr.add(curr).write((n % 10) as u8 + b'0'); + n /= 10; + + if n == 0 { + break; + } + } + } + + // SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid UTF-8 + let buf_slice = unsafe { + str::from_utf8_unchecked( + slice::from_raw_parts(buf_ptr.add(curr), buf.len() - curr)) + }; + f.pad_integral(is_nonnegative, "", buf_slice) + } + $(#[stable(feature = "rust1", since = "1.0.0")] impl fmt::Display for $t { #[allow(unused_comparisons)] |
