spdlog-rs icon indicating copy to clipboard operation
spdlog-rs copied to clipboard

Remove the last `format_args!` macro uses from hot paths

Open SpriteOvO opened this issue 2 months ago • 2 comments

Background

In the early stages of spdlog-rs development a few years ago, we had already noticed that the standard library's write! macros are very slow, and this could be proven by simple benchmarking.

In spdlog-rs, we've avoided using write! macros in formatters and sinks as much as possible, and instead use .write_str() to put things together manually, for example

  • Commit 9ff8b016f98a202e6f8d627e340893a2f3498ac1: Use write_all instead of writeln! to improve performance slightly
  • Commit 913234eea1edeccc97e75989a15136541233f052: Improve performance of built-in patterns

Because of these optimizations, performance in formatting has been significantly improved. However, there are still some remaining uses of format_args! that have not been completely removed, for example

https://github.com/SpriteOvO/spdlog-rs/blob/654b440d51c298983c8c0427665d1164157d33f4/spdlog/src/formatter/full_formatter.rs#L65-L72

https://github.com/SpriteOvO/spdlog-rs/blob/654b440d51c298983c8c0427665d1164157d33f4/spdlog/src/formatter/full_formatter.rs#L85-L92

https://github.com/SpriteOvO/spdlog-rs/blob/654b440d51c298983c8c0427665d1164157d33f4/spdlog/src/formatter/pattern_formatter/pattern/datetime.rs#L326-L340

What they have in common is that they write an integer to buffer and may with padding. However, writing integers without allocation with just stdlib is not possible in stable Rust today. If you're in nightly Rust, you can do this manually and probably improve performance.

use std::fmt::Display as _;

let mut opt = std::fmt::FormattingOptions::new(); // unstable
opt.width(Some(9)).fill('0'); // unstable
nanosecond.fmt(std::fmt::Formatter::new(&mut dest, opt) /* unstable */);

Solution

I found two crates that can convert integers to &str or &[u8] without allocation, which will most likely allow us to get rid of the last uses of format_args! and improve performance a bit further.

numtoa

mmstick/numtoa#5

itoa

dtolnay/itoa#46

They each have some issues and I'm not sure which one is better, so I'll test and benchmark them separately for spdlog-rs and then decide which one to use.

SpriteOvO avatar Sep 27 '25 15:09 SpriteOvO

Turns out itoa doesn't support leading zeros at the moment, I have opened a issue for it. dtolnay/itoa#61

Since we are using formats like {:03}, it looks like numtoa is the only option for now for spdlog-rs.

SpriteOvO avatar Sep 27 '25 15:09 SpriteOvO

The {integer}_padded functions in numtoa only supports padding length >= {integer}::BUF_LEN, it's still hard to replace std formatting like {:03} for integer type u32 with numtoa.

I have opened a PR to upstream adding new functions {integer}_filled to solve the problem.

mmstick/numtoa#40

SpriteOvO avatar Sep 27 '25 17:09 SpriteOvO