Skip to content

Commit 3e63846

Browse files
chore: Analyse the typegpu entry points (#2042)
1 parent cf6bdaf commit 3e63846

File tree

10 files changed

+360
-158
lines changed

10 files changed

+360
-158
lines changed

.github/workflows/treeshake-test.yml

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@ name: Tree-shake test
22

33
on:
44
pull_request:
5+
workflow_dispatch:
6+
inputs:
7+
target_branch:
8+
description: 'Target branch to compare against'
9+
required: true
10+
default: 'main'
511

612
jobs:
713
treeshake-test:
@@ -26,7 +32,7 @@ jobs:
2632
uses: actions/checkout@v4
2733
with:
2834
path: target-branch
29-
ref: ${{ github.base_ref }}
35+
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.target_branch || github.base_ref }}
3036

3137
- name: Install dependencies (PR branch)
3238
working-directory: pr-branch
@@ -45,17 +51,25 @@ jobs:
4551
pr-branch/pnpm-lock.yaml
4652
target-branch/pnpm-lock.yaml
4753
54+
- name: Generate import tests on PR branch
55+
working-directory: pr-branch
56+
run: pnpm --filter treeshake-test generate-tests
57+
58+
- name: Generate import tests on target branch
59+
working-directory: target-branch
60+
run: pnpm --filter treeshake-test generate-tests
61+
4862
- name: Run tree-shake test on PR branch
4963
working-directory: pr-branch
50-
run: pnpm --filter treeshake-test test
64+
run: pnpm --filter treeshake-test test --tsdown ${{ github.event_name == 'workflow_dispatch' && '--webpack' || '' }}
5165

5266
- name: Run tree-shake test on target branch
5367
working-directory: target-branch
54-
run: pnpm --filter treeshake-test test
68+
run: pnpm --filter treeshake-test test --tsdown ${{ github.event_name == 'workflow_dispatch' && '--webpack' || '' }}
5569

5670
- name: Compare results
5771
run: |
58-
node pr-branch/apps/treeshake-test/compare-results.ts \
72+
node pr-branch/apps/treeshake-test/compareResults.ts \
5973
pr-branch/apps/treeshake-test/results.json \
6074
target-branch/apps/treeshake-test/results.json \
6175
> comparison.md
@@ -109,3 +123,7 @@ jobs:
109123
} else {
110124
await createOrUpdateComment(issueNumber);
111125
}
126+
await core.summary
127+
.addHeading('Tree-shake Test Results')
128+
.addRaw(comparison)
129+
.write();

apps/treeshake-test/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ dist/
22

33
results.md
44
results.json
5+
tests/*_from_*
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import { build as esbuild } from 'esbuild';
22
import * as fs from 'node:fs/promises';
33
import * as path from 'node:path';
44
import { build as tsdown } from 'tsdown';
5-
import webpack from 'webpack';
65
import esbuildPlugin from 'unplugin-typegpu/esbuild';
7-
import webpackPlugin from 'unplugin-typegpu/webpack';
86
import rolldownPlugin from 'unplugin-typegpu/rolldown';
7+
import webpackPlugin from 'unplugin-typegpu/webpack';
8+
import webpack from 'webpack';
99

1010
export type ResultRecord = {
1111
testFilename: string;
Lines changed: 48 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import { arrayOf, type } from 'arktype';
44
import * as fs from 'node:fs/promises';
5+
import { ResultsTable } from './resultsTable.ts';
56

67
// Define schema for benchmark results
78
const ResultRecord = type({
@@ -24,118 +25,88 @@ function groupResultsByTest(results: typeof BenchmarkResults.infer) {
2425
return grouped;
2526
}
2627

27-
function calculateTrendMessage(
28-
prSize: number | undefined,
29-
targetSize: number | undefined,
30-
): string {
31-
if (prSize === undefined || targetSize === undefined) {
32-
return '';
33-
}
34-
if (prSize === targetSize) {
35-
return '(➖)';
36-
}
37-
const diff = prSize - targetSize;
38-
const percent = ((diff / targetSize) * 100).toFixed(1);
39-
if (diff > 0) {
40-
return `($\${\\color{red}+${percent}\\\\%}$$)`;
41-
}
42-
return `($\${\\color{green}${percent}\\\\%}$$)`;
43-
}
44-
45-
function prettifySize(size: number | undefined) {
46-
if (size === undefined) {
47-
return 'N/A';
48-
}
49-
const units = ['B', 'kB', 'MB', 'GB', 'TB'];
50-
let unitIndex = 0;
51-
let sizeInUnits = size;
52-
while (sizeInUnits > 1024 && unitIndex < units.length) {
53-
sizeInUnits /= 1024;
54-
unitIndex += 1;
55-
}
56-
return `${
57-
Number.isInteger(sizeInUnits) ? sizeInUnits : sizeInUnits.toFixed(2)
58-
} ${units[unitIndex]}`;
59-
}
60-
6128
async function generateReport(
6229
prResults: typeof BenchmarkResults.infer,
6330
targetResults: typeof BenchmarkResults.infer,
6431
) {
6532
const prGrouped = groupResultsByTest(prResults);
6633
const targetGrouped = groupResultsByTest(targetResults);
6734

68-
// Get all unique bundlers from both branches
35+
// All unique bundlers from both branches
6936
const allBundlers = new Set([
7037
...new Set(prResults.map((r) => r.bundler)),
7138
...new Set(targetResults.map((r) => r.bundler)),
7239
]);
7340

74-
// Get all unique tests from both branches
41+
// All unique tests from both branches
7542
const allTests = new Set([
7643
...Object.keys(prGrouped),
7744
...Object.keys(targetGrouped),
7845
]);
7946

80-
let output = '\n\n';
47+
// Split tests into static and dynamic
48+
const staticTests = [...allTests].filter((t) => !t.includes('_from_'))
49+
.sort();
50+
const dynamicTests = [...allTests].filter((t) => t.includes('_from_'))
51+
.sort();
8152

8253
// Summary statistics
83-
let totalIncrease = 0,
84-
totalDecrease = 0,
85-
totalUnchanged = 0,
86-
totalUnknown = 0;
54+
let totalDecreased = 0;
55+
let totalIncreased = 0;
56+
let totalUnchanged = 0;
57+
let totalUnknown = 0;
8758

8859
for (const test of allTests) {
8960
for (const bundler of allBundlers) {
9061
const prSize = prGrouped[test]?.[bundler];
9162
const targetSize = targetGrouped[test]?.[bundler];
9263

9364
if (targetSize === undefined || prSize === undefined) totalUnknown++;
94-
else if (prSize > targetSize) totalIncrease++;
95-
else if (prSize < targetSize) totalDecrease++;
65+
else if (prSize > targetSize) totalIncreased++;
66+
else if (prSize < targetSize) totalDecreased++;
9667
else totalUnchanged++;
9768
}
9869
}
9970

100-
output += '## 📊 Bundle Size Comparison\n\n';
101-
output += '## 📈 Summary\n\n';
102-
output += `- 📈 **Increased**: ${totalIncrease} bundles\n`;
103-
output += `- 📉 **Decreased**: ${totalDecrease} bundles\n`;
104-
output += `- ➖ **Unchanged**: ${totalUnchanged} bundles\n\n`;
105-
output += `- ❔ **Unknown**: ${totalUnknown} bundles\n\n`;
106-
107-
// Main comparison table
108-
output += '## 📋 Bundle Size Comparison\n\n';
109-
110-
// Table header
111-
output += '| Test';
112-
for (const bundler of allBundlers) {
113-
output += ` | ${bundler}`;
114-
}
115-
output += ' |\n';
71+
// Comparison tables
72+
const staticTable = new ResultsTable(allBundlers, 0.005);
73+
const dynamicTable = new ResultsTable(allBundlers, 0.005);
74+
const allTable = new ResultsTable(allBundlers, 0);
11675

117-
// Table separator
118-
output += '|---------';
119-
for (const _ of allBundlers) {
120-
output += '|---------';
121-
}
122-
output += ' |\n';
76+
staticTests.forEach((test) => {
77+
const prResults = prGrouped[test];
78+
const targetResults = targetGrouped[test];
79+
staticTable.addRow(test, prResults, targetResults);
80+
allTable.addRow(test, prResults, targetResults);
81+
});
12382

124-
// Table rows
125-
for (const test of [...allTests].sort()) {
126-
output += `| ${test}`;
83+
dynamicTests.forEach((test) => {
84+
const prResults = prGrouped[test];
85+
const targetResults = targetGrouped[test];
86+
dynamicTable.addRow(test, prResults, targetResults);
87+
allTable.addRow(test, prResults, targetResults);
88+
});
12789

128-
for (const bundler of allBundlers) {
129-
const prSize = prGrouped[test]?.[bundler];
130-
const targetSize = targetGrouped[test]?.[bundler];
90+
// Markdown generation
91+
let output = '';
13192

132-
output += ` | ${prettifySize(prSize)} ${
133-
calculateTrendMessage(prSize, targetSize)
134-
}`;
135-
}
136-
output += ' |\n';
93+
output += '## 📊 Bundle Size Comparison\n\n';
94+
output += '| 🟢 Decreased | ➖ Unchanged | 🔴 Increased | ❔ Unknown |\n';
95+
output += '| :---: | :---: | :---: | :---: |\n';
96+
output +=
97+
`| **${totalDecreased}** | **${totalUnchanged}** | **${totalIncreased}** | **${totalUnknown}** |\n\n`;
98+
99+
output += `## 👀 Notable results\n\n`;
100+
output += `### Static test results:\n${staticTable}\n\n`;
101+
output += `### Dynamic test results:\n${dynamicTable}\n\n`;
102+
103+
output += `## 📋 All results\n\n`;
104+
output += `${allTable}\n\n`;
105+
106+
if (allBundlers.size === 1) {
107+
output +=
108+
`If you wish to run a comparison for other, slower bundlers, run the 'Tree-shake test' from the GitHub Actions menu.\n`;
137109
}
138-
output += '\n';
139110

140111
return output;
141112
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import * as fs from 'node:fs/promises';
2+
import * as tgpuAll from 'typegpu';
3+
import * as dAll from 'typegpu/data';
4+
import * as stdAll from 'typegpu/std';
5+
6+
const TESTS_DIR = new URL('./tests/', import.meta.url);
7+
8+
async function generateTestFiles() {
9+
await fs.mkdir(TESTS_DIR, { recursive: true });
10+
11+
const tgpuAllImports = Object.keys(tgpuAll)
12+
.filter((key) => key !== 'default')
13+
.map((exportName) => ({
14+
export: exportName,
15+
from: 'typegpu',
16+
log: exportName,
17+
}));
18+
19+
const dAllImports = Object.keys(dAll)
20+
.map((exportName) => ({
21+
export: exportName,
22+
from: 'typegpu/data',
23+
log: exportName,
24+
}));
25+
26+
const stdAllImports = Object.keys(stdAll)
27+
.map((exportName) => ({
28+
export: exportName,
29+
from: 'typegpu/std',
30+
log: exportName,
31+
}));
32+
33+
const tgpuImports = Object.keys(tgpuAll.tgpu)
34+
.filter((key) => key !== '~unstable')
35+
.map((exportName) => ({
36+
export: 'tgpu',
37+
from: 'typegpu',
38+
log: `tgpu.${exportName}`,
39+
}));
40+
41+
const imports: { export: string; from: string; log: string }[] = [
42+
...tgpuAllImports,
43+
...dAllImports,
44+
...stdAllImports,
45+
...tgpuImports,
46+
];
47+
48+
for (const { export: exportName, from, log } of imports) {
49+
const testContent = `
50+
import { ${exportName} } from '${from}';
51+
console.log(typeof ${log});
52+
`;
53+
54+
const fileName = `${log}_from_${from.replaceAll('/', '')}.ts`;
55+
await fs.writeFile(new URL(fileName, TESTS_DIR), testContent);
56+
}
57+
}
58+
59+
await generateTestFiles();

apps/treeshake-test/index.ts

Lines changed: 0 additions & 66 deletions
This file was deleted.

apps/treeshake-test/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
"description": "Treeshake testing app for TypeGPU",
66
"type": "module",
77
"scripts": {
8-
"test": "node index.ts"
8+
"generate-tests": "tsx generateTests.ts",
9+
"test": "node runTests.ts"
910
},
1011
"dependencies": {
1112
"typegpu": "workspace:*",
@@ -17,6 +18,7 @@
1718
"esbuild": "^0.25.10",
1819
"ts-loader": "^9.5.4",
1920
"tsdown": "^0.15.6",
21+
"tsx": "^4.19.2",
2022
"typescript": "catalog:types",
2123
"webpack": "^5.102.0",
2224
"webpack-cli": "^6.0.1"

0 commit comments

Comments
 (0)