fmt
fmt performance: buffer growth, compiled formats and honest benchmarks
Inspect the 500-element inline buffer and growth policy, separate format checking from FMT_COMPILE, and measure formatting, allocation, I/O and build costs independently.
What you will learn
- The buffer has a policy, not a universal no-allocation promise
- Checking a format is not compiling its formatter
- Measure the work your application pays for
Before you start
- Basic C++ values, references, strings and build targets
- Ability to distinguish a documented expectation from a measured result
Trace concrete fmt behavior and choose an integration with explicit output, lifetime and verification boundaries.
Key takeaways
- The 500-element inline buffer belongs to a specific type and revision.
- Compiled format strings trade specialization against potential binary growth.
- Separate formatting, allocation, I/O and build measurements; do not invent savings.
The buffer has a policy, not a universal no-allocation promise
At the inspected revision, basic_memory_buffer defaults to 500 inline elements; the common char alias therefore starts with 500 bytes of inline character storage. Growth begins with old_capacity + old_capacity / 2, then accommodates a larger requested size and allocator limits. This is a concrete storage policy that can be inspected and tested.
The simplified arithmetic example below deliberately ignores allocator limits and overflow, and is restricted to small nonnegative inputs. It predicts a capacity of 750 when growing from 500 to satisfy 501, and 900 when a single request needs 900. These numbers describe that policy, not measured throughput or a promise that all fmt::format calls avoid allocation: converting the buffer into std::string can introduce another allocation.
Checking a format is not compiling its formatter
Literal format checking catches mismatches on supported C++20 configurations. FMT_COMPILE goes further by parsing a fixed format and generating specialized formatting code on the supported compiler path. The compile.h macro requires C++17 constexpr-if facilities for that path and falls back to FMT_STRING otherwise. Compiler features therefore affect what a macro invocation actually does.
The API guide warns that compiled formats may increase binary code size and recommends them for demonstrated formatting bottlenecks. Compare representative hot call sites instead of replacing every format string mechanically. Report compile time, linked binary size and runtime together; a faster isolated loop may still be a poor trade for a large application with many distinct strings.
Measure the work your application pays for
Keep string formatting, allocation and output I/O as separate benchmark phases. Use the same values, presentation rules, optimization settings and toolchain, and ensure results are consumed so the compiler cannot remove the work. Include short and long messages, reused buffers and representative custom types rather than only one attractive integer case.
formatted_size traverses the formatting path with a counting buffer, while format_to_n continues accounting for untruncated output. Computing size and then formatting is not automatically a free two-step optimization, and a small write cap is not proof of bounded CPU cost for a huge requested width. This review supplies no comparative throughput, latency, allocation-count or monetary saving claim.
Implementation steps
- 1
Fix the revision, compiler, optimization mode and representative inputs.
- 2
Measure formatting separately from output-device latency.
- 3
Compare default formatting, buffer reuse and selected compiled hot strings.
- 4
Publish binary/build effects and keep unmeasured values explicitly unknown.
Copy-ready example
function teachingCapacity(oldCapacity, requested) {
return Math.max(oldCapacity + Math.floor(oldCapacity / 2), requested);
}
console.log(teachingCapacity(500, 501), teachingCapacity(500, 900));
// 750 900; small-input model, not an allocator or benchmarkFrequently asked questions
Does an inline buffer prove fmt::format never allocates?
No. Larger outputs can grow the buffer, and the returned std::string has its own storage behavior.
Should every literal use FMT_COMPILE?
Not without measurement. The upstream guide identifies possible binary growth and recommends it where formatting is a demonstrated bottleneck.
Sources
- README.mdSource checked 2026-09-08
- doc/api.mdSource checked 2026-09-08
- include/fmt/core.hSource checked 2026-09-08
- include/fmt/format.hSource checked 2026-09-08
- include/fmt/compile.hSource checked 2026-09-08