| /* | |
| * Licensed to the Apache Software Foundation (ASF) under one | |
| * or more contributor license agreements. See the NOTICE file | |
| * distributed with this work for additional information | |
| * regarding copyright ownership. The ASF licenses this file | |
| * to you under the Apache License, Version 2.0 (the | |
| * "License"); you may not use this file except in compliance | |
| * with the License. You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, | |
| * software distributed under the License is distributed on an | |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| * KIND, either express or implied. See the License for the | |
| * specific language governing permissions and limitations | |
| * under the License. | |
| */ | |
| import os from 'node:os'; | |
| import { execFileSync } from 'node:child_process'; | |
| import { mkdir, writeFile, appendFile } from 'node:fs/promises'; | |
| import path from 'node:path'; | |
| export function summarize(samples) { | |
| if (!samples.length || samples.some((n) => !Number.isFinite(n))) | |
| throw new Error('Invalid samples'); | |
| const sorted = [...samples].sort((a, b) => a - b); | |
| const n = sorted.length; | |
| return { | |
| n, | |
| median: (sorted[Math.floor((n - 1) / 2)] + sorted[Math.floor(n / 2)]) / 2, | |
| p95: sorted[Math.ceil(n * 0.95) - 1], | |
| samples, | |
| }; | |
| } | |
| export const outputDir = path.resolve(process.env.MAKA_PERF_OUTPUT ?? 'perf-results'); | |
| export async function report(name, metadata, rows) { | |
| if (!rows.length) throw new Error('Empty performance report'); | |
| await mkdir(outputDir, { recursive: true }); | |
| const result = { | |
| schemaVersion: 1, | |
| name, | |
| commit: execFileSync('git', ['rev-parse', 'HEAD'], { encoding: 'utf8' }).trim(), | |
| environment: { | |
| node: process.version, | |
| os: os.version(), | |
| release: os.release(), | |
| arch: os.arch(), | |
| cpu: os.cpus()[0]?.model, | |
| logicalCpus: os.cpus().length, | |
| memoryBytes: os.totalmem(), | |
| runner: process.env.RUNNER_NAME, | |
| image: process.env.ImageVersion, | |
| }, | |
| metadata, | |
| rows, | |
| }; | |
| await writeFile(path.join(outputDir, name + '.json'), JSON.stringify(result, null, 2)); | |
| const summary = [ | |
| '## ' + name, | |
| '', | |
| 'Commit: ' + result.commit, | |
| '', | |
| '| Scenario / metric | n | median | p95 |', | |
| '| --- | ---: | ---: | ---: |', | |
| ...rows.map( | |
| (r) => | |
| '| ' + | |
| r.scenario + | |
| ' / ' + | |
| r.metric + | |
| ' | ' + | |
| r.n + | |
| ' | ' + | |
| r.median.toFixed(3) + | |
| ' | ' + | |
| r.p95.toFixed(3) + | |
| ' |', | |
| ), | |
| '', | |
| 'Raw samples and environment: ' + | |
| name + | |
| '.json. Nearest-rank p95; median averages the two middle samples.', | |
| '', | |
| JSON.stringify(metadata), | |
| '', | |
| ].join('\n'); | |
| await writeFile(path.join(outputDir, name + '.md'), summary); | |
| if (process.env.GITHUB_STEP_SUMMARY) await appendFile(process.env.GITHUB_STEP_SUMMARY, summary); | |
| } |