Skip to content

Commit 679c2ec

Browse files
author
devfive
committed
bench: stabilize Turbopack comparison
1 parent a9d4008 commit 679c2ec

18 files changed

Lines changed: 148 additions & 114 deletions

File tree

benchmark.js

Lines changed: 131 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,97 +1,131 @@
1-
import { existsSync, readdirSync, rmSync, statSync } from 'node:fs'
2-
import { join } from 'node:path'
3-
4-
import { execSync } from 'child_process'
5-
6-
function clearBuildFile() {
7-
const dirs = readdirSync('./benchmark')
8-
for (const dir of dirs) {
9-
const base = join('./benchmark', dir)
10-
if (!statSync(base).isDirectory()) continue
11-
for (const output of ['.next', 'dist', 'df']) {
12-
const target = join(base, output)
13-
if (existsSync(target)) rmSync(target, { recursive: true, force: true })
14-
}
15-
}
16-
}
17-
18-
function checkDirSize(path, filter) {
19-
let totalSize = 0
20-
21-
function calculateSize(directory) {
22-
const entries = readdirSync(directory)
23-
for (const entry of entries) {
24-
const entryPath = join(directory, entry)
25-
if (statSync(entryPath).isDirectory()) {
26-
calculateSize(entryPath) // 재귀적으로 하위 폴더 크기 계산
27-
} else if (!filter || filter(entryPath)) {
28-
const stats = statSync(entryPath)
29-
totalSize += stats.size // 파일 크기 합산
30-
}
31-
}
32-
}
33-
34-
calculateSize(path)
35-
return totalSize
36-
}
37-
38-
// Sum only the size of emitted CSS files. Build-size totals are dominated by
39-
// JS/assets and hide CSS-only differences (e.g. single-importer collapse).
40-
function checkCssSize(path) {
41-
return checkDirSize(path, (p) => p.endsWith('.css'))
42-
}
43-
44-
clearBuildFile()
45-
46-
function benchmark(target) {
47-
// Support both short names ('tailwind' -> next-tailwind) and full names ('vinext-devup-ui')
48-
const hasDir = existsSync(join('./benchmark', target, 'package.json'))
49-
const dir = hasDir ? target : 'next-' + target
50-
51-
performance.mark(target + '-start')
52-
console.profile(target)
53-
execSync('bun run --filter ' + dir + '-benchmark build', {
54-
stdio: 'inherit',
55-
})
56-
console.profileEnd(target)
57-
performance.mark(target + '-end')
58-
performance.measure(target, target + '-start', target + '-end')
59-
60-
const benchmarkDir = join('./benchmark', dir)
61-
// Resolve the real build-output dir. Next.js emits to `.next`; Vite emits to
62-
// `dist`. vinext (Next-on-Vite) emits its real artifacts to `dist` but ALSO
63-
// leaves a tiny vestigial `.next` stub (~988 B, no CSS) - so checking `.next`
64-
// first measured the empty stub and reported "988 bytes (css 0 bytes)" even
65-
// though dist held ~1.28 MB incl. the extracted CSS. Prefer `dist` when it
66-
// exists; fall back to `.next` for pure Next.js apps (which never emit dist).
67-
const distDir = join(benchmarkDir, 'dist')
68-
const outputDir = existsSync(distDir) ? distDir : join(benchmarkDir, '.next')
69-
const duration = (
70-
performance.getEntriesByName(target)[0].duration / 1000
71-
).toFixed(2)
72-
return `${target} ${duration}s ${checkDirSize(outputDir).toLocaleString()} bytes (css ${checkCssSize(outputDir).toLocaleString()} bytes)`
73-
}
74-
75-
let result = []
76-
77-
result.push(benchmark('tailwind'))
78-
result.push(benchmark('stylex'))
79-
result.push(benchmark('stylex-turbo'))
80-
result.push(benchmark('stylex-turbo-devup-ui'))
81-
result.push(benchmark('vanilla-extract'))
82-
result.push(benchmark('kuma-ui'))
83-
result.push(benchmark('panda-css'))
84-
result.push(benchmark('chakra-ui'))
85-
result.push(benchmark('mui'))
86-
result.push(benchmark('devup-ui'))
87-
result.push(benchmark('devup-ui-single'))
88-
result.push(benchmark('tailwind-turbo'))
89-
result.push(benchmark('devup-ui-single-turbo'))
90-
result.push(benchmark('devup-ui-turbo'))
91-
result.push(benchmark('vanilla-extract-devup-ui'))
92-
result.push(benchmark('tailwind-turbo-devup-ui'))
93-
result.push(benchmark('vinext-devup-ui'))
94-
// Multi-component app exercising single-importer collapse (atom dedup).
95-
result.push(benchmark('devup-ui-collapse'))
96-
97-
console.info(result.join('\n'))
1+
import { existsSync, readdirSync, rmSync, statSync } from 'node:fs'
2+
import { join } from 'node:path'
3+
4+
import { execSync } from 'child_process'
5+
6+
function clearBuildFile(dir) {
7+
const base = join('./benchmark', dir)
8+
for (const output of ['.next', 'dist', 'df']) {
9+
const target = join(base, output)
10+
if (existsSync(target)) rmSync(target, { recursive: true, force: true })
11+
}
12+
}
13+
14+
function checkDirSize(path, filter) {
15+
let totalSize = 0
16+
17+
function calculateSize(directory) {
18+
const entries = readdirSync(directory)
19+
for (const entry of entries) {
20+
const entryPath = join(directory, entry)
21+
if (statSync(entryPath).isDirectory()) {
22+
calculateSize(entryPath) // 재귀적으로 하위 폴더 크기 계산
23+
} else if (!filter || filter(entryPath)) {
24+
const stats = statSync(entryPath)
25+
totalSize += stats.size // 파일 크기 합산
26+
}
27+
}
28+
}
29+
30+
calculateSize(path)
31+
return totalSize
32+
}
33+
34+
// Sum only the size of emitted CSS files. Build-size totals are dominated by
35+
// JS/assets and hide CSS-only differences (e.g. single-importer collapse).
36+
function checkCssSize(path) {
37+
return checkDirSize(path, (p) => p.endsWith('.css'))
38+
}
39+
40+
let benchmarkRun = 0
41+
42+
function benchmark(target) {
43+
// Support both short names ('tailwind' -> next-tailwind) and full names ('vinext-devup-ui')
44+
const hasDir = existsSync(join('./benchmark', target, 'package.json'))
45+
const dir = hasDir ? target : 'next-' + target
46+
const run = `${target}-${benchmarkRun++}`
47+
48+
clearBuildFile(dir)
49+
performance.mark(run + '-start')
50+
console.profile(run)
51+
execSync('bun run --filter ' + dir + '-benchmark build', {
52+
stdio: 'inherit',
53+
})
54+
console.profileEnd(run)
55+
performance.mark(run + '-end')
56+
performance.measure(run, run + '-start', run + '-end')
57+
58+
const benchmarkDir = join('./benchmark', dir)
59+
// Resolve the real build-output dir. Next.js emits to `.next`; Vite emits to
60+
// `dist`. vinext (Next-on-Vite) emits its real artifacts to `dist` but ALSO
61+
// leaves a tiny vestigial `.next` stub (~988 B, no CSS) - so checking `.next`
62+
// first measured the empty stub and reported "988 bytes (css 0 bytes)" even
63+
// though dist held ~1.28 MB incl. the extracted CSS. Prefer `dist` when it
64+
// exists; fall back to `.next` for pure Next.js apps (which never emit dist).
65+
const distDir = join(benchmarkDir, 'dist')
66+
const outputDir = existsSync(distDir) ? distDir : join(benchmarkDir, '.next')
67+
const duration = performance.getEntriesByName(run)[0].duration / 1000
68+
return {
69+
duration,
70+
result: `${target} ${duration.toFixed(2)}s ${checkDirSize(outputDir).toLocaleString()} bytes (css ${checkCssSize(outputDir).toLocaleString()} bytes)`,
71+
}
72+
}
73+
74+
let result = []
75+
const turboSamples = new Map([
76+
['tailwind-turbo', []],
77+
['devup-ui-single-turbo', []],
78+
])
79+
80+
function record(target) {
81+
const sample = benchmark(target)
82+
const samples = turboSamples.get(target)
83+
if (samples) samples.push(sample.duration)
84+
result.push(sample.result)
85+
}
86+
87+
record('tailwind')
88+
record('stylex')
89+
record('stylex-turbo')
90+
record('stylex-turbo-devup-ui')
91+
record('vanilla-extract')
92+
record('kuma-ui')
93+
record('panda-css')
94+
record('chakra-ui')
95+
record('mui')
96+
record('devup-ui')
97+
record('devup-ui-single')
98+
record('tailwind-turbo')
99+
record('devup-ui-single-turbo')
100+
record('devup-ui-turbo')
101+
record('vanilla-extract-devup-ui')
102+
record('tailwind-turbo-devup-ui')
103+
record('vinext-devup-ui')
104+
// Multi-component app exercising single-importer collapse (atom dedup).
105+
record('devup-ui-collapse')
106+
107+
// A single fixed-order result on a shared CI runner is too noisy for the two
108+
// Turbopack builds we compare directly. Run six cold samples in alternating
109+
// order so each target runs first three times, then report their medians.
110+
const turboTargets = ['tailwind-turbo', 'devup-ui-single-turbo']
111+
for (let sample = 1; sample < 6; sample++) {
112+
const order = sample % 2 === 0 ? turboTargets : turboTargets.toReversed()
113+
for (const target of order) {
114+
turboSamples.get(target).push(benchmark(target).duration)
115+
}
116+
}
117+
118+
function median(samples) {
119+
const sorted = samples.toSorted((a, b) => a - b)
120+
const middle = sorted.length / 2
121+
return (sorted[middle - 1] + sorted[middle]) / 2
122+
}
123+
124+
for (const target of turboTargets) {
125+
const samples = turboSamples.get(target)
126+
result.push(
127+
`${target} median ${median(samples).toFixed(2)}s (${samples.length} cold samples: ${samples.map((sample) => sample.toFixed(2) + 's').join(', ')})`,
128+
)
129+
}
130+
131+
console.info(result.join('\n'))

benchmark/next-chakra-ui/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"private": true,
66
"scripts": {
77
"dev": "next dev",
8-
"build": "next build --experimental-debug-memory-usage --webpack",
8+
"build": "next build --webpack",
99
"start": "next start",
1010
"lint": "next lint"
1111
},

benchmark/next-devup-ui-collapse/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"private": true,
66
"scripts": {
77
"dev": "next dev",
8-
"build": "next build --experimental-debug-memory-usage",
8+
"build": "next build",
99
"start": "next start",
1010
"lint": "eslint"
1111
},

benchmark/next-devup-ui-single-turbo/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"private": true,
66
"scripts": {
77
"dev": "next dev",
8-
"build": "next build --experimental-debug-memory-usage",
8+
"build": "next build",
99
"start": "next start",
1010
"lint": "eslint"
1111
},

benchmark/next-devup-ui-single/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"private": true,
66
"scripts": {
77
"dev": "next dev",
8-
"build": "next build --experimental-debug-memory-usage --webpack",
8+
"build": "next build --webpack",
99
"start": "next start",
1010
"lint": "eslint"
1111
},

benchmark/next-devup-ui-turbo/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"private": true,
66
"scripts": {
77
"dev": "next dev",
8-
"build": "next build --experimental-debug-memory-usage",
8+
"build": "next build",
99
"start": "next start",
1010
"lint": "eslint"
1111
},

benchmark/next-devup-ui/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"private": true,
66
"scripts": {
77
"dev": "next dev",
8-
"build": "next build --experimental-debug-memory-usage --webpack",
8+
"build": "next build --webpack",
99
"start": "next start",
1010
"lint": "eslint"
1111
},

benchmark/next-kuma-ui/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"private": true,
66
"scripts": {
77
"dev": "next dev",
8-
"build": "next build --experimental-debug-memory-usage --webpack",
8+
"build": "next build --webpack",
99
"start": "next start",
1010
"lint": "next lint"
1111
},

benchmark/next-mui/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"private": true,
66
"scripts": {
77
"dev": "next dev",
8-
"build": "next build --experimental-debug-memory-usage --webpack",
8+
"build": "next build --webpack",
99
"start": "next start",
1010
"lint": "next lint"
1111
},

benchmark/next-panda-css/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"scripts": {
77
"prepare": "panda codegen",
88
"dev": "next dev",
9-
"build": "next build --experimental-debug-memory-usage --webpack",
9+
"build": "next build --webpack",
1010
"start": "next start",
1111
"lint": "next lint"
1212
},

0 commit comments

Comments
 (0)