🦀 I've improved the implementation behind all the string formatting macros in Rust: println!(), panic!(), format!(), write!(), log::info!(), and so on. (That is, everything based on format_args!().) They will compile a bit faster, use a bit less memory while compiling, result in smaller binaries, and produce more efficient code! 🎉

'Hello world' compiles 3% faster and a few bigger projects like Ripgrep and Cargo compile 1.5% to 2% faster. And those binaries are roughly 2% smaller. 🎊

This change will be available in Rust Nightly tomorrow, and should ship as part of Rust 1.93.0 in January.

#rustlang

Note that there are also lots of programs where this change makes very little difference. Many benchmarks show just 0.5% or 0.1% improvement, or simply zero difference.

The most extreme case is the `large-workspace` benchmark, which is a generated benchmark with hundreds of crates that each just have a few println!() statements. That one now compiles 38% faster and produces a 22% smaller binary.

@Mara excellent news for print-driven-development!
I'm mind blown that you were able to optimize it even more, do you have some explanation on how you achieved this?

It mostly comes down to removing a bunch of indirection, making the struct smaller, and adding a special case for the 'no arguments' case.

Diagrams of before and after:

@Mara amazing 👏 thanks for sharing!
@Mara Cool! Did you use specialized software to create these diagrams?
Flowchart Maker & Online Diagram Software

draw.io is free online diagram software for making flowcharts, process diagrams, org charts, UML, ER and network diagrams

@Mara Is there any write-up of the underlying dark magic (or light magic, if that's the work of a Jedi)

All my work on string formatting in Rust of the past few years is tracked in this tracking issue: https://github.com/rust-lang/rust/issues/99012

I wrote a bit about the history in https://blog.m-ou.se/format-args/ (two years ago).

This latest change is https://github.com/rust-lang/rust/pull/148789 and includes some explanation.

Tracking issue for improving std::fmt::Arguments and format_args!() · Issue #99012 · rust-lang/rust

Earlier this year in the libs team meeting, I presented several different ideas for alternative implementations of std::fmt::Arguments which could result in smaller binary size or higher performanc...

GitHub

@Mara Awesome! 🎉

Do you have a link the PR(s) that landed this? I'd love to see what the changes look like 😄

New format_args!() and fmt::Arguments implementation by m-ou-se · Pull Request #148789 · rust-lang/rust

Part of #99012 This is a new implementation of fmt::Arguments. In this implementation, fmt::Arguments is only two pointers in size. (Instead of six, before.) This makes it the same size as a &s...

GitHub
@rtzoeller Thank you very much!
@Mara Wait the magic trick is turning an array of arrays into a tiny interpreted language?
@Mara Wait did we come full circle back to printf
@natty Yeah, kind of. 😄
@Mara I remember your writeup a couple years ago on this, it looked like such a daunting task! Thank you for pursuing this!
@Mara Thank you very much for your hard work on this!
@Mara great job. Thank you 🥳
@Mara
Great reduction of waste. are there any downsides to the new approach?
for example, a flaw or copy error in a shared macro could potentially be more harmful. on the other hand, such a flaw would now be harder to hide.
I wonder if these considerations have already been made, or if I’m just overthinking it.
@vivasupeR @Mara From where I'm standing it doesn't look like you could overthink it if you tried.
@khleedril @Mara
from what you just said, the place you're standing must be a pretty shitty one.
@Mara I don't know how much you had written before, but I was impressed with the speed with which you opened a PR after I (naïvely) optimized the static case!

@Mara I've heard several people say the old implementation was incredibly arcane and hadn't changed much since the pre-1.0 days.

Regardless kudos to you for rewriting it and improving the performance, one of those wins almost every program benefits from

@bascule Yeah, the problem was that it was incredibly hard to change anything about format_args!() and fmt::Arguments.

The code in the compiler that expands the macro was messy and mixed implementation details of the fmt::Arguments structure into pretty much every part of the code, meaning that you had to rewrite a large chunk of the compiler just to try out a new fmt::Arguments implementation. Clippy used to depend on the exact expansion of format_args!() and would break with any change. During bootstrap, the standard library used to be compiled with both the old and new compiler, meaning it'd need to support both the old and new format_args!() expansion if you changed anything. And there were some stable API promises, such as support for the entire usize range for the width and precision fields, which blocked certain improvements.

It's taken years to slowly remove these roadblocks one by one. We have now *finally* reached a point where it's actually doable to overhaul format_args!() and fmt::Arguments without losing your mind.

See the tracking issue for some of the history: https://github.com/rust-lang/rust/issues/99012

Tracking issue for improving std::fmt::Arguments and format_args!() · Issue #99012 · rust-lang/rust

Earlier this year in the libs team meeting, I presented several different ideas for alternative implementations of std::fmt::Arguments which could result in smaller binary size or higher performanc...

GitHub
@Mara Damn, that is some fine work there. Thank you!
@Mara I thought the performance gain was negligible, then I saw the original implementation needed to toggle the SSE register just to print "Hello, world" on the screen. Certainly an important optimization regardless of CPU time spent.
@Mara wow. Great work!