55
66# Performance Regression Gate
77#
8+ # GPU jobs default to the RTX PRO 6000 runner label used by the upstream
9+ # deployment. Override with repo variable PERF_GATE_RUNS_ON, e.g. a JSON array
10+ # of runner labels.
11+ #
812# Runs a matrix of benchmark tasks on self-hosted runners, compares
913# results to the rolling baseline in angehu/perf-baselines, and posts a verdict
1014# table to the job summary. Non-blocking by default (gate_config.json).
1620# aggregate → download all bench artifacts, run oracle, update baselines
1721# (self-hosted, gpu)
1822#
19- # Baseline updates are pushed to angehu/perf-baselines only from main,
20- # develop, release or angehu/perf-gate-poc branches.
23+ # Baseline updates are published from protected-branch push events
24+ # (main/develop/release) plus angehu/perf-gate-poc while this POC is active.
25+ # Mirrored PR pushes under pull-request/<number> can run the gate on upstream
26+ # RTX runners; they are read-only unless PERF_GATE_ALLOW_MIRROR_BASELINE_UPDATE
27+ # is explicitly enabled as a temporary deployment escape hatch.
2128#
2229# backend_key: "{physics_backend}_{render_backend}" when render_backend is
2330# set, otherwise just physics_backend. Matches TaskConfig.backend_key.
2431# TODO: replace angehu/perf-baselines w/ real branch name
32+ # TODO: remove angehu/perf-gate-poc after deployment
2533
2634name : Performance Regression Gate
2735
2836on :
2937 pull_request :
30- types : [opened, synchronize, reopened]
38+ types : [opened, synchronize, reopened, ready_for_review]
39+ branches : [main, develop, 'release/**']
40+ merge_group :
3141 branches : [main, develop, 'release/**']
42+ push :
43+ branches : [main, develop, 'release/**', angehu/perf-gate-poc, 'pull-request/**']
3244 workflow_dispatch :
3345
3446concurrency :
@@ -37,13 +49,14 @@ concurrency:
3749
3850permissions :
3951 contents : write
52+ pull-requests : read
4053
4154env :
4255 CI_IMAGE_TAG : >-
4356 isaac-lab-ci:${{
4457 github.event_name == 'pull_request'
4558 && format('pr-{0}', github.event.pull_request.number)
46- || github.ref_name
59+ || 'sha'
4760 }}-${{ github.sha }}
4861
4962jobs :
5265 # ---------------------------------------------------------------------------
5366 config :
5467 name : Load Config
68+ if : ${{ github.event_name != 'pull_request' || github.event.pull_request.draft == false }}
5569 runs-on : ubuntu-latest
5670 outputs :
5771 isaacsim_image_name : ${{ steps.load.outputs.isaacsim_image_name }}
8296 # ---------------------------------------------------------------------------
8397 bench :
8498 name : Bench / ${{ matrix.task_id }} / ${{ matrix.physics_backend }}${{ matrix.render_backend != '' && format('_{0}', matrix.render_backend) || '' }}
85- runs-on : [self-hosted, gpu]
99+ if : ${{ github.event_name != 'pull_request' || github.event.pull_request.draft == false }}
100+ runs-on : ${{ fromJSON(vars.PERF_GATE_RUNS_ON || '["linux-amd64-gpu-rtxpro6000-latest-1"]') }}
86101 needs : [config]
87102 continue-on-error : true
88103 timeout-minutes : ${{ matrix.job_timeout_minutes }}
@@ -134,6 +149,16 @@ jobs:
134149 LOG_FILE="${ARTIFACT_DIR}/benchmark.log"
135150
136151 mkdir -p "${ARTIFACT_DIR}"
152+ GPU_MODEL="$(nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null | head -1 | xargs)"
153+ if [[ -z "${GPU_MODEL}" ]]; then
154+ GPU_MODEL="unknown-gpu"
155+ fi
156+ python3 tools/perf_regression_gate/write_launch_config.py \
157+ --task_id "${TASK_ID}" \
158+ --physics_backend "${PHYSICS_BACKEND}" \
159+ --render_backend "${RENDER_BACKEND}" \
160+ --gpu_model "${GPU_MODEL}" \
161+ --artifact_dir "${ARTIFACT_DIR}"
137162
138163 # Remove any stale container from a previous run attempt
139164 docker rm -f "${CONTAINER_NAME}" 2>/dev/null || true
@@ -142,10 +167,7 @@ jobs:
142167 # benchmark_non_rl.py accepts Hydra-style preset tokens (no leading dashes).
143168 # Physics is selected via the sim_backend token; render via renderer= token.
144169 # TODO: Update these tokens once benchmark_non_rl.py CLI flags are finalised.
145- PRESET_TOKENS=""
146- if [ -n "${RENDER_BACKEND}" ]; then
147- PRESET_TOKENS="renderer=${RENDER_BACKEND}"
148- fi
170+ HYDRA_ARGS="${{ matrix.hydra_args }}"
149171
150172 # Start the benchmark container in detached mode
151173 docker run -d --name "${CONTAINER_NAME}" \
@@ -172,7 +194,8 @@ jobs:
172194 --num_frames ${{ matrix.num_frames }} \
173195 --benchmark_backend json \
174196 --output_path /tmp/bench_out \
175- ${PRESET_TOKENS}
197+ ${{ matrix.seed != '' && format('--seed {0}', matrix.seed) || '' }} \
198+ ${HYDRA_ARGS}
176199 "
177200
178201 START=$(date +%s)
@@ -234,10 +257,7 @@ jobs:
234257
235258 docker rm -f "${CONTAINER_NAME}" 2>/dev/null || true
236259
237- PRESET_TOKENS=""
238- if [ -n "${RENDER_BACKEND}" ]; then
239- PRESET_TOKENS="renderer=${RENDER_BACKEND}"
240- fi
260+ HYDRA_ARGS="${{ matrix.hydra_args }}"
241261
242262 docker run -d --name "${CONTAINER_NAME}" \
243263 --init --stop-timeout 10 \
@@ -263,7 +283,8 @@ jobs:
263283 --num_frames ${{ matrix.num_frames }} \
264284 --benchmark_backend json \
265285 --output_path /tmp/bench_out \
266- ${PRESET_TOKENS}
286+ ${{ matrix.seed != '' && format('--seed {0}', matrix.seed) || '' }} \
287+ ${HYDRA_ARGS}
267288 "
268289
269290 START=$(date +%s)
@@ -327,6 +348,8 @@ jobs:
327348 --wall_time_s "${FINAL_WALL}" \
328349 --timeout_s "${{ matrix.bench_timeout_s }}" \
329350 --log_file "${ARTIFACT_DIR}/benchmark.log" \
351+ --launch_config "${ARTIFACT_DIR}/launch_config.json" \
352+ --gate_config tools/perf_regression_gate/gate_config.json \
330353 ${EXTRA_FLAGS}
331354
332355 - name : Upload bench artifacts
@@ -361,21 +384,22 @@ jobs:
361384 # ---------------------------------------------------------------------------
362385 aggregate :
363386 name : Aggregate + Verdict
364- runs-on : [self-hosted, gpu]
387+ runs-on : ${{ fromJSON(vars.PERF_GATE_RUNS_ON || '["linux-amd64- gpu-rtxpro6000-latest-1"]') }}
365388 needs : [bench]
366- # Always run even when some bench jobs fail or are skipped
367- if : always()
389+ # Always run for real gate attempts, even when some bench jobs fail.
390+ if : ${{ always() && (github.event_name != 'pull_request' || github.event.pull_request.draft == false) }}
368391
369392 steps :
370393 - name : Checkout Code
371394 uses : actions/checkout@v6
372395 with :
373396 fetch-depth : 0
374397
375- # Create a local branch so _baseline_worktree can commit to it
398+ # Prime the remote-tracking baseline ref. aggregate.py refetches immediately
399+ # before reading and again inside the transactional push retry loop.
376400 - name : Fetch baselines branch
377401 run : |
378- git fetch origin angehu/perf-baselines:angehu/perf-baselines || \
402+ git fetch origin +refs/heads/ angehu/perf-baselines:refs/remotes/origin/ angehu/perf-baselines || \
379403 echo "::warning::Baseline branch not found; gate will run as a seed run (no baseline comparison)"
380404
381405 - name : Download bench artifacts
@@ -385,24 +409,31 @@ jobs:
385409 path : artifacts/
386410 merge-multiple : false
387411
412+ - name : Resolve gate context
413+ id : gate_context
414+ env :
415+ ALLOW_MIRROR_UPDATE : ${{ vars.PERF_GATE_ALLOW_MIRROR_BASELINE_UPDATE || 'false' }}
416+ GITHUB_TOKEN : ${{ github.token }}
417+ run : |
418+ python3 tools/perf_regression_gate/github_gate_context.py
419+
388420 - name : Run aggregate oracle
389421 id : aggregate
390422 run : |
391- ALLOW_UPDATE="false"
392- if [[ "${{ github.ref }}" == "refs/heads/develop" || \
393- "${{ github.ref }}" == "refs/heads/angehu/perf-gate-poc" ]]; then
394- ALLOW_UPDATE="true"
423+ GPU_MODEL="$(nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null | head -1 | xargs)"
424+ if [[ -z "${GPU_MODEL}" ]]; then
425+ GPU_MODEL="unknown-gpu"
395426 fi
396427 python3 tools/perf_regression_gate/aggregate.py \
397428 --artifacts_dir artifacts/ \
398- --gpu_model L40S \
429+ --gpu_model "${GPU_MODEL}" \
399430 --gate_config tools/perf_regression_gate/gate_config.json \
400431 --baseline_branch angehu/perf-baselines \
401- --allow_baseline_update "${ALLOW_UPDATE}" \
432+ --baseline_remote origin \
433+ --baseline_push_retries 3 \
434+ --base_sha "${{ steps.gate_context.outputs.base_sha }}" \
435+ --target_branch "${{ steps.gate_context.outputs.target_branch }}" \
436+ --source_branch "${{ steps.gate_context.outputs.source_branch }}" \
437+ --allow_baseline_update "${{ steps.gate_context.outputs.allow_update }}" \
438+ --trusted_source "${{ steps.gate_context.outputs.trusted_source }}" \
402439 --summary_file "${GITHUB_STEP_SUMMARY}"
403-
404- # Push baseline updates only from eligible branches
405- - name : Push baseline updates
406- if : steps.aggregate.outputs.baselines_updated == 'true'
407- run : |
408- git push origin angehu/perf-baselines
0 commit comments