uucore: Remove ARGV cache - #13276
Conversation
sylvestre
left a comment
There was a problem hiding this comment.
the AI comment #0 isn't useful.
Please write it for a human as I am not an AI
|
Please update comment 0 |
|
GNU testsuite comparison: |
Merging this PR will regress 2 benchmarks
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ❌ | Simulation | ls_recursive_wide_tree[(10000, 1000)] |
84.5 ms | 87.4 ms | -3.37% |
| ❌ | Simulation | sort_long_line[10000] |
1.1 ms | 1.2 ms | -3.25% |
| ⚡ | Simulation | expand_many_lines[100000] |
116.4 ms | 103.5 ms | +12.47% |
| ⚡ | Simulation | expand_custom_tabstops[50000] |
31.4 ms | 28.8 ms | +9.19% |
| ⚡ | Simulation | complex_relative_date |
330.6 µs | 316.2 µs | +4.55% |
| ⚡ | Simulation | cksum_crc32b |
42.3 ms | 40.7 ms | +3.93% |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing AnuthaDev:arghh-alloc (9bad806) with main (e1efa7d)
Footnotes
-
46 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩
|
Updated the Summary section in description (if that's what you meant by comment 0) |
|
this one: |
|
i still would like comment #0 to be shorter |
|
Improved the comment. Will create a separate PR to add a memory benchmark. Please review. |
i am waiting for this to review this PR |
|
RAM usage is reduced and already catched by benches https://app.codspeed.io/uutils/coreutils/branches/AnuthaDev%3Aarghh-alloc?utm_source=github&utm_medium=comment-v2&utm_content=button&q=mode%3Amemory |
|
@sylvestre Please let me know if the existing benchmark pointed out by @oech3 is enough or I need to add a separate benchmark |
|
ok, thanks |
|
@sylvestre I can see that you have enabled auto-merge, but its still pending on your approval since you had requested changes earlier. |
|
@sylvestre You need to click on "Approve PR" for it to get merged. Its blocked due to your earlier review.
|
|
@sylvestre The merge is blocked on your approval. You need to submit another review with "Approved". |
Head branch was pushed to by a user without write access
|
@sylvestre Please review |
There was a problem hiding this comment.
Pull request overview
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Removes the lifetime ARGV cache in uucore and replaces it with one-time initialization for UTIL_NAME / EXECUTION_PHRASE, while making args_os() return fresh argv iterators to reduce persistent allocations.
Changes:
- Replaced
LazyLock-backedARGVcaching withOnceLockforUTIL_NAMEandEXECUTION_PHRASE. - Updated
bin_inner!and binaries to initialize the utility name / execution phrase from the first argv observation. - Updated
args_os()/args_os_filtered()semantics and docs to reflect non-cached argv iteration.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| src/uucore/src/lib/lib.rs | Removes cached ARGV; introduces OnceLock init APIs and updates argv iteration behavior/docs. |
| src/bin/uudoc.rs | Initializes UTIL_NAME / EXECUTION_PHRASE explicitly for the manpage flow. |
| src/bin/coreutils.rs | Initializes UTIL_NAME / EXECUTION_PHRASE depending on whether the utility is argv[0] or argv[1]. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| let name = std::path::Path::new(input).file_name().unwrap_or(input); | ||
| let result = UTIL_NAME.set(name.to_string_lossy().into_owned()); | ||
| debug_assert!( | ||
| result.is_ok(), | ||
| "init_util_name called after UTIL_NAME was already initialized" | ||
| ); |
| let result = EXECUTION_PHRASE.set(input.as_ref().to_string_lossy().into_owned()); | ||
| debug_assert!( | ||
| result.is_ok(), | ||
| "init_execution_phrase called after EXECUTION_PHRASE was already initialized" | ||
| ); |
| /// | ||
| /// Each call copies all of argv (and, on Windows, re-expands glob patterns), | ||
| /// so call it once and reuse the result rather than calling it repeatedly. | ||
| pub fn args_os() -> impl Iterator<Item = OsString> { |
| if uucore::get_utility_is_second_arg() { | ||
| uucore::init_util_name(&util_os); | ||
| let mut phrase = binary.clone().into_os_string(); | ||
| phrase.push(" "); | ||
| phrase.push(&util_os); | ||
| uucore::init_execution_phrase(&phrase); | ||
| } else { | ||
| uucore::init_util_name(&binary); | ||
| uucore::init_execution_phrase(&binary); | ||
| } |

Problem
uucorekeeps the CLI arguments cached in a static variable (ARGV), stating that repeatedly callingenv::args_osis expensive. Howeverenv::args_osis only called once during the binary lifecycle. Storing theARGVis useless overhead.Fix
ARGVvariable and added a different mechanism to initializeUTIL_NAMEandEXECUTION_PHRASE.init_util_name(),init_execution_phrase()that use the result of the firstargs_oscall to set these variables. This is to prevent an additional call toargs_osfor determining these values.get_or_initfor these values instead of panicking on uninitialized values so any downstream users ofuucorekeep working, albeit with a performance hit.Breaking changes
Two semantic changes for downstream consumers of the
uucorecrate:uucore::args_os()is no longer cheap to call repeatedly. It previously returned clones from a process-lifetime cache; it now copies all of argv (and re-expands globs viawildon Windows) on every call. Call it once and reuse the result.init_*functions, those values win over derivation from argv. Callers that never init see identical behavior to before.Measurements
printf "00 %.0s" {1..100000} > args.txtmain:This PR:
GNU echo:
The uutils version still allocates
332times more (down from653times), still lots of scope for reducing allocations.Impact
This change reduces memory allocation for all the coreutils binaries. The savings scale with argv: total allocations drop by one full argv copy, and the duplicate no longer persists for the process lifetime.
Practical benefit: Calling
ls pattern*in a directory with a large number of items matching the pattern allocates much less.Testing