From b5e470b6d4c3635b382b2a6fe6b327b5d4a9f420 Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Thu, 16 Jul 2026 15:59:41 +0900 Subject: [PATCH] enhance(dev): tweak get-backend-memory --- .github/scripts/backend-memory-report.mts | 5 +- .../measure-backend-memory-comparison.mts | 46 +++++++++++-------- .github/workflows/get-backend-memory.yml | 9 +++- .github/workflows/report-backend-memory.yml | 19 +++++--- 4 files changed, 51 insertions(+), 28 deletions(-) diff --git a/.github/scripts/backend-memory-report.mts b/.github/scripts/backend-memory-report.mts index fc64cb6db5..4a701655ec 100644 --- a/.github/scripts/backend-memory-report.mts +++ b/.github/scripts/backend-memory-report.mts @@ -384,8 +384,9 @@ if (heapSnapshotSection != null) { lines.push(''); } -const artifactUrl = process.env.MK_MEMORY_HEAP_SNAPSHOT_ARTIFACT_URL_HEAD!.trim(); -lines.push(`[Download representative V8 heap snapshot (head)](${artifactUrl})`); +const baseHeapSnapshotArtifactUrl = process.env.MK_MEMORY_HEAP_SNAPSHOT_ARTIFACT_URL_BASE!.trim(); +const headHeapSnapshotArtifactUrl = process.env.MK_MEMORY_HEAP_SNAPSHOT_ARTIFACT_URL_HEAD!.trim(); +lines.push(`Download representative V8 heap snapshot [base](${baseHeapSnapshotArtifactUrl}) / [head](${headHeapSnapshotArtifactUrl})`); lines.push(''); const jsFootprintSection = renderJsFootprintSection(baseJsFootprint, headJsFootprint); diff --git a/.github/scripts/measure-backend-memory-comparison.mts b/.github/scripts/measure-backend-memory-comparison.mts index ee95c68165..a4e93b3ce4 100644 --- a/.github/scripts/measure-backend-memory-comparison.mts +++ b/.github/scripts/measure-backend-memory-comparison.mts @@ -28,8 +28,15 @@ export type MemoryReport = { const [baseDirArg, headDirArg, baseOutputArg, headOutputArg] = process.argv.slice(2); const HEAP_SNAPSHOT_BREAKDOWN_TOP_N = util.readIntegerEnv('MK_MEMORY_HEAP_SNAPSHOT_BREAKDOWN_TOP_N', heapSnapshotUtil.defaultHeapSnapshotBreakdownTopN, 1); -const HEAD_HEAP_SNAPSHOT_WORK_DIR = resolve('head-heap-snapshots'); -const HEAD_HEAP_SNAPSHOT_OUTPUT_PATH = resolve('head-heap-snapshot.heapsnapshot'); +const heapSnapshotLabels = ['base', 'head'] as const; +const HEAP_SNAPSHOT_WORK_DIRS = { + base: resolve('base-heap-snapshots'), + head: resolve('head-heap-snapshots'), +}; +const HEAP_SNAPSHOT_OUTPUT_PATHS = { + base: resolve('base-heap-snapshot.heapsnapshot'), + head: resolve('head-heap-snapshot.heapsnapshot'), +}; // Use the head checkout's measurement harness for both targets so only the built backend differs. const MEASURE_MEMORY_SCRIPT = resolve(import.meta.dirname, '../../packages/backend/scripts/measure-memory.mts'); @@ -119,11 +126,11 @@ async function measureRepo(label: string, repoDir: string, round: number, option return JSON.parse(stdout) as MemorySample; } -function headHeapSnapshotPath(round: number) { - return join(HEAD_HEAP_SNAPSHOT_WORK_DIR, `round-${round}.heapsnapshot`); +function heapSnapshotPath(label: typeof heapSnapshotLabels[number], round: number) { + return join(HEAP_SNAPSHOT_WORK_DIRS[label], `round-${round}.heapsnapshot`); } -function selectRepresentativeHeadHeapSnapshotRound(samples: MemoryReport['samples'], summary: MemoryReport['summary']) { +function selectRepresentativeHeapSnapshotRound(samples: MemoryReport['samples'], summary: MemoryReport['summary']) { const medianTotal = summary.afterGc.heapSnapshot?.categories?.total; if (medianTotal == null || !Number.isFinite(medianTotal)) return null; @@ -144,13 +151,13 @@ function selectRepresentativeHeadHeapSnapshotRound(samples: MemoryReport['sample return selected?.round ?? null; } -async function saveRepresentativeHeadHeapSnapshot(samples: MemoryReport['samples'], summary: MemoryReport['summary']) { - const round = selectRepresentativeHeadHeapSnapshotRound(samples, summary); +async function saveRepresentativeHeapSnapshot(label: typeof heapSnapshotLabels[number], samples: MemoryReport['samples'], summary: MemoryReport['summary']) { + const round = selectRepresentativeHeapSnapshotRound(samples, summary); if (round == null) return; - await copyFile(headHeapSnapshotPath(round), HEAD_HEAP_SNAPSHOT_OUTPUT_PATH); - process.stderr.write(`Selected head heap snapshot round ${round} for artifact\n`); - await rm(HEAD_HEAP_SNAPSHOT_WORK_DIR, { recursive: true, force: true }); + await copyFile(heapSnapshotPath(label, round), HEAP_SNAPSHOT_OUTPUT_PATHS[label]); + process.stderr.write(`Selected ${label} heap snapshot round ${round} for artifact\n`); + await rm(HEAP_SNAPSHOT_WORK_DIRS[label], { recursive: true, force: true }); } async function main() { @@ -162,8 +169,10 @@ async function main() { const warmupRounds = util.readIntegerEnv('MK_MEMORY_COMPARE_WARMUP_ROUNDS', 1, 0); const startedAt = new Date().toISOString(); - await rm(HEAD_HEAP_SNAPSHOT_WORK_DIR, { recursive: true, force: true }); - await rm(HEAD_HEAP_SNAPSHOT_OUTPUT_PATH, { force: true }); + for (const label of heapSnapshotLabels) { + await rm(HEAP_SNAPSHOT_WORK_DIRS[label], { recursive: true, force: true }); + await rm(HEAP_SNAPSHOT_OUTPUT_PATHS[label], { force: true }); + } const reports = { base: { @@ -178,7 +187,7 @@ async function main() { for (let round = 1; round <= warmupRounds; round++) { process.stderr.write(`Starting warmup round ${round}/${warmupRounds}\n`); - for (const label of ['base', 'head'] as const) { + for (const label of heapSnapshotLabels) { await measureRepo(label, reports[label].dir, -round); } } @@ -188,10 +197,7 @@ async function main() { process.stderr.write(`Starting measurement round ${round}/${rounds}: ${order.join(' -> ')}\n`); for (const label of order) { - const shouldSaveHeadHeapSnapshot = label === 'head'; - const options = shouldSaveHeadHeapSnapshot - ? { heapSnapshotSavePath: headHeapSnapshotPath(round) } - : {}; + const options = { heapSnapshotSavePath: heapSnapshotPath(label, round) }; const sample = await measureRepo(label, reports[label].dir, round, options); reports[label].samples.push({ ...sample, @@ -204,9 +210,11 @@ async function main() { base: summarizeSamples(reports.base.samples), head: summarizeSamples(reports.head.samples), }; - await saveRepresentativeHeadHeapSnapshot(reports.head.samples, summaries.head); + for (const label of heapSnapshotLabels) { + await saveRepresentativeHeapSnapshot(label, reports[label].samples, summaries[label]); + } - for (const label of ['base', 'head'] as const) { + for (const label of heapSnapshotLabels) { const report = { timestamp: new Date().toISOString(), sampleCount: reports[label].samples.length, diff --git a/.github/workflows/get-backend-memory.yml b/.github/workflows/get-backend-memory.yml index 0d0bec3307..03f82789a3 100644 --- a/.github/workflows/get-backend-memory.yml +++ b/.github/workflows/get-backend-memory.yml @@ -97,11 +97,18 @@ jobs: MK_MEMORY_COMPARE_WARMUP_ROUNDS: 1 MK_MEMORY_HEAP_SNAPSHOT: 1 run: node head/.github/scripts/measure-backend-memory-comparison.mts base head memory-base.json memory-head.json + - name: Upload base heap snapshot + uses: actions/upload-artifact@v7 + with: + path: base-heap-snapshot.heapsnapshot + archive: false + if-no-files-found: error + retention-days: 7 - name: Upload head heap snapshot uses: actions/upload-artifact@v7 with: - name: backend-memory-head-heap-snapshot path: head-heap-snapshot.heapsnapshot + archive: false if-no-files-found: error retention-days: 7 - name: Measure backend loaded JS footprint diff --git a/.github/workflows/report-backend-memory.yml b/.github/workflows/report-backend-memory.yml index 3343ec745e..a6c0b4afdf 100644 --- a/.github/workflows/report-backend-memory.yml +++ b/.github/workflows/report-backend-memory.yml @@ -33,8 +33,8 @@ jobs: - name: Load PR Number id: load-pr-num run: echo "pr-number=$(cat artifacts/pr_number)" >> "$GITHUB_OUTPUT" - - name: Find head heap snapshot artifact - id: find-heap-snapshot-artifact + - name: Find heap snapshot artifacts + id: find-heap-snapshot-artifacts uses: actions/github-script@v8 with: script: | @@ -45,14 +45,21 @@ jobs: repo, run_id, }); - const artifact = artifacts.find(artifact => artifact.name === 'backend-memory-head-heap-snapshot'); - if (artifact == null) return; - core.setOutput('url', `https://github.com/${owner}/${repo}/actions/runs/${run_id}/artifacts/${artifact.id}`); + const artifactNames = { + base: 'base-heap-snapshot.heapsnapshot', + head: 'head-heap-snapshot.heapsnapshot', + }; + for (const [label, artifactName] of Object.entries(artifactNames)) { + const artifact = artifacts.find(artifact => artifact.name === artifactName); + if (artifact == null) throw new Error(`Artifact not found: ${artifactName}`); + core.setOutput(`${label}-url`, `https://github.com/${owner}/${repo}/actions/runs/${run_id}/artifacts/${artifact.id}`); + } - id: build-comment name: Build memory comment env: - MK_MEMORY_HEAP_SNAPSHOT_ARTIFACT_URL_HEAD: ${{ steps.find-heap-snapshot-artifact.outputs.url }} + MK_MEMORY_HEAP_SNAPSHOT_ARTIFACT_URL_BASE: ${{ steps.find-heap-snapshot-artifacts.outputs.base-url }} + MK_MEMORY_HEAP_SNAPSHOT_ARTIFACT_URL_HEAD: ${{ steps.find-heap-snapshot-artifacts.outputs.head-url }} run: node .github/scripts/backend-memory-report.mts ./artifacts/memory-base.json ./artifacts/memory-head.json ./output.md ./artifacts/js-footprint-base.json ./artifacts/js-footprint-head.json - uses: thollander/actions-comment-pull-request@v3 with: