diff --git a/.github/workflows/site.yml b/.github/workflows/site.yml index 4c8ca9e0d..f697331af 100644 --- a/.github/workflows/site.yml +++ b/.github/workflows/site.yml @@ -8,21 +8,21 @@ on: push: branches: ['main'] paths: - - 'website/**' - - '.github/workflows/site.yml' - - 'jsdoc2md.json' - - 'typedoc.json' - - 'package.json' - - 'packages/**' + - "website/**" + - ".github/workflows/site.yml" + - "jsdoc2md.json" + - "typedoc.json" + - "package.json" + - "packages/**" pull_request: - branches: ['main'] + branches: ["main"] paths: - - 'website/**' - - '.github/workflows/site.yml' - - 'jsdoc2md.json' - - 'typedoc.json' - - 'package.json' - - 'packages/**' + - "website/**" + - ".github/workflows/site.yml" + - "jsdoc2md.json" + - "typedoc.json" + - "package.json" + - "packages/**" # Allows you to run this workflow manually from the Actions tab workflow_dispatch: @@ -89,6 +89,7 @@ jobs: runs-on: ubuntu-latest needs: build if: github.event_name != 'pull_request' + concurrency: deploy-site steps: - name: Deploy to GitHub Pages id: deployment diff --git a/packages/benchmark/src/generateMessage.ts b/packages/benchmark/src/generateMessage.ts index d93c6f580..a01f0d633 100644 --- a/packages/benchmark/src/generateMessage.ts +++ b/packages/benchmark/src/generateMessage.ts @@ -5,7 +5,7 @@ const benchmarkData = await fetch(`https://raw.githubusercontent.com/discordeno/ .then((text) => JSON.parse(text.slice(24))) // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars -const commitSha = await fs.readFile('./sha', 'utf-8') +// const commitSha = await fs.readFile('./sha', 'utf-8') const results = JSON.parse(await fs.readFile('./data.json', 'utf-8')) interface BenchmarksData { @@ -37,7 +37,7 @@ for (const benchmark of latestBaseBenchmarks.benches) { [latestBaseBenchmarks.commit.id]: benchmark, } } -for (let i = 0; i < benchmarks.length; i++) { +for (let i = benchmarks.length - 1; i >= 0; i--) { for (const bench of benchmarks[i].benches) { if (compareWithHead[bench.name]) { compareWithHead[bench.name][benchmarks[i].commit.id] = bench @@ -55,7 +55,10 @@ message += `## Benchmark\n\n` message += '
Detail results of benchmarks\n\n' let header1 = `| Benchmark suite | Base (${latestBaseBenchmarks.commit.id}) |` let header2 = `|-|-|` -for (const [index, commitId] of benchmarks.map((benchmark) => benchmark.commit.id).entries()) { +const commitIds = benchmarks.map((benchmark) => benchmark.commit.id) +const uniqueCommitIds = commitIds.filter((benchmarkCommitId, index) => commitIds.indexOf(benchmarkCommitId) === index) + +for (const [index, commitId] of uniqueCommitIds.entries()) { header1 += index === 0 ? ` Latest Head (${commitId}) |` : ` ${commitId} |` header2 += '-|' } @@ -70,7 +73,7 @@ for (const benchName of Object.keys(compareWithHead)) { } \`${compareWithHead[benchName][latestBaseBenchmarks.commit.id].range}\``} |` : '|' - for (const commitId of benchmarks.map((benchmark) => benchmark.commit.id)) { + for (const commitId of uniqueCommitIds) { benchData += compareWithHead[benchName][commitId] ? ` \`${compareWithHead[benchName][commitId].value}\` ${compareWithHead[benchName][commitId].unit} \`${compareWithHead[benchName][commitId].range}\`|` : '|' diff --git a/scripts/generateMessage.js b/scripts/generateMessage.js index bc90d105a..cbd5df552 100644 --- a/scripts/generateMessage.js +++ b/scripts/generateMessage.js @@ -3,7 +3,7 @@ const benchmarkData = await fetch(`https://raw.githubusercontent.com/discordeno/ .then(async (res) => await res.text()) .then((text) => JSON.parse(text.slice(24))) // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars -const commitSha = await fs.readFile('./sha', 'utf-8') +// const commitSha = await fs.readFile('./sha', 'utf-8') const results = JSON.parse(await fs.readFile('./data.json', 'utf-8')) const benchmarks = results.entries.Benchmark benchmarks.reverse() @@ -14,7 +14,7 @@ for (const benchmark of latestBaseBenchmarks.benches) { [latestBaseBenchmarks.commit.id]: benchmark, } } -for (let i = 0; i < benchmarks.length; i++) { +for (let i = benchmarks.length - 1; i >= 0; i--) { for (const bench of benchmarks[i].benches) { if (compareWithHead[bench.name]) { compareWithHead[bench.name][benchmarks[i].commit.id] = bench @@ -30,7 +30,9 @@ message += `## Benchmark\n\n` message += '
Detail results of benchmarks\n\n' let header1 = `| Benchmark suite | Base (${latestBaseBenchmarks.commit.id}) |` let header2 = `|-|-|` -for (const [index, commitId] of benchmarks.map((benchmark) => benchmark.commit.id).entries()) { +const commitIds = benchmarks.map((benchmark) => benchmark.commit.id) +const uniqueCommitIds = commitIds.filter((benchmarkCommitId, index) => commitIds.indexOf(benchmarkCommitId) === index) +for (const [index, commitId] of uniqueCommitIds.entries()) { header1 += index === 0 ? ` Latest Head (${commitId}) |` : ` ${commitId} |` header2 += '-|' } @@ -43,7 +45,7 @@ for (const benchName of Object.keys(compareWithHead)) { compareWithHead[benchName][latestBaseBenchmarks.commit.id].unit } \`${compareWithHead[benchName][latestBaseBenchmarks.commit.id].range}\``} |` : '|' - for (const commitId of benchmarks.map((benchmark) => benchmark.commit.id)) { + for (const commitId of uniqueCommitIds) { benchData += compareWithHead[benchName][commitId] ? ` \`${compareWithHead[benchName][commitId].value}\` ${compareWithHead[benchName][commitId].unit} \`${compareWithHead[benchName][commitId].range}\`|` : '|' diff --git a/website/docs/benchmark.mdx b/website/docs/benchmark.mdx new file mode 100644 index 000000000..83c9382f8 --- /dev/null +++ b/website/docs/benchmark.mdx @@ -0,0 +1,11 @@ +--- +sidebar_position: 10 +--- + +# Benchmark + +Benchmark runs on every commit pushed on the Discordeno's main branch + +import Benchmark from '@site/src/components/Benchmark' + + diff --git a/website/package.json b/website/package.json index 63d10cc25..9a3eb5403 100644 --- a/website/package.json +++ b/website/package.json @@ -20,9 +20,11 @@ "@docusaurus/preset-classic": "2.3.1", "@easyops-cn/docusaurus-search-local": "^0.34.0", "@mdx-js/react": "^1.6.22", + "chart.js": "^4.2.1", "clsx": "^1.2.1", "prism-react-renderer": "^1.3.5", "react": "^17.0.2", + "react-chartjs-2": "^5.2.0", "react-dom": "^17.0.2" }, "devDependencies": { diff --git a/website/src/components/Benchmark.tsx b/website/src/components/Benchmark.tsx new file mode 100644 index 000000000..9cca40a97 --- /dev/null +++ b/website/src/components/Benchmark.tsx @@ -0,0 +1,157 @@ +import { CategoryScale, Chart as ChartJS, Legend, LinearScale, LineController, LineElement, PointElement, Title, Tooltip } from 'chart.js' +import React, { useEffect, useState } from 'react' +import { Chart } from 'react-chartjs-2' +ChartJS.register(CategoryScale, LineController, LinearScale, PointElement, LineElement, Title, Tooltip, Legend) + +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +const BenchmarkResultChart = ({ + name, + dataset, +}: { + name: string + dataset: Array<{ + bench: { + name: string + range: string | number + unit: string + value: string | number + extra: string | number | undefined + } + commit: { + author: { + email: string + name: string + username: string + } + committer: { + email: string + name: string + username: string + } + distinct: true + id: string + message: string + timestamp: string + tree_id: string + url: string + } + date: number + tool: string + }> +}) => { + const data = { + labels: dataset.map((d) => d.commit.id.slice(0, 7)), + datasets: [ + { + label: name, + data: dataset.map((d) => d.bench.value), + borderColor: '#ff3838', + backgroundColor: '#ff383860', // Add alpha for #rrggbbaa + }, + ], + } + + return ( + 0 ? dataset[0].bench.unit : '', + }, + beginAtZero: true, + }, + }, + plugins: { + tooltip: { + callbacks: { + afterTitle: (items) => { + const index = items[0].dataIndex + const data = dataset[index] + return '\n' + data.commit.message + '\n\n' + data.commit.timestamp + ' committed by @' + data.commit.author.username + '\n' + }, + label: (item) => { + let label = item.formattedValue + const { range, unit } = dataset[item.datasetIndex].bench + label += ` ${unit}` + if (range) { + label += ` (${range})` + } + return label + }, + afterLabel: (item) => { + const { extra } = dataset[item.datasetIndex].bench + return extra ? `\n${extra}` : '' + }, + }, + }, + }, + onClick: (_mouseEvent, activeElems) => { + if (activeElems.length === 0) { + return + } + // XXX: Undocumented. How can we know the index? + const index = activeElems[0].index + const url = dataset[index].commit.url + window.open(url, '_blank') + }, + }} + /> + ) +} + +export default function BenchmarkResultCharts(): JSX.Element { + const [data, setData] = useState<{ entries: { Benchmark: [] } }>() + + useEffect(() => { + if (!data) { + ;(async () => { + setData( + JSON.parse( + (await (await fetch('https://raw.githubusercontent.com/discordeno/discordeno/benchies/benchmarksResult/data.js')).text()).slice(24), + ), + ) + })() + } + }, []) + + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type + function collectBenchesPerTestCase(entries) { + const dataMap = new Map() + for (const entry of entries) { + const { commit, date, tool, benches } = entry + for (const bench of benches) { + const result = { commit, date, tool, bench } + const arr = dataMap.get(bench.name) + if (arr === undefined) { + dataMap.set(bench.name, [result]) + } else { + arr.push(result) + } + } + } + return dataMap + } + + return ( +
+ {data ? ( + Array.from(collectBenchesPerTestCase(data.entries.Benchmark), ([key, value]) => ({ benchName: key, benches: value })).map((bench, index) => ( + + )) + ) : ( + <> + )} +
+ ) +} diff --git a/website/yarn.lock b/website/yarn.lock index d69d409ac..47fc9187b 100644 --- a/website/yarn.lock +++ b/website/yarn.lock @@ -2354,6 +2354,13 @@ __metadata: languageName: node linkType: hard +"@kurkle/color@npm:^0.3.0": + version: 0.3.2 + resolution: "@kurkle/color@npm:0.3.2" + checksum: 79e97b31f8f6efb28c69d373f94b0c7480226fe8ec95221f518ac998e156444a496727ce47de6d728eb5c3369288e794cba82cae34253deb0d472d3bfe080e49 + languageName: node + linkType: hard + "@leichtgewicht/ip-codec@npm:^2.0.1": version: 2.0.4 resolution: "@leichtgewicht/ip-codec@npm:2.0.4" @@ -4110,6 +4117,15 @@ __metadata: languageName: node linkType: hard +"chart.js@npm:^4.2.1": + version: 4.2.1 + resolution: "chart.js@npm:4.2.1" + dependencies: + "@kurkle/color": ^0.3.0 + checksum: 7319fdfd1e29812e87bbc07737e8c9072ff659bbc09ade8dd262f83424cd7e48f89afe5220e11e002c9f236883fae3f3adb8adc16acb4682b0ddcc0b661c3af9 + languageName: node + linkType: hard + "cheerio-select@npm:^2.1.0": version: 2.1.0 resolution: "cheerio-select@npm:2.1.0" @@ -8802,6 +8818,16 @@ __metadata: languageName: node linkType: hard +"react-chartjs-2@npm:^5.2.0": + version: 5.2.0 + resolution: "react-chartjs-2@npm:5.2.0" + peerDependencies: + chart.js: ^4.1.1 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: ace702185be1450e5888a8bcd8b5fc1995067e3b11d236764a67f5567a3d7c32ff16923b8d48d3d39bda6e45135da6c044c9b43fbe8e1978f95aca9d2c0ce348 + languageName: node + linkType: hard + "react-dev-utils@npm:^12.0.1": version: 12.0.1 resolution: "react-dev-utils@npm:12.0.1" @@ -10872,9 +10898,11 @@ __metadata: "@easyops-cn/docusaurus-search-local": ^0.34.0 "@mdx-js/react": ^1.6.22 "@tsconfig/docusaurus": ^1.0.5 + chart.js: ^4.2.1 clsx: ^1.2.1 prism-react-renderer: ^1.3.5 react: ^17.0.2 + react-chartjs-2: ^5.2.0 react-dom: ^17.0.2 typescript: ^4.7.4 languageName: unknown