Repository navigation
mprintf: avoid scanning strings twice for unbounded %s - #23019
Merged
Merged
Conversation
When precision is unspecified, out_string() already knows the exact string length. Pass it directly to stream_run() instead of scanning again in stream_zrun(). Keep the bounded, NUL-aware scan for explicit precision, including non-NUL-terminated buffers. Document why the two paths differ. Closes #23019
cmeister2
force-pushed
the
cm2/printf
branch
from
September 21, 2026 10:17
acec0de to
acec0de
Compare
bagder
approved these changes
Sep 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Avoid scanning ordinary
%sarguments twice in the shared formatter.When precision is unspecified,
out_string()already knows the exact string length, normally fromstrlen(). Previously, it passed that length tostream_zrun(), which scanned the same bytes again before forwarding them tostream_run().This patch sends known-length strings directly to
stream_run(). Explicit precision still uses the existing bounded, NUL-aware scan. A flow comment explains why the two paths differ. This is one shared change benefitingsnprintf,maprintf, andsimpleformat, not three separate optimizations.Assembly justification
The September 21 benchmark report contains x86-64 disassembly from GCC 11.5.0 with
-O2 -DNDEBUG. GCC inlinedout_string()andstream_zrun()intoformatf(). Both versions still callstrlen()for nonempty strings with unspecified precision; the optimization removes the subsequent scan.Before, the following consecutive instructions scan the string again.
r14points to the string,r10holds its already-known length, andr8is the scan index:After, a precision check bypasses that scan. In this version,
r11dholds precision,r13holds length, andr10points to the string. The explicit-precision scan is omitted from this excerpt, as marked:For each eligible string of length N, this removes N redundant byte reads, their NUL tests, and the associated scan-loop work, at the cost of a precision check. Width-only formats such as
%*salso take this path: width does not impose a precision bound.The initial length calculation and actual output copy remain. Fixed-buffer output still clamps writes to available space; allocating output retains its staging, size limits, and allocation handling. This does not introduce SIMD formatting or change the output callbacks.
Recorded performance
The report measured five alternating before/after rounds pinned to CPU 2 on an Intel Core Ultra 7 165H under WSL2, with compilation paused. Both binaries used upstream revision
1ac826a1e064b94000820adc9ac187421ce4f0dband a static GCC 11.5.0-O2 -DNDEBUGbuild. Benchmark programs and inputs were unchanged.snprintfmaprintfsimpleformatEach workload was faster in four of five paired rounds. The removed reads correspond to unspecified-precision string arguments only; they exclude
strlen(), output copying, and literal-format handling. Allocation and free remain included in the allocating benchmark.These are the report's measurements, not a new benchmark of this branch. The measured candidate also included base64 changes, and timings shifted in unchanged routines. The disassembly demonstrates elimination of the redundant scan, but the exact percentages are not isolated to this PR or guaranteed across compilers and platforms.
Safety and validation
Explicit precision is a bound, not necessarily the string length. Its scan must stop at either NUL or the precision limit, preserving both short terminated strings and non-NUL-terminated
%.*sbuffers. Width, padding, truncation, NULL-string handling, positional arguments, and%naccounting remain unchanged.The build and existing formatter tests 557 and 1398 passed. Their coverage includes precision limits, non-NUL-terminated input, padding, truncation, and the
va_listvariants, including allocating output.checksrcandgit diff --checkalso passed. No additional tests were needed for these already-covered behaviors; the full suite, sanitizers, and cross-platform checks were not run for this patch.