Very few developers have the need to write super optimized JavaScript code. In application development, we tend to favor readability over optimization. But that’s not the case with frameworks. Developers who use frameworks expect them to run as fast as possible. In fact, speed is often a defining characteristic when choosing a framework. There are techniques that make code run faster. You’ve probably heard about linked lists, monomorphism, and bitmasks, right? Maybe you've even used some. Well, you can find all these and a bunch of other interesting approaches in the sources of most popular JS frameworks.
Over the past year, I’ve seen a lot while reverse-engineering Angular and React. In this talk, I want to share my findings with you. Some of you may end up applying them at work. And others, who knows, may even end up writing the next big framework.
Student Profile Sample - We help schools to connect the data they have, with ...
JS Fest 2019. Max Koretskiy. A sneak peek into super optimized code in JS frameworks
1. Why Angular and React are so fast?
Max Koretskyi aka Wizard
Developer Advocate
2. 2015 20162014 2017 2018
A failed startup
that made me
learn to program
and gave broad
programming
experience
SO activity that
build my
knowledge base
and created a
foundation for
blogging
Angular In Depth
that helped me
build strong
community
and establish
reputation as an
Angular expert
Public speaking
that helped me
meet awesome
people and
become GDE and
MVP
Developer Advocate
job that allows me to
to develop
programming and
marketing skills at
the same time
where 100-hour work weeks got me in 5 years
4. Monomorphism Bit fields & Bit masks Bloom filters
Benedikt Meurer, V8 optimizations lead engineer
Alex Rickabught, Angular core team
Dan Ambramov, React core team
Kudos to
Optimization techniques in Angular and React
5. We use one type for all View nodes so that property
access in loops stay monomorphic!
Misko Hevery, technical lead of Angular
Angular sources comments
6. Fiber node … shares the same hidden class.
Never add fields outside of construction in `ReactFiber`
Contributing To React Fiber guidelines
Sebastian Markbåge, technical lead of React
7. • What is hidden class and why is it shared by fiber and view nodes?
Questions we need to answer
• What is monomophic property access and why is it important?
• What is a fiber node in React & a view node in Angular?
8. Representing a template in Angular
@Component({
template: `
<h1>The title is: {{title}}</h1>
<h2>My hero is: {{hero}}</h2>
`
})
export class AppComponent {
title = 'Tour of Heroes';
hero = 'Windstorm';
}
Type: h1
Type: h2
View Nodes
bindings: {
text: "Tour of Heroes"
}
bindings: {
text: " 'Windstorm"
}
9. Representing a template in React
class App extends Component {
state = {
title: 'Tour of Heroes',
hero: 'Windstorm'
};
render() {
return (
[
<h1>The title is: {this.state.title}</h1>,
<h2>My hero is: {this.state.hero}</h2>
]
)
}
}
Type: h1
Type: h2
props: {
children: "Tour of Heroes"
}
props: {
children: " Windstorm "
}
Fiber Nodes
10. Fiber and View nodes are used a lot
when processing changes
properties could easily be accessed
over 10 000* times
function updateNode(node, …) {
let value = node.property;
}
*100 components x 10 elements x 10 function calls
11. Locating value of an
object's property
in memory
is a complicated process
12. let object = { x: 5, y: 6 };
JSObject
5
6
Property information
Offset: 0
[[Writable]]: true
[[Enumerable]]: true
[[Configurable]]: true
Shape
'x'
'y'
Property information
Offset: 1
[[Writable]]: true
[[Enumerable]]: true
[[Configurable]]: true
Shapes aka Hidden Class (Maps in V8)
20. Polymorphic property access
getX(a);
getX(b);
getX(c);
getX(D);
function getX(o) { return o.x; }
let a = {x: 5, y: 6};
let b = {y: 7, x: 8}; // different order
let b = {y: 7, x: 8, z: 5}; // new properties
let d = Object.create({}, {}; // different prototype
a function only saw up to 4 different types of a shape
26. function beginWork(fiberNode, ...) {
...
switch (fiberNode.tag) {
case FunctionalComponent: {...}
case ClassComponent: {...}
case HostComponent:
return updateHostComponent(fiberNode, ...);
case ...
}
}
Branching by node type in React
30. Effects in React Fiber architecture
• Render phase (build a list of effects)
• Process changes from setState
• Update props on child elements
• Call lifecycle methods (shouldComponentUpdate etc.)
The result of the phase is a tree of fiber nodes marked with side-effects.
• Commit phase (apply effects)
• Update DOM
• Call lifecycle methods (componentDidUpdate etc.)
• Update refs
32. let effectTag = 0b00000000;
0 0 0 0 0 0 0 0
effectTag = effectTag | effectTags.Update;
0 0 0 0 0 1 0 0
Write with bitwise OR
Define bitfield
isUpdateEffectSet = !! (effectTag & effectTags.Update);
Check with bitwise AND
33. Branching by effect in React
function updateHostEffects(fiberNode) {
...
const effectTag = fiberNode.effectTag;
if (effectTag & effectTags.Placement) { ... }
if (effectTag & effectTags.PlacementAndUpdate) { ... }
if (effectTag & effectTags.Update) { ... }
...
}
34. Encoding objects in bit fields
let user = {
first: true,
group: 20,
sortKey: 347843
};
allocations in memory for:
• a standard JS object
• a shape with keys metadata
• 3 values stored in the keys
36. Encoding objects in bit fields
Field Restrictions on values Bits required
sortKey less than 1M 20 (220 > 1M)
group less than 50 6 (26 = 64)
first boolean 1
00000 0 000000 0000000000000000000
sortKeygroupfirst
32 bits
37. Encoding objects in bit fields
let user = 0x 05454ec3;
let user = 0b 00000 1 010100 001010100111011000011;
let user = { first: true, group: 20, sortKey: 347843 };
Field Decimal Binary
sortKey 347843 001010100111011000011
group 20 010100
first true 1
39. • millions of allocations for
objects and values
1 million of objects require
VS
• one typed array for one million
32-integer values
Regular JS object Encoded in a bitfield
• a ton of GC (garbage collection)
cleanup after each iteration
• much larger and potentially
fragmented memory usage
• almost zero garbage collection
• smaller and contiguous memory
usage
40. Bloom filters
is element in the set?
DEFINITELY NO
100% probability
MAYBE
varying probability
data structure that answers the question
41. 0 0 0 0 1 0
Is element in a set? Is bits [n1,n2…n] set?
Each element is encoded in a bit field
let value = "Jonh";
calculateBitNumber(value); // 2
42. John (2)
0 0 0 0 0 0 0 0
Anna (1)Tom (4)
[ "John", "Anna", "Tom" ]
let calculateBitValue = (value) => value.charCodeAt(0) % 8
111
58. Follow me to learn fundamentals
maxkoretskyi
maxkoretskyi
maxkoretskyi.com
Hinweis der Redaktion
The problem is collisions. 2 letters.
Hash functions are usually used to produce some number. But it can be anything. Even a simple counter. And that's what Angular does.
The problem is collisions. 2 letters.
Hash functions are usually used to produce some number. But it can be anything. Even a simple counter. And that's what Angular does.
If you have any questions, I'll be sitting in the experts room tomorrow at 3 pm.
Or you can catch me in the hallways, I'll be wearing this T-shirt so you can recognize me.
For more in-depth information follow me on Twitter. I promise I won’t waste your time.
I’ll write a follow-up article on change detection. So stay tuned and expect some tweets about it quite soon.