Skip to content

Commit

Permalink
Add Deep JS stacks example
Browse files Browse the repository at this point in the history
  • Loading branch information
paulirish committed Jan 7, 2025
1 parent 43093b2 commit 5cf62dd
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 0 deletions.
10 changes: 10 additions & 0 deletions deep-js-stacks/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# deep-js-stacks

This test case exists to show what happens to JS callstacks at around 256 frames tall.

The known bug: the v8 sampling profiler only emits the _top-most_\* 256 callframes on the stack.

\* Visually in the flamechart it's the bottom-most.

<img width="1209" alt="top of flamechart" src="https://github.com/user-attachments/assets/0da728f5-4dc3-4412-97c0-4f197a40f37b" />
<img width="1124" alt="bottom of flamechart" src="https://github.com/user-attachments/assets/e62dbf02-dccc-418a-9fbe-488213eb2fb5" />
46 changes: 46 additions & 0 deletions deep-js-stacks/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// The problem manifests at a stack depth of 256, but startDemo and stall count as well.
const lowBound = 248;
const highBound = 256;
// Additionally, worth pointing out that the named functions (call250, call252, etc) may not appear at exactly that index in the stack. For extra debugging fun. :)

const output = document.getElementById('output');
let currentMaxDepth = 0;

function stall() {
const start = Date.now();
while (Date.now() - start < 0.5) {}
}

function startDemo() {
const start = performance.now();

for (let maxDepth = lowBound; maxDepth <= highBound; maxDepth++) {
currentMaxDepth = maxDepth;
call0();
}
for (let maxDepth = highBound; maxDepth >= lowBound; maxDepth--) {
currentMaxDepth = maxDepth;
call0();
}

const measure = performance.measure(
`stack-calling ${lowBound}-${highBound}`,
{ start, end: performance.now() },
);

output.textContent += `Total execution time: ${measure.duration.toLocaleString()}ms\n`;
}

// Generate all the call functions
for (let i = highBound; i >= 0; i--) {
window[`call${i}`] = new Function(`return function call${i}() {
if (${i} > currentMaxDepth) return;
if (${i} === currentMaxDepth) stall();
else call${i + 1}();
${i % 4 === 0 ? `//# sourceURL=colorcall${i}.js \n` : ''}
}`)();
}

startDemo();
document.querySelector('button').addEventListener('click', startDemo);
11 changes: 11 additions & 0 deletions deep-js-stacks/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>Generate icicles ~256 entries high</title>
</head>
<body>
<button>Start Demo</button>
<pre id="output"></pre>
<script src="app.js"></script>
</body>
</html>
1 change: 1 addition & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<li><a href="basic-samples-stack/index.html">basic-samples-stack</a></li>
<li><a href="changing-resource-priority/index.html">changing-resource-priority</a></li>
<li><a href="console-timings/index.html">console-timings</a></li>
<li><a href="deep-js-stacks/index.html">deep-js-stacks</a></li>
<li><a href="forced-layout-main-thread/index.html">forced-layout-main-thread</a></li>
<li><a href="gpu-tasks/index.html">gpu-tasks</a></li>
<li><a href="idle-callback-time/index.html">idle-callback-time</a></li>
Expand Down

0 comments on commit 5cf62dd

Please sign in to comment.