fix(parser): object-literal async shorthand methods now track async-f… #72
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
| name: Perf Timeline | |
| # Records one immutable benchmark snapshot per commit on the dedicated, unprotected | |
| # `perf-data` branch (NOT main — keeps main clean and dodges branch protection). | |
| # The "Are we fast yet?" page (pages.yml) reads these at deploy time. Each snapshot | |
| # is anchor-normalized, so the trend is meaningful across varying CI runners. | |
| on: | |
| push: | |
| branches: [main] | |
| paths-ignore: | |
| - 'docs/perf/index.html' | |
| - 'docs/perf/timeline/**' | |
| # Manual backfill: dispatch once per historical commit to fill the timeline. | |
| # Snapshot identity is derived from the checked-out HEAD (sha + commit time), | |
| # so the rest of the job is commit-agnostic. | |
| workflow_dispatch: | |
| inputs: | |
| ref: | |
| description: 'Commit SHA/ref to snapshot (backfill)' | |
| required: true | |
| default: 'main' | |
| test262: | |
| description: 'Also run the ~4-min Test262 macro benchmark' | |
| type: boolean | |
| default: false | |
| permissions: | |
| contents: write | |
| concurrency: | |
| group: perf-timeline-main | |
| # Serialize snapshot runs — they push to the perf-data branch, which can't take | |
| # concurrent writers. Forward-only (one snapshot per push) means no backfill | |
| # bursts to queue, so cancel-in-progress: false is enough. | |
| cancel-in-progress: false | |
| jobs: | |
| snapshot: | |
| if: github.actor != 'github-actions[bot]' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Check out target ref | |
| env: | |
| TARGET_REF: ${{ github.event.inputs.ref || github.sha }} | |
| run: | | |
| # A short SHA is read by actions/checkout as a branch name and fails; | |
| # fetch-depth:0 pulled everything, so resolve any committish locally. | |
| git checkout --force "$TARGET_REF" | |
| # Corpus for the opt-in Test262 macro series (see "Add Test262 macro" below). | |
| # Gated on the same condition so non-opted-in commits skip the restore. | |
| - name: Cache Test262 corpus | |
| if: contains(github.event.head_commit.message, '[perf-test262]') || github.event.inputs.test262 == 'true' | |
| uses: actions/cache@v4 | |
| with: | |
| path: test262 | |
| key: test262-${{ hashFiles('.test262-rev') }} | |
| restore-keys: test262- | |
| - uses: actions/setup-go@v5 | |
| with: | |
| go-version-file: go.mod | |
| - name: Capture snapshot | |
| id: snap | |
| run: | | |
| set -euo pipefail | |
| short_sha="$(git rev-parse --short=12 HEAD)" | |
| commit_epoch="$(git show -s --format=%ct HEAD)" | |
| stamp="$(date -u -d "@${commit_epoch}" +%Y%m%dT%H%M%SZ)" | |
| name="${stamp}-${short_sha}.json" | |
| out="${RUNNER_TEMP}/${name}" | |
| go run ./cmd/bench-ratchet \ | |
| -count 3 -benchtime 1s -timeout 20m \ | |
| -baseline "${out}" \ | |
| snapshot | |
| echo "name=${name}" >> "$GITHUB_OUTPUT" | |
| echo "out=${out}" >> "$GITHUB_OUTPUT" | |
| # Opt-in: fold the Test262 macro-benchmark into the snapshot as a | |
| # `test262.total` series. Runs only on commits whose message carries | |
| # `[perf-test262]`, or a manual dispatch with the `test262` input checked, | |
| # since the macro adds ~4 min. Non-fatal: a macro hiccup must never drop | |
| # the micro snapshot above. | |
| - name: Add Test262 macro to snapshot | |
| if: contains(github.event.head_commit.message, '[perf-test262]') || github.event.inputs.test262 == 'true' | |
| continue-on-error: true | |
| env: | |
| PER_TEST_TIMEOUT: 1.2s | |
| run: | | |
| set -euo pipefail | |
| out="${{ steps.snap.outputs.out }}" | |
| ./setup-test262.sh | |
| ./scripts/macro-test262.sh "${RUNNER_TEMP}/t262.jsonl" "${RUNNER_TEMP}/t262.stats.json" | |
| # JSONL: one record per line, so filter per-line — no -s slurp needed. | |
| total_ns="$(jq 'select(.name=="total") | .ns_per_op' "${RUNNER_TEMP}/t262.jsonl")" | |
| passed="$(jq 'select(.name=="total") | .iterations' "${RUNNER_TEMP}/t262.jsonl")" | |
| set_hash="$(jq -r 'select(.name=="total") | .set_hash // ""' "${RUNNER_TEMP}/t262.jsonl")" | |
| anchor_ns="$(jq -r '.anchor.ns_per_op' "$out")" | |
| # Skip the fold if nothing passed — mean over an empty set is undefined | |
| # (and the series would carry no signal anyway). | |
| if [ "${passed:-0}" -gt 0 ]; then | |
| # Metric is the MEAN per-test ns (summed_ns / passing_count), not the sum: | |
| # the sum conflates per-test speed with how many tests pass, so a | |
| # conformance win (more passing tests add their durations) reads as a perf | |
| # regression. The mean removes that count-inflation. set_hash fingerprints | |
| # the exact contributing set (from bench-test262, #24), so the consumer can | |
| # still see a residual composition shift the mean can't cancel. Anchor- | |
| # normalized like every other series; the page plots each self-relative, so | |
| # the absolute ratio doesn't affect chart scale. | |
| jq --argjson ns "$total_ns" --argjson a "$anchor_ns" --argjson p "$passed" --arg h "$set_hash" ' | |
| ($ns / $p) as $mean | |
| | .benchmarks["test262.total"] = ({ | |
| ns_per_op: $mean, allocs_per_op: 0, bytes_per_op: 0, | |
| ratio_to_anchor: ($mean / $a), | |
| samples: [{ iterations: $p, ns_per_op: $mean, ratio_to_anchor: ($mean / $a) }] | |
| } + (if $h == "" then {} else { set_hash: $h } end))' "$out" > "${out}.tmp" | |
| mv "${out}.tmp" "$out" | |
| echo "test262.total: mean $(jq -r '.benchmarks["test262.total"].ns_per_op' "$out") ns/test" \ | |
| "over ${passed} passing tests (set ${set_hash:-none}," \ | |
| "ratio $(jq -r '.benchmarks["test262.total"].ratio_to_anchor' "$out"))" | |
| fi | |
| - name: Render run summary | |
| run: | | |
| src="${{ steps.snap.outputs.out }}" | |
| { | |
| echo "## Snapshot ${{ steps.snap.outputs.name }}" | |
| echo | |
| echo "anchor \`$(jq -r '.anchor.ns_per_op' "$src")\` ns/op · machine \`$(jq -r '.machine.cpu_model' "$src")\` · go \`$(jq -r '.machine.go_version' "$src")\`" | |
| echo | |
| echo "| Benchmark | Anchor× | Wall (ns/op) | Allocs/op |" | |
| echo "|---|---:|---:|---:|" | |
| jq -r '.benchmarks | to_entries | sort_by(-.value.ratio_to_anchor)[] | |
| | "| \(.key | sub("github.com/nooga/paserati/";"")) | \((.value.ratio_to_anchor*1000|round)/1000) | \(.value.ns_per_op|round) | \(.value.allocs_per_op) |"' "$src" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| - name: Publish to perf-data branch | |
| run: | | |
| set -euo pipefail | |
| name="${{ steps.snap.outputs.name }}" | |
| src="${{ steps.snap.outputs.out }}" | |
| remote="https://x-access-token:${{ github.token }}@github.com/${{ github.repository }}.git" | |
| wt="${RUNNER_TEMP}/perf-data" | |
| if git ls-remote --exit-code --heads "$remote" perf-data >/dev/null 2>&1; then | |
| git clone --depth 1 --branch perf-data "$remote" "$wt" | |
| else | |
| # First run: orphan branch with no history/files. | |
| git clone --depth 1 "$remote" "$wt" | |
| git -C "$wt" checkout --orphan perf-data | |
| git -C "$wt" rm -rf . >/dev/null 2>&1 || true | |
| fi | |
| mkdir -p "$wt/timeline" | |
| cp "$src" "$wt/timeline/$name" | |
| git -C "$wt" config user.name "github-actions[bot]" | |
| git -C "$wt" config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git -C "$wt" add timeline | |
| if git -C "$wt" diff --cached --quiet; then | |
| echo "snapshot already present — nothing to publish" | |
| exit 0 | |
| fi | |
| git -C "$wt" commit -m "perf: snapshot ${name}" | |
| git -C "$wt" push "$remote" HEAD:perf-data |