name: Nightly benchmarks on: schedule: # 03:15 UTC every day — off peak, avoids colliding with the regular CI surge. - cron: "15 3 * * *" workflow_dispatch: permissions: contents: read issues: write jobs: bench: name: Run perf contract and regression checks runs-on: ubuntu-latest timeout-minutes: 60 env: RUSTFLAGS: "" # override target-cpu=native from .cargo/config.toml steps: - uses: actions/checkout@v6 - uses: dtolnay/rust-toolchain@v1 with: toolchain: stable - uses: Swatinem/rust-cache@v2 - uses: actions/setup-python@v6 with: python-version: "3.12" - name: Install uv run: pip install uv - name: Build ferro-ta wheel run: | uv sync --extra dev uv run maturin build --release --out dist uv run pip install --force-reinstall dist/*.whl - name: Run vs-TA-Lib benchmark run: uv run python benchmarks/bench_vs_talib.py --output benchmarks/artifacts/nightly_vs_talib.json - name: Run SIMD benchmark matrix run: uv run python benchmarks/bench_simd.py --output benchmarks/artifacts/nightly_simd.json - name: Check hotspot regression id: hotspot continue-on-error: true run: uv run python benchmarks/check_hotspot_regression.py --tolerance 0.05 - name: Check vs-TA-Lib regression id: vs_talib continue-on-error: true run: uv run python benchmarks/check_vs_talib_regression.py --tolerance 0.05 - name: Run criterion bench (build only) run: cargo bench -p ferro_ta_core --no-run - name: Upload nightly artifacts uses: actions/upload-artifact@v7 if: always() with: name: nightly-bench-${{ github.run_id }} path: benchmarks/artifacts/ retention-days: 30 - name: Open issue on regression if: steps.hotspot.outcome == 'failure' || steps.vs_talib.outcome == 'failure' uses: actions/github-script@v9 env: HOTSPOT_OUTCOME: ${{ steps.hotspot.outcome }} VS_TALIB_OUTCOME: ${{ steps.vs_talib.outcome }} RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} with: script: | const hotspot = process.env.HOTSPOT_OUTCOME; const vsTalib = process.env.VS_TALIB_OUTCOME; const runUrl = process.env.RUN_URL; const failed = []; if (hotspot === 'failure') failed.push('hotspot'); if (vsTalib === 'failure') failed.push('vs-TA-Lib'); const title = `Nightly benchmark regression: ${failed.join(' + ')}`; const body = [ `The nightly benchmark workflow reported a >5% regression in: **${failed.join(', ')}**.`, '', `Run: ${runUrl}`, '', 'Artifacts with the raw JSON are attached to the run above.', 'If this is a known regression, update the baseline JSON and close.', 'If it is unexpected, investigate the last commit on `main` before the nightly ran.', ].join('\n'); await github.rest.issues.create({ owner: context.repo.owner, repo: context.repo.repo, title, body, labels: ['performance', 'regression', 'nightly'], });