Skip to content

Commit 95092d4

Browse files
committed
chore: add V8 debug utilities to prettierignore and track them
1 parent 5d7ccc2 commit 95092d4

File tree

3 files changed

+253
-0
lines changed

3 files changed

+253
-0
lines changed

.prettierignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
yarn.lock
22
.angular
33
packages/core/src/indent.ts
4+
packages/type/benchmarks/v8-*.js
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
/**
2+
* V8 Bytecode and Optimization Analysis
3+
*
4+
* Run with:
5+
* node --print-bytecode --print-bytecode-filter=* benchmarks/v8-bytecode-analysis.js 2>&1 | head -200
6+
* node --trace-opt --trace-deopt benchmarks/v8-bytecode-analysis.js 2>&1
7+
* node --allow-natives-syntax benchmarks/v8-bytecode-analysis.js
8+
*/
9+
10+
// Test functions - using plain JS for clean bytecode output
11+
function directReturn(s0) {
12+
return { tags: s0.tags, priority: s0.priority, id: s0.id, name: s0.name };
13+
}
14+
15+
function varThenReturn(s0) {
16+
var s3 = { tags: s0.tags, priority: s0.priority, id: s0.id, name: s0.name };
17+
return s3;
18+
}
19+
20+
function withOptional(s0) {
21+
var s3 = { tags: s0.tags, priority: s0.priority, id: s0.id, name: s0.name };
22+
if ('ready' in s0) {
23+
s3.ready = s0.ready ?? null;
24+
}
25+
return s3;
26+
}
27+
28+
// Test inputs
29+
const inputWith = { id: 1, name: 'test', ready: true, tags: ['a', 'b'], priority: 5 };
30+
const inputWithout = { id: 1, name: 'test', tags: ['a', 'b'], priority: 5 };
31+
32+
// Force V8 to compile and optimize
33+
function warmup(fn, input, count = 100000) {
34+
for (let i = 0; i < count; i++) {
35+
fn(input);
36+
}
37+
}
38+
39+
// Check optimization status if --allow-natives-syntax is enabled
40+
// Must use eval to avoid parse errors when flag is not set
41+
const checkOptStatus = new Function(
42+
'fn',
43+
'name',
44+
`
45+
try {
46+
const status = %GetOptimizationStatus(fn);
47+
const flags = [];
48+
if (status & 1) flags.push('is_function');
49+
if (status & 2) flags.push('never_optimized');
50+
if (status & 4) flags.push('always_optimized');
51+
if (status & 8) flags.push('maybe_deopted');
52+
if (status & 16) flags.push('optimized');
53+
if (status & 32) flags.push('turbofan');
54+
if (status & 64) flags.push('interpreted');
55+
if (status & 128) flags.push('maglev');
56+
if (status & 256) flags.push('sparkplug');
57+
console.log(name + ': status=' + status + ' [' + flags.join(', ') + ']');
58+
} catch(e) {
59+
console.log(name + ': (natives not available)');
60+
}
61+
`,
62+
);
63+
64+
console.log('=== V8 Bytecode Analysis ===\n');
65+
66+
// Warmup all functions
67+
console.log('Warming up functions...');
68+
warmup(directReturn, inputWith);
69+
warmup(varThenReturn, inputWith);
70+
warmup(withOptional, inputWith);
71+
warmup(withOptional, inputWithout);
72+
73+
console.log('\n=== Optimization Status ===\n');
74+
75+
try {
76+
checkOptStatus(directReturn, 'directReturn');
77+
checkOptStatus(varThenReturn, 'varThenReturn');
78+
checkOptStatus(withOptional, 'withOptional');
79+
} catch (e) {
80+
console.log('(Run with --allow-natives-syntax to see optimization status)\n');
81+
}
82+
83+
// Benchmark with result accumulation to prevent DCE
84+
function benchmark(name, fn, input, iterations = 2000000) {
85+
const results = [];
86+
87+
// Pre-warmup
88+
for (let i = 0; i < 50000; i++) results.push(fn(input));
89+
results.length = 0;
90+
91+
const start = performance.now();
92+
for (let i = 0; i < iterations; i++) {
93+
results.push(fn(input));
94+
}
95+
const end = performance.now();
96+
97+
const opsPerSec = iterations / ((end - start) / 1000);
98+
console.log(`${name}: ${(opsPerSec / 1000000).toFixed(1)}M ops/s`);
99+
100+
return results.length; // Use results to prevent elimination
101+
}
102+
103+
console.log('=== Benchmark Results ===\n');
104+
105+
benchmark('directReturn', directReturn, inputWith);
106+
benchmark('varThenReturn', varThenReturn, inputWith);
107+
benchmark('withOptional (has ready)', withOptional, inputWith);
108+
benchmark('withOptional (no ready)', withOptional, inputWithout);
109+
110+
// Mixed input test - this should trigger deoptimization
111+
console.log('\n=== Mixed Input Test (triggers polymorphism) ===\n');
112+
113+
function benchmarkMixed(name, fn, iterations = 2000000) {
114+
const results = [];
115+
116+
for (let i = 0; i < 50000; i++) {
117+
results.push(fn(i % 2 === 0 ? inputWith : inputWithout));
118+
}
119+
results.length = 0;
120+
121+
const start = performance.now();
122+
for (let i = 0; i < iterations; i++) {
123+
results.push(fn(i % 2 === 0 ? inputWith : inputWithout));
124+
}
125+
const end = performance.now();
126+
127+
const opsPerSec = iterations / ((end - start) / 1000);
128+
console.log(`${name} (mixed): ${(opsPerSec / 1000000).toFixed(1)}M ops/s`);
129+
130+
return results.length;
131+
}
132+
133+
benchmarkMixed('withOptional', withOptional);
134+
135+
console.log('\n=== Object Shape Analysis ===\n');
136+
137+
const r1 = directReturn(inputWith);
138+
const r2 = varThenReturn(inputWith);
139+
const r3 = withOptional(inputWith);
140+
const r4 = withOptional(inputWithout);
141+
142+
console.log('directReturn keys:', Object.keys(r1));
143+
console.log('varThenReturn keys:', Object.keys(r2));
144+
console.log('withOptional(with) keys:', Object.keys(r3));
145+
console.log('withOptional(without) keys:', Object.keys(r4));
146+
147+
console.log('\n=== Done ===');
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/**
2+
* V8 Force Optimization Analysis
3+
*
4+
* Run with: node --allow-natives-syntax benchmarks/v8-force-opt.js
5+
*/
6+
7+
function directReturn(s0) {
8+
return {tags: s0.tags, priority: s0.priority, id: s0.id, name: s0.name};
9+
}
10+
11+
function varThenReturn(s0) {
12+
var s3 = {tags: s0.tags, priority: s0.priority, id: s0.id, name: s0.name};
13+
return s3;
14+
}
15+
16+
function withOptional(s0) {
17+
var s3 = {tags: s0.tags, priority: s0.priority, id: s0.id, name: s0.name};
18+
if ("ready" in s0) {
19+
s3.ready = s0.ready ?? null;
20+
}
21+
return s3;
22+
}
23+
24+
const inputWith = { id: 1, name: 'test', ready: true, tags: ['a', 'b'], priority: 5 };
25+
const inputWithout = { id: 1, name: 'test', tags: ['a', 'b'], priority: 5 };
26+
27+
function getOptStatusString(status) {
28+
const flags = [];
29+
if (status & 1) flags.push('function');
30+
if (status & 2) flags.push('never_opt');
31+
if (status & 4) flags.push('always_opt');
32+
if (status & 8) flags.push('maybe_deopted');
33+
if (status & 16) flags.push('OPTIMIZED');
34+
if (status & 32) flags.push('TURBOFAN');
35+
if (status & 64) flags.push('interpreted');
36+
if (status & 128) flags.push('MAGLEV');
37+
if (status & 256) flags.push('sparkplug');
38+
return `${status} [${flags.join(', ')}]`;
39+
}
40+
41+
console.log('=== Before any calls ===');
42+
console.log('directReturn:', getOptStatusString(%GetOptimizationStatus(directReturn)));
43+
console.log('varThenReturn:', getOptStatusString(%GetOptimizationStatus(varThenReturn)));
44+
console.log('withOptional:', getOptStatusString(%GetOptimizationStatus(withOptional)));
45+
46+
console.log('\n=== Warmup (1000 calls each) ===');
47+
for (let i = 0; i < 1000; i++) {
48+
directReturn(inputWith);
49+
varThenReturn(inputWith);
50+
withOptional(inputWith);
51+
}
52+
53+
console.log('directReturn:', getOptStatusString(%GetOptimizationStatus(directReturn)));
54+
console.log('varThenReturn:', getOptStatusString(%GetOptimizationStatus(varThenReturn)));
55+
console.log('withOptional:', getOptStatusString(%GetOptimizationStatus(withOptional)));
56+
57+
console.log('\n=== Force optimize ===');
58+
%OptimizeFunctionOnNextCall(directReturn);
59+
%OptimizeFunctionOnNextCall(varThenReturn);
60+
%OptimizeFunctionOnNextCall(withOptional);
61+
62+
// Trigger the optimization
63+
directReturn(inputWith);
64+
varThenReturn(inputWith);
65+
withOptional(inputWith);
66+
67+
console.log('directReturn:', getOptStatusString(%GetOptimizationStatus(directReturn)));
68+
console.log('varThenReturn:', getOptStatusString(%GetOptimizationStatus(varThenReturn)));
69+
console.log('withOptional:', getOptStatusString(%GetOptimizationStatus(withOptional)));
70+
71+
console.log('\n=== Heavy warmup (100k calls) ===');
72+
for (let i = 0; i < 100000; i++) {
73+
directReturn(inputWith);
74+
varThenReturn(inputWith);
75+
withOptional(inputWith);
76+
}
77+
78+
console.log('directReturn:', getOptStatusString(%GetOptimizationStatus(directReturn)));
79+
console.log('varThenReturn:', getOptStatusString(%GetOptimizationStatus(varThenReturn)));
80+
console.log('withOptional:', getOptStatusString(%GetOptimizationStatus(withOptional)));
81+
82+
// Benchmark with forced optimization
83+
console.log('\n=== Benchmark (after forced opt) ===');
84+
85+
function bench(name, fn, input, iters = 2000000) {
86+
const acc = [];
87+
for (let i = 0; i < 10000; i++) acc.push(fn(input));
88+
acc.length = 0;
89+
90+
const start = performance.now();
91+
for (let i = 0; i < iters; i++) acc.push(fn(input));
92+
const end = performance.now();
93+
94+
console.log(`${name}: ${(iters / ((end - start) / 1000) / 1e6).toFixed(1)}M ops/s (len=${acc.length})`);
95+
}
96+
97+
bench('directReturn ', directReturn, inputWith);
98+
bench('varThenReturn ', varThenReturn, inputWith);
99+
bench('withOptional(+) ', withOptional, inputWith);
100+
bench('withOptional(-) ', withOptional, inputWithout);
101+
102+
console.log('\n=== Final optimization status ===');
103+
console.log('directReturn:', getOptStatusString(%GetOptimizationStatus(directReturn)));
104+
console.log('varThenReturn:', getOptStatusString(%GetOptimizationStatus(varThenReturn)));
105+
console.log('withOptional:', getOptStatusString(%GetOptimizationStatus(withOptional)));

0 commit comments

Comments
 (0)