[benchmarking] Multiple fixes to stabilize the nightly benchmark suite#2035
Conversation
|
Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually. Contributors can view more details about this message here. |
| --engine-kwargs='{"tensor_parallel_size": 1}' | ||
| --autoscaling-config='{"min_replicas": 4, "max_replicas": 4}' | ||
| timeout_s: 700 | ||
| timeout_s: 1200 # warm-run wall ~700s observed; headroom added for cold vLLM model load (cf. ndd_dynamo_dp4: 2700) |
There was a problem hiding this comment.
I'm wondering why this happened now?
There was a problem hiding this comment.
Because even dynamo should not take 2700s rn since we upgraded versions to 1.1.0 (i.e.we can reduce that)
There was a problem hiding this comment.
I need to investigate that. I know we had success on the DGX-A100 machine with 700s, but I don't know yet if a larger timeout is needed for the other machine running nightlies because it's slower in general, or if something else is causing a longer runtime on the other machine.
`resolve_env_vars` previously raised `ValueError` on the first undefined
`${VAR}` reference, halting the entire session even when the missing var
was only used by a few entries. The default is now to substitute an empty
string and log a warning. Pass `--strict-config-check` to `run.py` to
restore the old fail-fast behavior.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: rlratzel <rratzel@nvidia.com>
In nemo-ci pipeline 52840568 / leaf 52841002, ndd_ray_serve_dp4 was SLURM-killed by TIME LIMIT at ~11:30 wall — only seconds after its benchmark subprocess succeeded (Output 853/853, benchmark wall 249s, total subprocess wall ~450s). The existing timeout_s: 700 converts to SLURM --time=00:11:40, giving no headroom for Ray teardown or cold vLLM model load. Bump to 1200s (20 min): - ~70% headroom over the observed warm-run wall - Still well below ndd_dynamo_dp4's 2700s ceiling, which is documented for cold flash-attn / gpt-oss-20b loads Reference failed job: https://gitlab-master.nvidia.com/dl/JoC/nemo-ci/-/jobs/327777542 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: rlratzel <rratzel@nvidia.com>
The existing --entries flag uses pytest's "-k" expression evaluator, which does substring matching on bare identifiers. This is correct for interactive use but dangerous for automated callers that target a single known entry: passing --entries foo also selects foo_repeat, foo_extra, etc. Concrete failure: in nemo-ci pipeline 52840568 / leaf 52841002, the SLURM job for entry "audio_tagging_tts_xenna" was invoked with --entries audio_tagging_tts_xenna, which also matched the sibling "audio_tagging_tts_xenna_repeat". That entry was executed within the non-_repeat SLURM job, leaving a logs/ray.log file in the _repeat entry's per-entry results dir. The legitimate _repeat SLURM job then crashed at Ray cluster setup because logs/ is preserved by design (run.py:175-178) and the stale ray.log capture file collided. Changes: * benchmarking/run.py: add --entry-exact-name argparse flag (mutually exclusive with --entries); pass through to Session.from_dict. * benchmarking/runner/session.py: extend Session.from_dict to accept entry_exact_name; when set, filter by exact entry-name equality (takes precedence over entry_filter_expr; passing both raises ValueError). * benchmarking/tools/ci_benchmark_launcher.sh: switch CI per-job invocation from --entries to --entry-exact-name. ENTRY_NAME is already populated by the per-entry CI job generator with the exact entry name, so no value change is needed. Interactive --entries behavior is unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: rlratzel <rratzel@nvidia.com>
Brings the new flag in line with --entries in both look and feel: * Accepts a comma-separated list of one or more exact entry names, not just a single name. This matches the mental model of --entries (which conceptually selects a set of entries) and lets a single invocation target any subset by exact name. * Every name in the list must exactly match a configured (enabled) entry; otherwise the run aborts with a ValueError that lists the unknown names alongside the available entry names. This makes typos a hard error rather than a silent no-op. * Duplicates in the input are collapsed; result order follows the YAML, matching how --entries behaves. * benchmarking/run.py: rename argparse flag --entry-exact-name to --entries-exact; parse comma-separated value into list[str]; reject empty / whitespace-only inputs; wrap Session.from_dict in try/except to surface ValueError as a clean CLI error. * benchmarking/runner/session.py: rename parameter entry_exact_name to entries_exact (list[str]); add strict validation that every requested name matches a configured entry; error message lists both missing and available names. * benchmarking/tools/ci_benchmark_launcher.sh: rename flag in the CI per-job invocation; ENTRY_NAME is a single name today so this works as a single-element list. Interactive --entries semantics unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: rlratzel <rratzel@nvidia.com>
ed1855e to
7b35169
Compare
In nemo-ci pipeline 53092974 / leaf 53093663, exact_dedup_identification was SLURM-killed by TIME LIMIT at 68% (516/755) into the "Inserting into shuffler" phase. The shuffler ran at ~1.83 it/s on EOS H100, so the shuffler phase alone needs ~410s, plus Ray cluster setup, dataset stat, post-shuffle dedup compute, and cleanup — total wall ~800-1000s, not fitting in a 500s budget. The previous 500s value was set when the test was effectively a no-op (the rpv2 dataset was unreadable due to filesystem permissions, so the test failed fast on stat() before doing any real work — see PR description for the rpv2 access fix story). 500s was also reportedly sufficient on a faster system; EOS may simply be slower for this workload. Worth investigating after the test is unblocked. Bump to 1500s (25 min): - ~50% headroom over the estimated 1000s realistic wall on EOS - Parallel in spirit to the ndd_ray_serve_dp4 700 -> 1200 bump earlier in this PR Reference failed job: https://gitlab-master.nvidia.com/dl/JoC/nemo-ci/-/jobs/329434352 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: rlratzel <rratzel@nvidia.com>
The math benchmarks (math_preprocess, math_preprocess_classifier, math_preprocess_llm_cleanup) shell out to the lynx text browser via nemo_curator/stages/math/download/html_extractors/lynx.py for HTML extraction. lynx is not present in the Curator benchmark container, so those benchmarks currently fail with: RuntimeError: lynx executable not found in PATH lynx is GPL-licensed, so we deliberately do not bake it into the redistributable Curator image. Instead it is installed transiently in the existing benchmark container at CI run time. The image we publish stays GPL-free; the apt-installed lynx lives only for the lifetime of the CI container. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: rlratzel <rratzel@nvidia.com>
Adds `delete_scratch: false` to the fuzzy_dedup_identification entry so
its scratch directory (under session_entry_dir/scratch/{cache,output})
is retained after the entry finishes. The downstream dedup_removal_*
benchmarks read these artifacts at known paths, so the prior default
session-level cleanup (delete_scratch: true) was wiping them out
before they could be consumed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: rlratzel <rratzel@nvidia.com>
Both domain_classification_raydata and domain_classification_xenna have a requirement on domain_label_games_count with exact_value: 149816. Run-to-run output of the classifier drifts by several hundred to a few thousand classifications, causing repeated false failures while the actual benchmark (throughput, total docs, number of domains predicted) is healthy. Loosens the metric to a +/- 5% range (142325 .. 157307) which still catches genuine regressions of the classifier output while tolerating normal run-to-run variability. domain_label_news_count is left at exact_value: 2817 pending further investigation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: rlratzel <rratzel@nvidia.com>
…rk_env_check Signed-off-by: rlratzel <rratzel@nvidia.com>
Both dedup_removal entries had timeout_s values that fit when the test was effectively a no-op (it failed fast on stale fuzzy_id_generator artifacts before doing real work). Once the upstream fuzzy_id_generator data was refreshed, the actual benchmarks ran for real: * dedup_removal_raydata observed wall ~1419s (92% complete when killed at the prior 1100s ceiling) -> 1800s (~27% headroom over the estimated 1500s full wall) * dedup_removal_xenna passed at 1523s wall with the prior 1500s ceiling (no margin) -> 1800s as a preemptive bump to match raydata and absorb run-to-run variance Reference: nemo-ci pipeline 53333039 / leaf 53333328 — raydata killed by SLURM TIME LIMIT during normal pipeline execution while xenna finished successfully but with zero headroom. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: rlratzel <rratzel@nvidia.com>
The earlier delete_scratch: false override on fuzzy_dedup_identification
was added so the entry's scratch/output artifacts could be picked up
by the downstream dedup_removal_* benchmarks within the same pipeline.
In practice the artifacts are consumed via the canonical dataset path:
{datasets_path}/cleaned_exact_dedup_all_cc_fuzzy_output_nightly_container_paths/
fuzzy_id_generator.json
FuzzyDuplicateIds/
Operators promote a known-good fuzzy_dedup output to this path once,
and dedup_removal_raydata / dedup_removal_xenna consume it from there
on every subsequent pipeline (no same-pipeline dependency). With that
workflow in place, the per-entry delete_scratch override is no longer
needed and just leaves unused data on lustre across runs. Revert to
the session-level default (delete_scratch: true).
This reverts commit 6369150.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: rlratzel <rratzel@nvidia.com>
For cross-host benchmark comparisons (e.g. A100 vs H100) the host's /dev/shm size differs (~550 GB vs ~1 TB) and the container inherits the host default. Provide an opt-in env var to remount /dev/shm at a chosen size. Unset preserves prior behavior. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: rlratzel <rratzel@nvidia.com>
Background `nvidia-smi` poller writes one CSV per entry covering all GPUs on the node, independent of CUDA_VISIBLE_DEVICES. Lets us verify post-run whether Ray/Xenna actually honored the visible-device mask (any nonzero util on masked indices ⇒ leakage). Unset → no polling, preserving prior behavior. Subprocess is killed on EXIT via trap so a python crash doesn't leave it orphaned. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: rlratzel <rratzel@nvidia.com>
The benchmarking container cap of 1 TiB on the A100 host shrinks the container's visible memory to ~50% of host and shm to ~25%, which makes env.json report 1024 GiB / 512 GiB even though the host has ~2 TiB / ~1008 GiB. EOS reports the host values directly, so the A100 vs EOS comparison shows an artificial environmental mismatch. Raising the cap to 2 TiB lets A100's container see the host's full memory, matching EOS. Also remove the CURATOR_SHM_SIZE_BYTES env-var block from ci_benchmark_launcher.sh: pyxis-on-EOS does not grant CAP_SYS_ADMIN, so the remount silently fell back to the WARNING branch and never applied. With A100 raised to match EOS, the toggle is no longer needed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: rlratzel <rratzel@nvidia.com>
The prior CURATOR_GPU_POLL_INTERVAL_S nvidia-smi block in
ci_benchmark_launcher.sh polled GPUs by spawning nvidia-smi as a background
process. That implementation was bash-only, lived outside the runner's
lifecycle, and named output files ad-hoc.
Replace with a Python GPUStatsRecorder that uses gpustat (already a curator
dep) on a daemon thread, started/stopped as a context manager around each
entry's subprocess in run_entry. Per-entry CSV is written to
{session_entry_path}/gpustats.csv with columns: timestamp_utc, gpu_id,
utilization_gpu_pct, utilization_memory_pct, temperature_c, processes
(JSON-encoded list of {pid, username, command, gpu_memory_usage}).
Polling cadence is configured via a new top-level YAML key
`gpu_stats_recorder.interval_s` in the benchmark config (default 1.0;
set to 0 to disable). The recorder polls all visible GPUs regardless of
CUDA_VISIBLE_DEVICES, which lets us verify post-run that Ray/Xenna
honored the visible-device mask.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: rlratzel <rratzel@nvidia.com>
Curator-side counterpart of the upcoming nemo-ci curator_benchmark_launch
wrapper (still under design). Adds three small, backwards-compatible
pieces of plumbing so per-launch metadata (session name, viewer URL,
run reason) can flow from the launcher all the way through to the Slack
parent message and env.json — gated behind new env vars / flags so
today's nightly schedule is unaffected.
* ci_benchmark_launcher.sh:
- Honor NEMO_CI_SESSION_NAME when set; otherwise pick nightly-<TS>
for scheduled (cron) pipelines and the legacy benchmark_run_<id>
name for direct launch_pipeline.py manual launches.
- Compose a run-viewer URL only when NEMO_CI_VIEWER_HOST is set, using
the host-side lustre path and the resolved session name.
- Pass --viewer-url and --run-reason through to run.py when set.
* run.py:
- New --viewer-url and --run-reason CLI flags (both default None).
- When --run-reason is set, inject it into env_dict before sinks
initialize, so it persists to env.json and appears in the Slack
environment block.
- When --viewer-url is set, patch the Slack sink's sink_config
in-process (no YAML schema change required) before sinks initialize.
* slack_sink.py:
- SlackSink reads optional viewer_url from sink_config.
- SlackParentMessage accepts viewer_url and renders a "Results viewer:
<url|open run>" section between the overall-status block and the
environment table — only when set.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: rlratzel <rratzel@nvidia.com>
praateekmahajan
left a comment
There was a problem hiding this comment.
LGTM! Thanks for this 🙏
power_draw / power_limit / fan_speed are populated by the same NVML query gpustat.new_query() already issues for utilization/memory/temp, so reading them adds no measurable overhead (no extra NVML calls). Datacenter SKUs (H100/A100 chassis cards) usually return None for fan_speed since there's no controllable per-card fan; render None as empty string for CSV cleanliness. Same handling for power_draw / power_limit on the rare hardware that doesn't expose them. CSV columns now: timestamp_utc, gpu_id, utilization_gpu_pct, utilization_memory_pct, temperature_c, power_draw_w, power_limit_w, fan_speed_pct, processes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: rlratzel <rratzel@nvidia.com>
Tighter than the original 142325..157307 (+/-5% around the observed 149816); now 148318..151314 (+/-1%). Catches smaller classifier-output drifts that the prior wider window absorbed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: rlratzel <rratzel@nvidia.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: rlratzel <rratzel@nvidia.com>
…rk_env_check Signed-off-by: rlratzel <rratzel@nvidia.com>
Greptile SummaryThis PR bundles eight independent fixes to stabilize the Curator nightly benchmark suite, improving the pass rate from 23/37 to 40/41 entries. Changes span timeout bumps, a new
Confidence Score: 4/5Safe to merge for the benchmark stability fixes; the viewer-URL feature introduced here will silently not work until the one-line attribute assignment is corrected. The core fixes — timeout bumps, --entries-exact, non-fatal env vars, lynx install — are all straightforward and well-verified in the referenced CI pipelines. The GPUStatsRecorder is a best-effort side-channel that cannot affect benchmark correctness. The only functional defect is that --viewer-url won't surface in Slack messages because self.viewer_url is set at SlackSink.init time and the post-init sink_config patch has no effect; this breaks a new observability feature but nothing that was working before. benchmarking/run.py and benchmarking/runner/sinks/slack_sink.py for the viewer_url propagation issue; benchmarking/runner/gpu_stats_recorder.py for the CSV header discrepancy vs. documentation. Important Files Changed
Sequence DiagramsequenceDiagram
participant CLI as run.py (main)
participant Session
participant SlackSink
participant RunEntry as run_entry()
participant GPURec as GPUStatsRecorder
participant Subprocess
CLI->>Session: "from_dict(config, entries_exact=[...])"
Session-->>CLI: session (filtered entries)
CLI->>SlackSink: __init__(sink_config)
Note over SlackSink: self.viewer_url = sink_config.get(viewer_url) = None
CLI->>SlackSink: "sink_config[viewer_url] = args.viewer_url"
Note over CLI,SlackSink: self.viewer_url unchanged (still None)
CLI->>SlackSink: initialize(session_name, session, env_dict)
SlackSink->>SlackSink: "_create_parent_message(viewer_url=self.viewer_url=None)"
loop for each entry
CLI->>RunEntry: run_entry(..., gpu_stats_recorder_interval_s)
RunEntry->>GPURec: __enter__() start()
GPURec-->>RunEntry: daemon thread polling gpustats.csv
RunEntry->>Subprocess: run_command_with_timeout(cmd, timeout)
Subprocess-->>RunEntry: run_data
RunEntry->>GPURec: __exit__() stop()
GPURec-->>RunEntry: CSV flushed and closed
RunEntry-->>CLI: success/failure
CLI->>SlackSink: register_benchmark_entry_finished(result)
end
CLI->>SlackSink: finalize()
Reviews (1): Last reviewed commit: "Merge remote-tracking branch 'upstream/m..." | Re-trigger Greptile |
|
|
||
| # Optional run-viewer URL surfaced as a "Results viewer" link in the parent | ||
| # Slack message. Typically populated in-process by run.py from --viewer-url. | ||
| self.viewer_url: str | None = sink_config.get("viewer_url") |
There was a problem hiding this comment.
--viewer-url silently dropped — self.viewer_url is never updated after __init__
self.viewer_url is assigned once from sink_config.get("viewer_url") at init time. In run.py, the CLI flag patches sink.sink_config["viewer_url"] = args.viewer_url after init, but that only mutates the backing dict — self.viewer_url still holds None when initialize() later calls _create_parent_message(viewer_url=self.viewer_url). The URL will never appear in the Slack parent message.
The fix is to also set sink.viewer_url = args.viewer_url directly in run.py, or to re-read it at the start of initialize().
| def stop(self) -> None: | ||
| if self._thread is None: | ||
| return | ||
| self._stop_event.set() | ||
| self._thread.join(timeout=10.0) | ||
| self._thread = None | ||
| if self._csv_file is not None: | ||
| self._csv_file.close() | ||
| self._csv_file = None | ||
| self._csv_writer = None |
There was a problem hiding this comment.
Potential write-after-close if background thread outlives 10 s join timeout
stop() calls self._stop_event.set(), then self._thread.join(timeout=10.0). If _poll_once blocks longer than 10 s (e.g., NVML hangs), the join returns but the thread is still alive. _csv_file.close() is then called immediately, leaving the running thread free to call self._csv_writer.writerow(...) on a closed file. Since _poll_loop swallows all exceptions, the next write silently fails or raises an OSError that is only logged at WARNING level — no data is lost for the benchmark itself, but the CSV could be left truncated or corrupted.
| HEADER: ClassVar[list[str]] = [ | ||
| "timestamp_utc", | ||
| "gpu_id", | ||
| "utilization_gpu_pct", | ||
| "utilization_memory_pct", | ||
| "temperature_c", | ||
| "power_draw_w", | ||
| "power_limit_w", | ||
| "fan_speed_pct", | ||
| "processes", | ||
| ] |
There was a problem hiding this comment.
CSV header in code does not match the documented 6-column format
The PR description and the test plan both say the expected CSV header is timestamp_utc,gpu_id,utilization_gpu_pct,utilization_memory_pct,temperature_c,processes, but the actual HEADER constant here contains 9 fields, adding power_draw_w, power_limit_w, and fan_speed_pct between temperature_c and processes. Any downstream consumer or CI validation that checks for the 6-column format documented in the test plan (CSV header matches timestamp_utc,...,processes) will fail.
| RESULTS_HOST_DIR="${DEFAULT_CLUSTER_DIR}/curator_ci/results/${BRANCH_NAME}/${SESSION_NAME}" | ||
| ENC_DIR=$(python3 -c 'import sys, urllib.parse; print(urllib.parse.quote(sys.argv[1]))' "${RESULTS_HOST_DIR}") | ||
| ENC_RUN=$(python3 -c 'import sys, urllib.parse; print(urllib.parse.quote(sys.argv[1]))' "${SESSION_NAME}") | ||
| VIEWER_URL="http://${NEMO_CI_VIEWER_HOST}/run-viewer?dir=${ENC_DIR}&run=${ENC_RUN}" |
There was a problem hiding this comment.
Viewer URL constructed over plain HTTP, not HTTPS
VIEWER_URL="http://${NEMO_CI_VIEWER_HOST}/..." hardcodes http://. If the viewer host is exposed over HTTPS (which is typical for internal dashboards), the link sent to Slack will be an unclickable or redirected HTTP URL. Consider using https:// by default, or parameterizing the scheme as part of NEMO_CI_VIEWER_HOST.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| if args.viewer_url: | ||
| for sink in session.sinks: | ||
| if getattr(sink, "name", None) == "slack": | ||
| sink.sink_config["viewer_url"] = args.viewer_url |
There was a problem hiding this comment.
Since
self.viewer_url is already set from sink_config at init time, patching sink.sink_config["viewer_url"] has no effect on the value _create_parent_message will see. Setting the attribute directly ensures the URL is visible when initialize() runs.
| if args.viewer_url: | |
| for sink in session.sinks: | |
| if getattr(sink, "name", None) == "slack": | |
| sink.sink_config["viewer_url"] = args.viewer_url | |
| if args.viewer_url: | |
| for sink in session.sinks: | |
| if getattr(sink, "name", None) == "slack": | |
| sink.sink_config["viewer_url"] = args.viewer_url | |
| sink.viewer_url = args.viewer_url |
NVIDIA-NeMo#2035) * [benchmarking] Make undefined env vars in config non-fatal by default `resolve_env_vars` previously raised `ValueError` on the first undefined `${VAR}` reference, halting the entire session even when the missing var was only used by a few entries. The default is now to substitute an empty string and log a warning. Pass `--strict-config-check` to `run.py` to restore the old fail-fast behavior. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Signed-off-by: rlratzel <rratzel@nvidia.com> * benchmark: bump ndd_ray_serve_dp4 timeout_s 700 -> 1200 In nemo-ci pipeline 52840568 / leaf 52841002, ndd_ray_serve_dp4 was SLURM-killed by TIME LIMIT at ~11:30 wall — only seconds after its benchmark subprocess succeeded (Output 853/853, benchmark wall 249s, total subprocess wall ~450s). The existing timeout_s: 700 converts to SLURM --time=00:11:40, giving no headroom for Ray teardown or cold vLLM model load. Bump to 1200s (20 min): - ~70% headroom over the observed warm-run wall - Still well below ndd_dynamo_dp4's 2700s ceiling, which is documented for cold flash-attn / gpt-oss-20b loads Reference failed job: https://gitlab-master.nvidia.com/dl/JoC/nemo-ci/-/jobs/327777542 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: rlratzel <rratzel@nvidia.com> * benchmark: add --entry-exact-name flag for exact entry matching The existing --entries flag uses pytest's "-k" expression evaluator, which does substring matching on bare identifiers. This is correct for interactive use but dangerous for automated callers that target a single known entry: passing --entries foo also selects foo_repeat, foo_extra, etc. Concrete failure: in nemo-ci pipeline 52840568 / leaf 52841002, the SLURM job for entry "audio_tagging_tts_xenna" was invoked with --entries audio_tagging_tts_xenna, which also matched the sibling "audio_tagging_tts_xenna_repeat". That entry was executed within the non-_repeat SLURM job, leaving a logs/ray.log file in the _repeat entry's per-entry results dir. The legitimate _repeat SLURM job then crashed at Ray cluster setup because logs/ is preserved by design (run.py:175-178) and the stale ray.log capture file collided. Changes: * benchmarking/run.py: add --entry-exact-name argparse flag (mutually exclusive with --entries); pass through to Session.from_dict. * benchmarking/runner/session.py: extend Session.from_dict to accept entry_exact_name; when set, filter by exact entry-name equality (takes precedence over entry_filter_expr; passing both raises ValueError). * benchmarking/tools/ci_benchmark_launcher.sh: switch CI per-job invocation from --entries to --entry-exact-name. ENTRY_NAME is already populated by the per-entry CI job generator with the exact entry name, so no value change is needed. Interactive --entries behavior is unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: rlratzel <rratzel@nvidia.com> * benchmark: rename --entry-exact-name to --entries-exact (list, strict) Brings the new flag in line with --entries in both look and feel: * Accepts a comma-separated list of one or more exact entry names, not just a single name. This matches the mental model of --entries (which conceptually selects a set of entries) and lets a single invocation target any subset by exact name. * Every name in the list must exactly match a configured (enabled) entry; otherwise the run aborts with a ValueError that lists the unknown names alongside the available entry names. This makes typos a hard error rather than a silent no-op. * Duplicates in the input are collapsed; result order follows the YAML, matching how --entries behaves. * benchmarking/run.py: rename argparse flag --entry-exact-name to --entries-exact; parse comma-separated value into list[str]; reject empty / whitespace-only inputs; wrap Session.from_dict in try/except to surface ValueError as a clean CLI error. * benchmarking/runner/session.py: rename parameter entry_exact_name to entries_exact (list[str]); add strict validation that every requested name matches a configured entry; error message lists both missing and available names. * benchmarking/tools/ci_benchmark_launcher.sh: rename flag in the CI per-job invocation; ENTRY_NAME is a single name today so this works as a single-element list. Interactive --entries semantics unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: rlratzel <rratzel@nvidia.com> * benchmark: bump exact_dedup_identification timeout_s 500 -> 1500 In nemo-ci pipeline 53092974 / leaf 53093663, exact_dedup_identification was SLURM-killed by TIME LIMIT at 68% (516/755) into the "Inserting into shuffler" phase. The shuffler ran at ~1.83 it/s on EOS H100, so the shuffler phase alone needs ~410s, plus Ray cluster setup, dataset stat, post-shuffle dedup compute, and cleanup — total wall ~800-1000s, not fitting in a 500s budget. The previous 500s value was set when the test was effectively a no-op (the rpv2 dataset was unreadable due to filesystem permissions, so the test failed fast on stat() before doing any real work — see PR description for the rpv2 access fix story). 500s was also reportedly sufficient on a faster system; EOS may simply be slower for this workload. Worth investigating after the test is unblocked. Bump to 1500s (25 min): - ~50% headroom over the estimated 1000s realistic wall on EOS - Parallel in spirit to the ndd_ray_serve_dp4 700 -> 1200 bump earlier in this PR Reference failed job: https://gitlab-master.nvidia.com/dl/JoC/nemo-ci/-/jobs/329434352 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: rlratzel <rratzel@nvidia.com> * benchmark: install lynx in ci_benchmark_launcher.sh The math benchmarks (math_preprocess, math_preprocess_classifier, math_preprocess_llm_cleanup) shell out to the lynx text browser via nemo_curator/stages/math/download/html_extractors/lynx.py for HTML extraction. lynx is not present in the Curator benchmark container, so those benchmarks currently fail with: RuntimeError: lynx executable not found in PATH lynx is GPL-licensed, so we deliberately do not bake it into the redistributable Curator image. Instead it is installed transiently in the existing benchmark container at CI run time. The image we publish stays GPL-free; the apt-installed lynx lives only for the lifetime of the CI container. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: rlratzel <rratzel@nvidia.com> * benchmark: preserve scratch dir for fuzzy_dedup_identification Adds `delete_scratch: false` to the fuzzy_dedup_identification entry so its scratch directory (under session_entry_dir/scratch/{cache,output}) is retained after the entry finishes. The downstream dedup_removal_* benchmarks read these artifacts at known paths, so the prior default session-level cleanup (delete_scratch: true) was wiping them out before they could be consumed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: rlratzel <rratzel@nvidia.com> * benchmark: range domain_label_games_count +/-5% (was exact) Both domain_classification_raydata and domain_classification_xenna have a requirement on domain_label_games_count with exact_value: 149816. Run-to-run output of the classifier drifts by several hundred to a few thousand classifications, causing repeated false failures while the actual benchmark (throughput, total docs, number of domains predicted) is healthy. Loosens the metric to a +/- 5% range (142325 .. 157307) which still catches genuine regressions of the classifier output while tolerating normal run-to-run variability. domain_label_news_count is left at exact_value: 2817 pending further investigation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: rlratzel <rratzel@nvidia.com> * benchmark: bump dedup_removal_* timeout_s for real-data wall Both dedup_removal entries had timeout_s values that fit when the test was effectively a no-op (it failed fast on stale fuzzy_id_generator artifacts before doing real work). Once the upstream fuzzy_id_generator data was refreshed, the actual benchmarks ran for real: * dedup_removal_raydata observed wall ~1419s (92% complete when killed at the prior 1100s ceiling) -> 1800s (~27% headroom over the estimated 1500s full wall) * dedup_removal_xenna passed at 1523s wall with the prior 1500s ceiling (no margin) -> 1800s as a preemptive bump to match raydata and absorb run-to-run variance Reference: nemo-ci pipeline 53333039 / leaf 53333328 — raydata killed by SLURM TIME LIMIT during normal pipeline execution while xenna finished successfully but with zero headroom. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: rlratzel <rratzel@nvidia.com> * Revert "benchmark: preserve scratch dir for fuzzy_dedup_identification" The earlier delete_scratch: false override on fuzzy_dedup_identification was added so the entry's scratch/output artifacts could be picked up by the downstream dedup_removal_* benchmarks within the same pipeline. In practice the artifacts are consumed via the canonical dataset path: {datasets_path}/cleaned_exact_dedup_all_cc_fuzzy_output_nightly_container_paths/ fuzzy_id_generator.json FuzzyDuplicateIds/ Operators promote a known-good fuzzy_dedup output to this path once, and dedup_removal_raydata / dedup_removal_xenna consume it from there on every subsequent pipeline (no same-pipeline dependency). With that workflow in place, the per-entry delete_scratch override is no longer needed and just leaves unused data on lustre across runs. Revert to the session-level default (delete_scratch: true). This reverts commit 6369150. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: rlratzel <rratzel@nvidia.com> * Add optional /dev/shm cap via CURATOR_SHM_SIZE_BYTES env var For cross-host benchmark comparisons (e.g. A100 vs H100) the host's /dev/shm size differs (~550 GB vs ~1 TB) and the container inherits the host default. Provide an opt-in env var to remount /dev/shm at a chosen size. Unset preserves prior behavior. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: rlratzel <rratzel@nvidia.com> * Add optional all-GPU utilization poller via CURATOR_GPU_POLL_INTERVAL_S Background `nvidia-smi` poller writes one CSV per entry covering all GPUs on the node, independent of CUDA_VISIBLE_DEVICES. Lets us verify post-run whether Ray/Xenna actually honored the visible-device mask (any nonzero util on masked indices ⇒ leakage). Unset → no polling, preserving prior behavior. Subprocess is killed on EXIT via trap so a python crash doesn't leave it orphaned. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: rlratzel <rratzel@nvidia.com> * Raise A100 container memory cap to 2 TiB; drop EOS-side shm remount The benchmarking container cap of 1 TiB on the A100 host shrinks the container's visible memory to ~50% of host and shm to ~25%, which makes env.json report 1024 GiB / 512 GiB even though the host has ~2 TiB / ~1008 GiB. EOS reports the host values directly, so the A100 vs EOS comparison shows an artificial environmental mismatch. Raising the cap to 2 TiB lets A100's container see the host's full memory, matching EOS. Also remove the CURATOR_SHM_SIZE_BYTES env-var block from ci_benchmark_launcher.sh: pyxis-on-EOS does not grant CAP_SYS_ADMIN, so the remount silently fell back to the WARNING branch and never applied. With A100 raised to match EOS, the toggle is no longer needed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: rlratzel <rratzel@nvidia.com> * Replace bash GPU poller with threaded GPUStatsRecorder The prior CURATOR_GPU_POLL_INTERVAL_S nvidia-smi block in ci_benchmark_launcher.sh polled GPUs by spawning nvidia-smi as a background process. That implementation was bash-only, lived outside the runner's lifecycle, and named output files ad-hoc. Replace with a Python GPUStatsRecorder that uses gpustat (already a curator dep) on a daemon thread, started/stopped as a context manager around each entry's subprocess in run_entry. Per-entry CSV is written to {session_entry_path}/gpustats.csv with columns: timestamp_utc, gpu_id, utilization_gpu_pct, utilization_memory_pct, temperature_c, processes (JSON-encoded list of {pid, username, command, gpu_memory_usage}). Polling cadence is configured via a new top-level YAML key `gpu_stats_recorder.interval_s` in the benchmark config (default 1.0; set to 0 to disable). The recorder polls all visible GPUs regardless of CUDA_VISIBLE_DEVICES, which lets us verify post-run that Ray/Xenna honored the visible-device mask. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: rlratzel <rratzel@nvidia.com> * Wire NEMO_CI_* env vars + Slack viewer-URL block (curator-side) Curator-side counterpart of the upcoming nemo-ci curator_benchmark_launch wrapper (still under design). Adds three small, backwards-compatible pieces of plumbing so per-launch metadata (session name, viewer URL, run reason) can flow from the launcher all the way through to the Slack parent message and env.json — gated behind new env vars / flags so today's nightly schedule is unaffected. * ci_benchmark_launcher.sh: - Honor NEMO_CI_SESSION_NAME when set; otherwise pick nightly-<TS> for scheduled (cron) pipelines and the legacy benchmark_run_<id> name for direct launch_pipeline.py manual launches. - Compose a run-viewer URL only when NEMO_CI_VIEWER_HOST is set, using the host-side lustre path and the resolved session name. - Pass --viewer-url and --run-reason through to run.py when set. * run.py: - New --viewer-url and --run-reason CLI flags (both default None). - When --run-reason is set, inject it into env_dict before sinks initialize, so it persists to env.json and appears in the Slack environment block. - When --viewer-url is set, patch the Slack sink's sink_config in-process (no YAML schema change required) before sinks initialize. * slack_sink.py: - SlackSink reads optional viewer_url from sink_config. - SlackParentMessage accepts viewer_url and renders a "Results viewer: <url|open run>" section between the overall-status block and the environment table — only when set. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: rlratzel <rratzel@nvidia.com> * GPUStatsRecorder: also record power draw, power limit, fan speed power_draw / power_limit / fan_speed are populated by the same NVML query gpustat.new_query() already issues for utilization/memory/temp, so reading them adds no measurable overhead (no extra NVML calls). Datacenter SKUs (H100/A100 chassis cards) usually return None for fan_speed since there's no controllable per-card fan; render None as empty string for CSV cleanliness. Same handling for power_draw / power_limit on the rare hardware that doesn't expose them. CSV columns now: timestamp_utc, gpu_id, utilization_gpu_pct, utilization_memory_pct, temperature_c, power_draw_w, power_limit_w, fan_speed_pct, processes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: rlratzel <rratzel@nvidia.com> * Tighten domain_label_games_count tolerance from +/-5% to +/-1% Tighter than the original 142325..157307 (+/-5% around the observed 149816); now 148318..151314 (+/-1%). Catches smaller classifier-output drifts that the prior wider window absorbed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: rlratzel <rratzel@nvidia.com> * nightly-benchmark.yaml: drop noisy timeout_s commentary Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: rlratzel <rratzel@nvidia.com> --------- Signed-off-by: rlratzel <rratzel@nvidia.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Summary
Bundles eight independent fixes uncovered while triaging the Curator
nightly benchmark suite against
main(reference nemo-ci pipelines52840568,
53098506,
53323567,
53333039,
and 53423863),
plus three environmental/observability changes added to enable fair A100
vs EOS comparison runs.
Pass-rate progression: 23/37 → 28/37 → 6/8 (scoped) → 1/2 (scoped) → 40/41
(full suite, 53423863). Sole remaining failure is
video_embedding_xenna,a new entry merged in from upstream main that needs a missing NVENC
library — out of scope for this PR.
1. Make undefined env vars in config non-fatal by default (
b737202d)resolve_env_varspreviously raisedValueErroron the first undefined${VAR}reference, halting the entire benchmark session even when themissing var was used by only a few entries.
so unrelated entries can still run.
--strict-config-checkCLI flag torun.pyto restore the oldfail-fast behavior.
2. New
--entries-exactflag, exact-match entry filtering (ab1d4385,e8c6a52f,7b351693)Fixes a substring-aliasing bug in CI per-job invocations:
--entriesuses pytest's-ksubstring expression evaluator, so--entries audio_tagging_tts_xennaalso matchesaudio_tagging_tts_xenna_repeat. In CI, where each per-job script runs--entries <entry-name>, this caused the non-_repeatjob to alsoexecute the
_repeatentry, polluting that entry's per-entry resultsdir and crashing the subsequent legitimate
_repeatSLURM job withCapture file ... already existsat Ray cluster setup.--entries-exactaccepting a comma-separated list of exact entrynames. Every supplied name must match a configured (enabled) entry,
otherwise the run aborts with a
ValueErrorlisting the unknown namesalongside the available entry names.
--entries(CLI andSession.from_dictbothenforce this).
benchmarking/tools/ci_benchmark_launcher.shswitched from--entriesto
--entries-exact.--entriessubstring expression semantics are unchanged.3. Bump too-tight
timeout_svalues for several entriesMultiple
nightly-benchmark.yamlentries hadtimeout_svalues thatdidn't cover the actual wall time on the EOS H100 runner. These split
into two categories:
(e.g. rpv2 data was unreadable, or fuzzy_id_generator artifacts were
stale and the test failed fast); once the underlying issue was fixed
and the test ran for real, the historical ceiling was too tight (Add style check #3.2,
Add style check #3.3).
3.1
ndd_ray_serve_dp4: 700 → 1200 (ab1d4385)--timeceiling ~13s after its benchmarksubprocess succeeded — Ray teardown overhead pushed total wall over the
wire.
https://gitlab-master.nvidia.com/dl/JoC/nemo-ci/-/jobs/327777542
3.2
exact_dedup_identification: 500 → 1500 (21647b4f)dataset was unreadable due to filesystem permissions, so the test failed
fast on
stat()). Once rpv2 access was restored, the test SLURM-killedat 68% (516/755) of the "Inserting into shuffler" phase.
cluster setup + dataset stat + post-shuffle dedup compute + cleanup ≈
realistic wall 800-1000s.
https://gitlab-master.nvidia.com/dl/JoC/nemo-ci/-/jobs/329434352
3.3
dedup_removal_*: 1100/1500 → 1800/1800 (c2f3a6b0)Once the upstream
fuzzy_id_generatorartifacts were refreshed (seesection 4 — the
delete_scratch: falsefix made this possible), thededup_removal tests actually ran the workload for the first time:
dedup_removal_raydata: observed wall ~1419s (92% complete when killedat the prior 1100s ceiling) → 1800s (~27% headroom over the estimated
1500s full wall).
dedup_removal_xenna: passed at 1523s wall with the prior 1500s ceiling(zero margin) → 1800s as a preemptive bump to match raydata and absorb
run-to-run variance.
Reference: nemo-ci pipeline 53333039 / leaf 53333328 — raydata killed by
SLURM TIME LIMIT during normal pipeline execution while xenna finished
with zero headroom.
4. Install
lynxin the CI benchmark launcher (d0eac901)The math benchmarks (
math_preprocess,math_preprocess_classifier,math_preprocess_llm_cleanup) shell out to thelynxtext browser vianemo_curator/stages/math/download/html_extractors/lynx.pyfor HTMLextraction.
lynxis not in the Curator container, so those benchmarksfail with
RuntimeError: lynx executable not found in PATH.lynxis GPL-licensed, so we deliberately do not bake it into theredistributable Curator image. Instead it is installed transiently in the
existing benchmark container at CI run time, used during the run, and
discarded with the container. The published image stays GPL-free; the
apt-installed
lynxonly lives for the lifetime of each CI container.5. (Reverted) Preserve scratch dir for
fuzzy_dedup_identification(63691509thena3c38ac6)Initial commit
63691509addeddelete_scratch: falseto thefuzzy_dedup_identificationentry to keep its scratch artifacts around forthe downstream
dedup_removal_*benchmarks. In practice the consumptionpath is the canonical dataset path:
Operators promote a known-good fuzzy_dedup output to this path once, and
the dedup_removal entries consume it from there on every subsequent
pipeline — there is no same-pipeline dependency. Given that workflow, the
per-entry
delete_scratch: falseoverride is unnecessary and just leavesunused data on lustre across runs. Reverted by
a3c38ac6; the entry nowuses the session-level default (
delete_scratch: true).6. Range
domain_label_games_countmetric +/-1% (0c375b7e, tightened inab31cf11)Both
domain_classification_raydataanddomain_classification_xennahad a requirement on
domain_label_games_countwithexact_value: 149816.Run-to-run output of the classifier drifts by several hundred to a few
thousand classifications, causing repeated false failures while the actual
benchmark (throughput, total docs, number of domains predicted) is
healthy.
Loosens the metric to a +/-1% range (
min_value: 148318,max_value: 151314) which still catches genuine regressions of theclassifier output while tolerating normal run-to-run variability.
domain_label_news_countis left atexact_value: 2817pending furtherinvestigation.
7. Raise A100 container memory cap to host capacity (
fd6e9cfd)The benchmarking container on the A100 host was capped at 1 TiB RAM (and
512 GiB shm, derived as 50% of container memory) by
_max_container_memory_bytes = 1 * _TBinbenchmarking/tools/gen_runscript_vars.py. The A100 host actually has~2 TiB RAM and ~1008 GiB
/dev/shm— comparable to the EOS H100 host.The artificial cap made A100's
env.jsonreport ~50% of host RAM and~25% of host shm, surfacing a fake environmental delta in A100 vs EOS
comparison runs even though the workload (Ray plasma capped at 500 GB
on both sides) was bounded identically.
Raises the cap to
2 * _TBso A100's container memory and shm reflecthost capacity. After this change, A100
env.jsonreportstotal_system_memory_bytes≈ 2.16 × 10¹² andshm_size_bytes≈1.08 × 10¹² — matching EOS.
Also removes an experimental
CURATOR_SHM_SIZE_BYTESenv-var block inci_benchmark_launcher.shthat attempted to cap EOS-side/dev/shmviamount -o remount,size=…. Pyxis containers on EOS don't grantCAP_SYS_ADMIN, so the remount silently fell back to the WARNING branchand the cap never applied. Raising A100 up to match EOS is the
maintainable direction (the EOS shm ceiling cannot be capped down without
nemo-ci infrastructure changes), so the toggle is no longer needed.
8. Background per-GPU stats recorder (
c2fd47e6)Adds
benchmarking/runner/gpu_stats_recorder.py—GPUStatsRecorder,a context-managed daemon thread that polls all GPUs via
gpustat(already a curator dep) and writes one row per (timestamp, GPU) pair to
{session_entry_path}/gpustats.csvwhile each entry's benchmarksubprocess runs.
CSV columns:
timestamp_utcgpu_idutilization_gpu_pctutilization_memory_pctmemory_used / memory_total * 100temperature_cprocesses{pid, username, command, gpu_memory_usage}Configurable via a new top-level YAML key in
nightly-benchmark.yaml:The recorder polls all NVML-visible GPUs, independent of
CUDA_VISIBLE_DEVICES. This lets us verify post-run whether Ray/Xennaactually honored the visible-device mask in cross-host comparison runs
(any nonzero util on a masked GPU index = leakage).
Replaces the prior shell-based
nvidia-smi -lpoller (block removed fromci_benchmark_launcher.sh) which lived outside the runner lifecycle,wrote ad-hoc file names, and required a separate env-var toggle to enable.
9. Wire
NEMO_CI_*env vars + Slack viewer-URL block (da4c6760)Curator-side plumbing for an upcoming nemo-ci-hosted developer-launch
wrapper (
curator_benchmark_launch.py, still under design). All threeedits are gated behind new env vars / flags so today's nightly schedule
is byte-identical to before.
ci_benchmark_launcher.sh:NEMO_CI_SESSION_NAMEwhen set; otherwise picksnightly-<TS>for scheduled (cron) pipelines and the legacy
benchmark_run_<id>name for direct
launch_pipeline.pylaunches.NEMO_CI_VIEWER_HOSTis set,using the host-side lustre path and the resolved session name.
--viewer-urland--run-reasonthrough torun.pywhen set.run.py: new--viewer-urland--run-reasonCLI flags. When--run-reasonis set it lands inenv_dict(and thusenv.json+the Slack environment block). When
--viewer-urlis set the Slacksink's
sink_config["viewer_url"]is patched in-process beforesink.initialize(...)— no YAML schema change required.slack_sink.py:SlackSinkreads optionalviewer_urlfromsink_config;SlackParentMessageacceptsviewer_urland rendersa
*Results viewer:* <{url}|open run>section between theoverall-status block and the environment table, only when set.
The new
NEMO_CI_*namespace is shared with the upcoming nemo-cilauncher work and is intentionally generic (not curator-specific) so
other modules can opt in later.
Verification
Across multiple nemo-ci pipelines:
— first verification of commits 1, 2, 3.1, 3.2: 28/37 pass (up from
23/37 baseline). Confirmed
audio_tagging_tts_xenna_repeatworks(
--entries-exact),ndd_ray_serve_dp4works (timeout bump),exact_dedup_identificationworks (rpv2 + timeout),audio_tagging_tts_xennaworks withoutHF_SECRET_KEYset.— scoped 8-entry run of commits 4, 5, 6: 6/8 pass (
math_preprocess*all pass thanks to lynx install;
domain_classification_*pass with themetric range;
fuzzy_dedup_identificationpasses). The two failures(
dedup_removal_*) were caused by a same-pipeline race withfuzzy_dedup_identification — fixed by operators promoting fresh
fuzzy_id_generator.jsonartifacts to the canonical dataset pathbetween runs.
— scoped 2-entry run with fresh artifacts in place:
dedup_removal_xennapasses (1523s, no margin);
dedup_removal_raydataSLURM-killed by TIMELIMIT at 92% completion → addressed by commit
c2f3a6b0(Add style check #3.3).— full-suite verification after Add style check #3.3: 40/41 pass. Sole failure is
video_embedding_xenna(new entry merged from upstream main, missingNVENC library — separate follow-up).
— in flight. Verifies Add batched decorator #7 (cap bump doesn't break EOS) and Fix noisy CUDA shutdown #8 (recorder
always-on writes
gpustats.csv); also captures empirical evidence ofwhether
CUDA_VISIBLE_DEVICES=0,1,2,3actually restricts Ray/Xenna to4 GPUs on EOS.
Test plan
Undefined env vars
audio_tagging_tts_xennaran successfully in 53098506 withHF_SECRET_KEYunset.--strict-config-check; confirm it still exits with theoriginal
ValueError.behavior change.
--entries-exactand 53323567; no cross-entry pollution observed.
--entries-exact <typo>exits with an error listing the unknownname and the available entry names.
--entries-exact a,b,cruns only those three entries in YAML order.--entriesand--entries-exactexits with a"mutually exclusive" error.
Timeout bumps
ndd_ray_serve_dp4finished without TIME LIMIT cancellation in53098506.
exact_dedup_identificationfinished without TIME LIMIT in 53098506.dedup_removal_raydataanddedup_removal_xennaboth finishwithout TIME LIMIT cancellation (verified in 53398187 / 53423863).
lynx install
math_preprocess*entries all passed in 53323567 —RuntimeError: lynx executable not foundno longer fires.fuzzy_dedup scratch retention (now reverted)
fuzzy_id_generator.json+FuzzyDuplicateIds/written to thecanonical dataset path.
dedup_removal_*consume the promoted artifacts in a cleanpipeline (verified in 53398187).
delete_scratch: falseoverride on
fuzzy_dedup_identificationis no longer needed and hasbeen reverted (commit
a3c38ac6).domain_label_games_count range
domain_classification_*both passed in 53323567 with the newrange satisfied.
A100 container memory cap (#7)
fd6e9cfd, a freshrun produces
env.jsonwithtotal_system_memory_bytes≈ host total(~2.16 × 10¹²) and
shm_size_bytes≈ ~1.08 × 10¹².the unrelated
ci_benchmark_launcher.shshm-block removal —verified in 53584394 run-up).
GPUStatsRecorder (#8)
gpustats.csvexists under eachper-entry session dir on EOS lustre.
timestamp_utc,gpu_id,utilization_gpu_pct,utilization_memory_pct,temperature_c,processes.gpu_stats_recorder.interval_s: 0in the YAML disablesthe recorder (no file written); default (omitted key) gives 1 Hz.
CUDA_VISIBLE_DEVICES(NVML-visible, not CUDA-runtime-visible).🤖 Generated with Claude Code