diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a4cf78b4ac0..b380f48ace5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -272,6 +272,9 @@ jobs: timeout-minutes: 60 env: CARGO_TARGET_DIR: ${{ github.workspace }}/target + CARGO_PROFILE_RELEASE_DEBUG: line-tables-only + KEYNOTE_PROFILE_DIR: ${{ github.workspace }}/keynote-profile + STDB_V8_FLAGS: "--perf-basic-prof --perf-basic-prof-only-functions --interpreted-frames-native-stack" steps: - name: Find Git ref env: @@ -303,7 +306,14 @@ jobs: prefix-key: v1 - name: Build keynote benchmark binaries - run: cargo build --release -p spacetimedb-cli -p spacetimedb-standalone + run: > + cargo + --config 'build.rustflags = ["-C", "force-frame-pointers=yes"]' + build + --release + -p spacetimedb-cli + -p spacetimedb-standalone + --features spacetimedb-standalone/perfmap # Node 24 is the current Active LTS line. - name: Set up Node.js @@ -320,7 +330,44 @@ jobs: working-directory: crates/bindings-typescript - name: Run keynote-2 benchmark regression check - run: cargo ci keynote-bench + id: keynote_bench + run: | + mkdir -p "$KEYNOTE_PROFILE_DIR" + set +e + samply record \ + --save-only \ + --unstable-presymbolicate \ + --profile-name keynote-bench \ + -o "$KEYNOTE_PROFILE_DIR/profile.json.gz" \ + -- cargo ci keynote-bench + status=$? + ls -lh "$KEYNOTE_PROFILE_DIR" || true + exit "$status" + + - name: Upload keynote benchmark profile + if: failure() && steps.keynote_bench.outcome == 'failure' + uses: actions/upload-artifact@v4 + with: + name: keynote-bench-profile + path: ${{ env.KEYNOTE_PROFILE_DIR }} + if-no-files-found: warn + retention-days: 14 + + - name: Report keynote benchmark profile instructions + if: failure() && steps.keynote_bench.outcome == 'failure' + run: | + { + echo "## Keynote benchmark profile" + echo + echo "The keynote benchmark failed after recording a Samply/Firefox profile." + echo + echo "1. Open this run's Summary page: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" + echo "2. Download the artifact named \`keynote-bench-profile\`." + echo "3. Open and load \`profile.json.gz\`." + echo "4. Compare hot frames against a recent nightly \`keynote-bench-nightly-profile\` artifact." + } >> "$GITHUB_STEP_SUMMARY" + + echo "::error title=Keynote benchmark profile uploaded::Download the keynote-bench-profile artifact from this run and open profile.json.gz in https://profiler.firefox.com/." lints: name: Lints diff --git a/.github/workflows/keynote-bench-nightly.yml b/.github/workflows/keynote-bench-nightly.yml new file mode 100644 index 00000000000..7e378ba5df4 --- /dev/null +++ b/.github/workflows/keynote-bench-nightly.yml @@ -0,0 +1,108 @@ +name: Keynote Bench Nightly + +on: + schedule: + # Nightly at 3 AM UTC. + - cron: '0 3 * * *' + workflow_dispatch: + inputs: + ref: + description: "Git ref to benchmark" + required: false + default: master + +permissions: + contents: read + +concurrency: + group: keynote-bench-nightly + cancel-in-progress: true + +jobs: + keynote_bench: + name: Keynote Bench + runs-on: spacetimedb-benchmark-runner + timeout-minutes: 60 + env: + CARGO_TARGET_DIR: ${{ github.workspace }}/target + CARGO_PROFILE_RELEASE_DEBUG: line-tables-only + KEYNOTE_PROFILE_DIR: ${{ github.workspace }}/keynote-profile + STDB_V8_FLAGS: "--perf-basic-prof --perf-basic-prof-only-functions --interpreted-frames-native-stack" + steps: + - name: Checkout sources + uses: actions/checkout@v4 + with: + ref: ${{ github.event.inputs.ref || 'master' }} + + - uses: dsherret/rust-toolchain-file@v1 + - name: Set default rust toolchain + run: rustup default $(rustup show active-toolchain | cut -d' ' -f1) + + - name: Cache Rust dependencies + uses: Swatinem/rust-cache@v2 + with: + workspaces: ${{ github.workspace }} + shared-key: spacetimedb + save-if: false + prefix-key: v1 + + - name: Build keynote benchmark binaries + run: > + cargo + --config 'build.rustflags = ["-C", "force-frame-pointers=yes"]' + build + --release + -p spacetimedb-cli + -p spacetimedb-standalone + --features spacetimedb-standalone/perfmap + + # Node 24 is the current Active LTS line. + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 24 + + - uses: ./.github/actions/setup-pnpm + with: + run_install: true + + - name: Build TypeScript SDK + run: pnpm build + working-directory: crates/bindings-typescript + + - name: Run keynote-2 benchmark regression check + id: keynote_bench + run: | + mkdir -p "$KEYNOTE_PROFILE_DIR" + samply record \ + --save-only \ + --unstable-presymbolicate \ + --profile-name keynote-bench-nightly \ + -o "$KEYNOTE_PROFILE_DIR/profile.json.gz" \ + -- cargo ci keynote-bench + ls -lh "$KEYNOTE_PROFILE_DIR" || true + + - name: Upload keynote benchmark profile + if: always() + uses: actions/upload-artifact@v4 + with: + name: keynote-bench-nightly-profile + path: ${{ env.KEYNOTE_PROFILE_DIR }} + if-no-files-found: warn + retention-days: 90 + + - name: Report keynote benchmark profile instructions + if: failure() && steps.keynote_bench.outcome == 'failure' + run: | + { + echo "## Keynote benchmark profile" + echo + echo "The nightly keynote benchmark failed after recording a Samply/Firefox profile." + echo + echo "1. Open this run's Summary page: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" + echo "2. Download the artifact named \`keynote-bench-nightly-profile\`." + echo "3. Open and load \`profile.json.gz\`." + echo "4. Compare hot frames against earlier nightly \`keynote-bench-nightly-profile\` artifacts." + } >> "$GITHUB_STEP_SUMMARY" + + echo "::error title=Keynote benchmark profile uploaded::Download the keynote-bench-nightly-profile artifact from this run and open profile.json.gz in https://profiler.firefox.com/."