feat(hydro_lang, hydro_deploy): replay captured rustc commands for final test builds, skipping cargo#3045
Closed
shadaj wants to merge 1 commit into
Closed
feat(hydro_lang, hydro_deploy): replay captured rustc commands for final test builds, skipping cargo#3045shadaj wants to merge 1 commit into
shadaj wants to merge 1 commit into
Conversation
Deploying hydro with
|
| Latest commit: |
95a6fa4
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://1fd50ff9.hydroflow.pages.dev |
| Branch Preview URL: | https://sandbox-d501e509-750f-4480-b.hydroflow.pages.dev |
…nal test builds, skipping cargo Continuing the per-test CI cost investigation: with warm caches, per-phase profiling showed the final `cargo rustc` of each generated example dominating at ~1.35s of a ~1.8s test (serial, 32-core). Decomposition of that 1.35s: ~320ms fixed cargo overhead (a no-op `cargo rustc` with nothing dirty), ~180ms rustc frontend, ~240ms codegen, ~430ms link (already on rust-lld), plus ~30ms per-job build-dir population and cargo's cross-process target-dir locking (a likely contention source under nextest parallelism in CI that serial profiling doesn't even see). sccache can't help: the example links a dylib, so the compile is never cacheable. Since every final build against a given prebuild runs an identical rustc command modulo a few per-test values, capture it once and replay it directly: - New `hydro_concurrent_cargo::final_rustc` module (shared so both hydro_lang and hydro_deploy use it): the first final build after a prebuild runs cargo with `-v`, and the printed rustc invocation is parsed (shell-tokenizer handling cargo's unix/windows quoting), validated, and stored as a template with placeholders for the per-test values: crate name, source path, out dir, and `-C metadata`/`-C extra-filename` (unique per test so multiple dlopen'ed cdylibs don't collide). Per-job target dir paths are rewritten to the shared target dir (their deps/.fingerprint/build entries are symlinks into it anyway). Templates live next to the prebuild lock file and are keyed to the prebuild stamp (`features_hash:timestamp`), so any prebuild rerun invalidates them — staleness tracking is exactly as strong as the prebuild's own. - Replay invokes rustc directly (no cargo): substitutes the per-test values, parses rustc's `--json=artifacts` stderr for the linked artifact, and surfaces rendered diagnostics. Any anomaly (parse failure, missing files, compile error) deletes the template and falls back to the unchanged cargo path, which remains the source of truth for command construction and error reporting. - Replay still takes the per-job lock via new `hydro_concurrent_cargo::lock_job_dir` (serializing concurrent builds of an identical flow) but skips job-dir population entirely; no cargo also means no cargo target-dir lock contention. New `PrebuildGuard::read_stamp()` / `lock_path()` expose the stamp for template keying. - `hydro_lang::compile_trybuild_example` (sim + maelstrom tests): replay is used for all non-fuzz builds; fuzz mode keeps the cargo path (different crate layout + linker flags). - `hydro_deploy` `build_crate_memoized` (localhost deploy tests): replay is used for local dylib example builds with default profile/rustflags — the same conditions under which deploy_graph enables dynamic linking; the replayed artifact keeps `shared_library_path` pointing at the shared `debug/deps`. Measured (32-core local, serial, warm): sim test mean 1814ms -> 1391ms (-23%); final compile phase 1347ms (`final_build`) -> 954ms (`final_rustc`), and populate_job_dir (~30ms) disappears from the steady state. The cargo capture build itself is unchanged in cost and happens once per prebuild per feature set. Full `hydro_lang` suite (188 tests incl. sim_fuzzer) and `hydro_test` localhost deploy suite (18 tests) pass, with all steady-state builds confirmed replaying; clippy and fmt clean. Scripts updated: profile_test_phases.sh documents the new `final_rustc` phase; bench_trybuild.sh removes captured templates when touching examples so its warm phase still exercises (and measures) the cargo capture path. Considered and rejected for now: deriving the rustc command from the prebuild's lib invocation (the example target differs in crate-type/emit/externs/link args, so it would mean reimplementing cargo's argv construction) and constructing the command from first principles (fragile against profile settings like `debug = "line-tables-only"` that trybuild copies into the generated workspace). Remaining per-test costs, in case of follow-up: ~430ms link + ~240ms codegen (biggest lever: `-Zlink-native-libraries=no` saved ~175ms but is nightly-only), ~180ms flow_build, ~65ms sim_codegen, ~26ms cargo_metadata. Co-authored-by: Infinity 🤖 <infinity@hydro.run> PR: #3045
shadaj
force-pushed
the
sandbox-d501e509-750f-4480-b494-be09aeb6f835
branch
from
July 17, 2026 22:13
23b9bff to
95a6fa4
Compare
Member
Author
|
This had very little impact on CI times, and introduces a lot of brittle code, so closing for now. |
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.
Continuing the per-test CI cost investigation: with warm caches, per-phase
profiling showed the final
cargo rustcof each generated example dominating at~1.35s of a ~1.8s test (serial, 32-core). Decomposition of that 1.35s:
~320ms fixed cargo overhead (a no-op
cargo rustcwith nothing dirty),~180ms rustc frontend, ~240ms codegen, ~430ms link (already on rust-lld),
plus ~30ms per-job build-dir population and cargo's cross-process target-dir
locking (a likely contention source under nextest parallelism in CI that serial
profiling doesn't even see). sccache can't help: the example links a dylib, so
the compile is never cacheable.
Since every final build against a given prebuild runs an identical rustc
command modulo a few per-test values, capture it once and replay it directly:
hydro_concurrent_cargo::final_rustcmodule (shared so both hydro_langand hydro_deploy use it): the first final build after a prebuild runs cargo
with
-v, and the printed rustc invocation is parsed (shell-tokenizerhandling cargo's unix/windows quoting), validated, and stored as a template
with placeholders for the per-test values: crate name, source path, out dir,
and
-C metadata/-C extra-filename(unique per test so multiple dlopen'edcdylibs don't collide). Per-job target dir paths are rewritten to the shared
target dir (their deps/.fingerprint/build entries are symlinks into it
anyway). Templates live next to the prebuild lock file and are keyed to the
prebuild stamp (
features_hash:timestamp), so any prebuild rerun invalidatesthem — staleness tracking is exactly as strong as the prebuild's own.
parses rustc's
--json=artifactsstderr for the linked artifact, andsurfaces rendered diagnostics. Any anomaly (parse failure, missing files,
compile error) deletes the template and falls back to the unchanged cargo
path, which remains the source of truth for command construction and error
reporting.
hydro_concurrent_cargo::lock_job_dir(serializing concurrent builds of anidentical flow) but skips job-dir population entirely; no cargo also means
no cargo target-dir lock contention. New
PrebuildGuard::read_stamp()/lock_path()expose the stamp for template keying.hydro_lang::compile_trybuild_example(sim + maelstrom tests): replay isused for all non-fuzz builds; fuzz mode keeps the cargo path (different
crate layout + linker flags).
hydro_deploybuild_crate_memoized(localhost deploy tests): replay isused for local dylib example builds with default profile/rustflags — the
same conditions under which deploy_graph enables dynamic linking; the
replayed artifact keeps
shared_library_pathpointing at the shareddebug/deps.Measured (32-core local, serial, warm): sim test mean 1814ms -> 1391ms (-23%);
final compile phase 1347ms (
final_build) -> 954ms (final_rustc), andpopulate_job_dir (~30ms) disappears from the steady state. The cargo capture
build itself is unchanged in cost and happens once per prebuild per feature
set. Full
hydro_langsuite (188 tests incl. sim_fuzzer) andhydro_testlocalhost deploy suite (18 tests) pass, with all steady-state builds confirmed
replaying; clippy and fmt clean.
Scripts updated: profile_test_phases.sh documents the new
final_rustcphase;bench_trybuild.sh removes captured templates when touching examples so its
warm phase still exercises (and measures) the cargo capture path.
Considered and rejected for now: deriving the rustc command from the prebuild's
lib invocation (the example target differs in crate-type/emit/externs/link
args, so it would mean reimplementing cargo's argv construction) and
constructing the command from first principles (fragile against profile
settings like
debug = "line-tables-only"that trybuild copies into thegenerated workspace). Remaining per-test costs, in case of follow-up: ~430ms
link + ~240ms codegen (biggest lever:
-Zlink-native-libraries=nosaved~175ms but is nightly-only), ~180ms flow_build, ~65ms sim_codegen, ~26ms
cargo_metadata.