Skip to content

Fix S-101 multipoint sounding glyphs collapsing onto one anchor #286

Fix S-101 multipoint sounding glyphs collapsing onto one anchor

Fix S-101 multipoint sounding glyphs collapsing onto one anchor #286

Workflow file for this run

name: Performance
on:
pull_request:
branches: [main]
jobs:
perf-gate:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'
# --- Build the BASE branch's PerfRunner into a dedicated dir ---
- name: Checkout base branch
run: git checkout ${{ github.event.pull_request.base.sha }}
# The perf tooling on the base SHA may predate this PR's CLI
# surface (--out-subdir, --append, --round-tag, --side, --scenarios)
# and the perf.iteration activity instrumentation the gate reads.
# Overlay the PR's perf tooling + the tiny Diagnostics.Export delta
# (an --append overload on AddFileExporter) onto the base tree so
# the base runner is built with this PR's runner code linked against
# the base SHA's library code. The library code under test stays
# at the base SHA — only the measurement harness is overlaid.
#
# Directory.Packages.props is overlaid as well because central
# package management requires every PackageReference in the
# overlaid PerfRunner csproj to have a matching PackageVersion;
# if the PR adds a new package (e.g. profiler dependencies) the
# base CPM file will not contain it and restore will fail.
- name: Overlay PR perf tooling onto base tree
run: |
git checkout ${{ github.event.pull_request.head.sha }} -- \
tools/EncDotNet.S100.PerfRunner \
tools/EncDotNet.S100.PerfReport \
tools/perf \
src/EncDotNet.S100.Crs.ProjNet \
src/EncDotNet.S100.Diagnostics.Export \
Directory.Packages.props
- name: Build base PerfRunner
run: |
dotnet publish tools/EncDotNet.S100.PerfRunner/EncDotNet.S100.PerfRunner.csproj \
--configuration Release \
--output /tmp/perf-bin/base \
--no-self-contained
# --- Build the PR branch's PerfRunner into a separate dir ---
- name: Checkout PR branch
run: git checkout ${{ github.event.pull_request.head.sha }}
- name: Build candidate PerfRunner
run: |
dotnet publish tools/EncDotNet.S100.PerfRunner/EncDotNet.S100.PerfRunner.csproj \
--configuration Release \
--output /tmp/perf-bin/cand \
--no-self-contained
# --- Build the PerfReport tool from the PR branch (gate logic) ---
- name: Build PerfReport
run: |
dotnet build tools/EncDotNet.S100.PerfReport/EncDotNet.S100.PerfReport.csproj \
--configuration Release
# --- Interleaved measurement: 5 rounds × 4 iters/side = 20 samples/side ---
- name: Run interleaved performance measurements
run: |
tools/perf/interleave.sh \
--base-runner /tmp/perf-bin/base/EncDotNet.S100.PerfRunner.dll \
--cand-runner /tmp/perf-bin/cand/EncDotNet.S100.PerfRunner.dll \
--base-out /tmp/perf-baseline \
--cand-out /tmp/perf-candidate \
--corpus tests/datasets \
--rounds 5 \
--iters 4 \
--warmup 3 \
--subdir interleaved
# --- First gate pass (median + MAD; suspicious zone allowed) ---
- name: Run performance gate (first pass)
id: gate1
run: |
dotnet run --no-build --configuration Release \
--project tools/EncDotNet.S100.PerfReport -- \
gate \
/tmp/perf-baseline/interleaved \
/tmp/perf-candidate/interleaved \
--threshold 10 \
--min-abs 100 \
--mad-k 3.0 \
--retry-zone-mult 2.0 \
--out /tmp/perf-gate.md
if [[ -s /tmp/perf-gate.md.suspicious.txt ]]; then
echo "suspicious=$(tr '\n' ',' < /tmp/perf-gate.md.suspicious.txt | sed 's/,$//')" >> "$GITHUB_OUTPUT"
else
echo "suspicious=" >> "$GITHUB_OUTPUT"
fi
# --- Retry only the suspicious scenarios with extra rounds ---
- name: Re-test suspicious scenarios
if: steps.gate1.outputs.suspicious != ''
run: |
echo "Re-testing scenarios: ${{ steps.gate1.outputs.suspicious }}"
tools/perf/interleave.sh \
--base-runner /tmp/perf-bin/base/EncDotNet.S100.PerfRunner.dll \
--cand-runner /tmp/perf-bin/cand/EncDotNet.S100.PerfRunner.dll \
--base-out /tmp/perf-baseline \
--cand-out /tmp/perf-candidate \
--corpus tests/datasets \
--rounds 5 \
--iters 4 \
--warmup 3 \
--subdir interleaved \
--scenarios "${{ steps.gate1.outputs.suspicious }}"
# --- Final gate (no retry zone — suspicious scenarios now have ≥40 samples) ---
- name: Run performance gate (final)
if: steps.gate1.outputs.suspicious != ''
run: |
dotnet run --no-build --configuration Release \
--project tools/EncDotNet.S100.PerfReport -- \
gate \
/tmp/perf-baseline/interleaved \
/tmp/perf-candidate/interleaved \
--threshold 10 \
--min-abs 100 \
--mad-k 3.0 \
--retry-zone-mult 1.0 \
--out /tmp/perf-gate.md
- name: Upload gate report
if: always()
uses: actions/upload-artifact@v4
with:
name: perf-gate-report
path: |
/tmp/perf-gate.md
/tmp/perf-gate.md.suspicious.txt
/tmp/perf-baseline/interleaved/
/tmp/perf-candidate/interleaved/
if-no-files-found: ignore
- name: Post PR comment
if: always() && github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const path = '/tmp/perf-gate.md';
if (!fs.existsSync(path)) {
console.log('No gate report found, skipping comment.');
return;
}
const body = fs.readFileSync(path, 'utf8');
const marker = '<!-- perf-gate-comment -->';
const fullBody = `${marker}\n${body}`;
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
const existing = comments.find(c => c.body?.includes(marker));
if (existing) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id,
body: fullBody,
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: fullBody,
});
}