Skip to content

Instantly share code, notes, and snippets.

@zz85
Created September 28, 2017 11:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zz85/5ec7de7c86ee59a47102cdf64fa53742 to your computer and use it in GitHub Desktop.
Save zz85/5ec7de7c86ee59a47102cdf64fa53742 to your computer and use it in GitHub Desktop.
Flamegraphs + Donuts Sunburst Charts
<html>
<body>
<script>
class File {
constructor(name, size) {
this.name = name;
this.size = size;
}
}
class Dir {
constructor(name) {
this.name = name;
this.children = [];
this.size = 0;
}
}
random_names = [... new Set(`
A virtual DOM tree is a Javascript data structure that describes a DOM tree. It consists of nested virtual DOM nodes, also known as vnodes.
The first time a virtual DOM tree is rendered, it is used as a blueprint to create a DOM tree that matches its structure.
Typically, virtual DOM trees are then recreated every render cycle, which normally occurs in response to event handlers or to data changes. Mithril diffs a vnode tree against its previous version and only modifies DOM elements in spots where there are changes.
It may seem wasteful to recreate vnodes so frequently, but as it turns out, modern Javascript engines can create hundreds of thousands of objects in less than a millisecond. On the other hand, modifying the DOM is several orders of magnitude more expensive than creating vnodes.
For that reason, Mithril uses a sophisticated and highly optimized virtual DOM diffing algorithm to minimize the amount of DOM updates. Mithril also generates carefully crafted vnode data structures that are compiled by Javascript engines for near-native data structure access performance. In addition, Mithril aggressively optimizes the function that creates vnodes as well.
The reason Mithril goes to such great lengths to support a rendering model that recreates the entire virtual DOM tree on every render is to provide retained mode rendering, a style of rendering that makes it drastically easier to manage UI complexity.
To illustrate why retained mode is so important, consider the DOM API and HTML. The DOM API is an immediate mode) rendering system and requires writing out exact instructions to assemble a DOM tree procedurally. The imperative nature of the DOM API means you have many opportunities to micro-optimize your code, but it also means that you have more chances of introducing bugs and more chances to make code harder to understand.
In contrast, HTML is a retained mode rendering system. With HTML, you can write a DOM tree in a far more natural and readable way, without worrying about forgetting to append a child to a parent, running into stack overflows when rendering extremely deep trees, etc.
Virtual DOM goes one step further than HTML by allowing you to write dynamic DOM trees without having to manually write multiple sets of DOM API calls to efficiently synchronize the UI to arbitrary data changes.
`.split(/\W+/))].filter(w => w.length > 4)
function random_tree_structure(node, level) {
if (level > 9) return;
// node must be a directory
const count = (Math.random() * (11 - level) | 0) + 1;
for (let i = 0; i < count; i++) {
let child
if (Math.random() < 0.6) {
// file
child = new File(random(random_names), Math.random() * 10000 | 0)
}
else {
child = new Dir(random(random_names))
random_tree_structure(child, level + 1)
}
node.children.push(child)
}
}
function random(v) {
return v[Math.random() * v.length | 0]
}
function sizes(node) {
if (node instanceof File) {
return node.size;
}
if (node instanceof Dir) {
let sum = 0;
node.children.forEach(node => {
sum += sizes(node)
});
node.size = sum;
return sum;
}
}
// breath first
function walk(node, func, depth=0, sum=0) {
const enter = func(node, depth, sum)
if (node instanceof Dir) {
let tmp = sum;
node.children.forEach(n => {
tmp = walk(n, func, depth + 1, tmp)
});
}
return enter;
}
function walkin(node, func, depth=0) {
if (node instanceof Dir) {
node.children.forEach(n => walk(n, func, depth + 1));
}
func(node, depth)
}
root = new Dir('/')
// root.children.push(new File('a', 10))
// root.children.push(new File('a', 20))
// root.children.push(new File('a', 30))
// root.children.push(new File('a', 40))
// root.children.push(new File('a', 50))
random_tree_structure(root, 1)
sizes(root);
walk(root, (node, depth, sum) => {
node.x = sum;
const x = sum + node.size
return x;
})
let count = 0;
walk(root, (node) => {
count++;
})
console.log(count);
canvas = document.createElement('canvas');
document.body.appendChild(canvas);
width = innerWidth - 20;
height = innerHeight;
ctx = canvas.getContext('2d');
ctx.translate(10, 10);
canvas.width = width;
canvas.height = height;
HEIGHT = 20;
offset = 0
last_offset = 0
ctx.strokeStyle = '#999'
ctx.fillStyle = '#eee'
walk(root, (node, depth) => {
const y =
height - (depth + 1) * HEIGHT
// depth * HEIGHT;
ctx.rect(node.x / root.size * width, y, node.size / root.size * width, HEIGHT)
ctx.fill();
ctx.stroke();
})
ctx.translate(width / 2, height / 2);
walk(root, (node, depth) => {
ctx.beginPath();
const start = node.x / root.size * 2 * Math.PI;
const end = (node.x + node.size) / root.size * 2 * Math.PI;
ctx.arc(0, 0, (depth + 2) * HEIGHT, start, end, false);
ctx.arc(0, 0, (depth + 3) * HEIGHT, end, start, true);
// ctx.arc(0, 0, (9 - depth + 1) * HEIGHT, start, end, false);
// ctx.arc(0, 0, (9 - depth + 2) * HEIGHT, end, start, true);
ctx.closePath();
ctx.stroke();
ctx.fill();
})
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment