RFC: Replacing tinyformat with {fmt} #33942

issue hulxv openend this issue on November 25, 2025
  1. hulxv commented at 10:34 am on November 25, 2025: none

    Description

    Bitcoin Core has used tinyformat (via its strprintf wrapper) for text formatting for many years, a choice that reflected the library’s simplicity and its ability to be dropped into a large C++ codebase with minimal friction. Over time, the C++ standard and the ecosystem around formatting have evolved: {fmt} has become the de facto modern formatting library and served as the basis for std::format in later C++ standards. At the same time, a security and maintenance review of Tinyformat in the Bitcoin context revealed limitations that required ad-hoc fixes. Replacing tinyformat with {fmt} is therefore not merely an API swap; it is an alignment with modern C++ conventions, with a maintained codebase and stronger compile-time guarantees that reduce future maintenance cost.

    The primary motivations for replacing tinyformat with {fmt} can be expressed in concrete, measurable terms. Benchmarks maintained by the {fmt} project and reproduced by early adopters show that {fmt} formats common workloads at speeds close to printf and substantially faster than Tinyformat (which relies on iostreams). In one commonly cited microbenchmark suite, {fmt} completes a heavy formatting loop in roughly 1.3–1.4 seconds while tinyformat finishes the same workload in roughly 2.25–2.6 seconds, which implies an empirical throughput improvement on the order of 1.6x to 2x for formatting-heavy tight loops. These measurements come from the {fmt} benchmark collection and independent reproductions of the original tinyformat benchmark.

    Beyond per-call speed, the difference in generated code and compile-time behavior is also material. The {fmt} project’s benchmark table reports much smaller stripped binaries and faster compile times for equivalent test programs: for a medium test scenario, {fmt} produced a stripped binary in the tens of kilobytes while the tinyformat/io-based alternative produced a binary in the low hundreds of kilobytes, and {fmt}’s measured compile times were lower than the iostream/tinyformat combination in the sample runs. These results indicate that adopting {fmt} can reduce the code-size impact of formatting and improve build characteristics on large codebases where formatting is used frequently.

    Metric {fmt} tinyformat printf / iostream (for reference)
    Execution time for a heavy formatting loop (2M iterations) 1.42 s 2.25 s printf ~1.30 s, iostream ~1.85 s.
    Measured throughput improvement (tinyformat -> {fmt}) {fmt} ~= 1.6x faster than tinyformat
    Stripped binary size for a medium test project 34 KiB 386 KiB
    Compile time for that medium test project 46.8 s 64.6 s
    Approximate binary-size reduction (tinyformat -> {fmt}) {fmt} produces binaries ~11× smaller in this scenario (386 KiB -> 34 KiB)

    reference for benchmarks results from {fmt} team:
    https://github.com/fmtlib/fmt?tab=readme-ov-file#benchmarks

    In addition to all of that, I think that will be a good step toward migrating to C++20 in the future, and we will be able to depend on the standard format library itself.

    There were a lot of discussions to replace tinyformat with {fmt}, and I think the time has come to do that.

  2. maflcko added the label Questions and Help on Nov 25, 2025
  3. maflcko commented at 11:30 am on November 25, 2025: member

    In addition to all of that, I think that will be a good step toward migrating to C++20 in the future, and we will be able to depend on the standard format library itself.

    C++20 is already used, so I’ll go ahead and close this for now.

    I don’t think there is value in switching tinyformat to {fmt} and then again to std::format, with all the churn.

    If the switch is done, it should be straight to std::format.

    So I’ll close this for now, but the discussion can continue.

  4. maflcko closed this on Nov 25, 2025

  5. maflcko commented at 11:33 am on November 25, 2025: member
    For further context: The current minimum required GCC version is 12.1 (https://github.com/bitcoin/bitcoin/blob/master/doc/dependencies.md#compiler), however std::format only comes with GCC libstdc++ 13.1
  6. hulxv commented at 11:38 am on November 25, 2025: none

    For further context: The current minimum required GCC version is 12.1 (https://github.com/bitcoin/bitcoin/blob/master/doc/dependencies.md#compiler), however std::format only comes with GCC libstdc++ 13.1

    For that, I thought about switching first to {fmt} and after that, we can switch to std::format easily when we bump the minimum required GCC version

  7. maflcko commented at 11:43 am on November 25, 2025: member

    Yes, I understand, but there are no meaningful user-visible benefits:

    • Performance does not matter, because formatting isn’t done in hot loops
    • A few KiB of binary size don’t matter, because the binary is several MiB in size
  8. hulxv commented at 11:58 am on November 25, 2025: none
    But from the developers’ view, I think having features like compile-time error-checking and easy API and other features will be helpful to have. At least it will make it a little bit easier. Maybe this is a little thing, but nice to have
  9. maflcko commented at 12:06 pm on November 25, 2025: member
    There is compile-time error-checking already implemented for tinyformat in this codebase, so there is also no developer benefit of using std::format or {fmt}.

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2025-11-26 06:13 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me