The document discusses optimization techniques used in Angular and React to improve performance. It explains that both frameworks use monomorphic property access by enforcing that all view/fiber nodes share the same "hidden class" or shape. This avoids expensive property lookups and allows properties to be accessed over 10,000 times faster. The document also discusses how bit fields and bit masks are used to efficiently represent side effects and other metadata in React fiber nodes. Bloom filters are mentioned as something Angular uses in its dependency injection system.
Max Koretskyi "Why are Angular and React so fast?"
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
My 5 years in IT
4. Benedikt Meurer, V8 optimizations lead engineer
Alex Rickabught, Angular core team
Dan Ambramov, React core team
Monomorphism Bit fields & Bit masks Bloom filters
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 a fiber node in React & a view node in Angular?
• What is hidden class and why is it shared by fiber and view nodes?
• What is monomophic property access and why is it important?
Questions we need to answer
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)
17. 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
21. type Element {
fieldA;
fieldB;
}
type Text {
fieldC;
field;
}
type Component {
fieldE;
fieldF;
}
Property access is not monormophic
type Node {
tag: nodeType;
fieldA | null;
fieldB | null;
fieldC | null;
fieldD | null;
fieldE | null;
fieldF | null;
}
Property access is monormophic
22. function beginWork(fiberNode, ...) {
...
switch (fiberNode.tag) {
case FunctionalComponent: {...}
case ClassComponent: {...}
case HostComponent:
return updateHostComponent(fiberNode, ...);
case ...
Branching by node type in React
24. Bit field is just an array of bits
let effectTag = 0b01010001; // 81
0 1 0 1 0 0 0 1
25. Side effects in React fiber are
encoded using bit fields
0 1 0 1 0 1 0 0
Placement
Update
26. Effects in React Fiber architecture
• Render phase
• 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
28. let effectTag = 0b00000000;
0 0 0 0 0 0 0 0
effectTag = effectTag | effectTags.Update;
0 0 0 0 0 1 0 0
isUpdateEffectSet = !! (effectTag & effectTags.Update);
Write with bitwise OR
Define bitfield
Check with bitwise AND
29. Branching by effect in React
function updateHostEffects(fiberNode) {
...
const effectTag = fiberNode.effectTag;
if (effectTag & effectTags.Placement) { ... }
if (effectTag & effectTags.PlacementAndUpdate) { ... }
if (effectTag & effectTags.Update) { ... }
...
}
30. Encoding objects in bit fields
let user = {
first: true,
group: 20,
sortKey: 347843
};
allocations in memory for:
• a standard JS object
• 3 strings for the keys
• 3 values stored in the keys
31. 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
32. 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
34. • millions of allocations of keys,
objects and values
• a ton of GC (garbage collection)
cleanup after each iteration
• larger and fragmented memory
usage
1 million of objects require
VS
• one typed array for one million
32-integer values
• alsmot zero garbage collection
• smaller and contiguous memory
usage.
Regular JS object Encoded in a bitfield
35. Bloom filters
is element in the set?
DEFINITELY NO MAYBE
100% probability varying probability
data structure that answers the question
36. 0 1 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";
calculateBit1Number(value); // 2
calculateBit2Number(value); // 7
37. John - 2
0 0 0 0 0 0 0 1
0 0 0 0 1 0 0 0
0 0 0 0 0 0 1 0
Using bloom filter for string values
Anna – 1
Tom - 4
[ "John", "Anna", "Tom" ]
let calculateBit1Value = (value) => value.charCodeAt(0) % 8
48. Follow me to learn fundamentals
@maxim_koretskyi
maxkoretskyi
maxkoretskyi.com
Hinweis der Redaktion
История имени
Идея доклада
- Первый раз выступление на русском языке и необходимость улыбаться
Использует много техник, мономорфизм самый сложный для понимания
Как я натолкнулся на гайдлайны для контрибьюторов в React 16 и новее.
Кто понимает ее?
- updateNode эквиваленты в Angular и React во время процессинга изменений, механизм CD вкратце
- Много логических шагов, инструкций процессору в разы больше
Каждый объект в JS внутри виртуалной машины представлен структурой данных, которая называет Shape/Hidden Class/Map.
Shape определяет структуру объекта JS и расположение значений полей в памяти.
В независимости от количества объектов, мы храним информацию обо всех объектах один раз.
Adding/removing properties changes the Shape and creates a transition chain.
Objects with different prototypes have different shapes
Оптимизация с помощью Inline Caching.
Виртуальные машины используют его что-то бы запомнить и закешировать информацию о том, как найти значение какого-либо поля объекта.
И понятия мономорфизма и hidden classes связано именно с этими техниками.
They keep the same shape for a View node (Angular) and a Fiber node (React) in all function calls.
Effects describe the work that needs to be done during the following commit phase.
To create this object, a JS engine has to allocate a standard JS object, allocate 3 strings for the keys and store the 3 values.
Given that I know as the developer of this app that the sortKey field is never larger than, say, 1 million and the group is at most 50 and the first is a boolean, which is 1 or 0.
I can use this info to put all this info into a single 32-bit integer.
sortKey can fit inside 20 bits (220 > 1M), group can fit into 6 bits (26 = 64) and first can fit into a single bit, total 27 bits.
To create this value, the JS engine has to … allocate nothing (in optimised passes), it's just an integer that fits comfortably in a register inside the CPU, we even have 5 bits to spare! ;) I can now use the bit masking and insertion techniques you mentioned to read/write data in these values (did I mention that bit ops are single instructions on the CPU?)
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.
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.
Dependency Injection (DI), is an important application design pattern. We use in ag-Grid. It significantly simplifies architecture.
Angular has its own DI framework, which is typically used in the design of Angular applications to increase their efficiency and modularity.
Key part of it is an injector. It's basically a container that knows how to instantiate a particular service and which dependencies to pass to the service.
The Angular dependency injection system is hierarchical.
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.
I hope that the knowledge we covered today has awaken your curiosity and thirst for knowledge.
Never stop learning and you'll be constantly reaching new heights. I want you all guys to become extraordinary engineers.
Thanks for your attention and good luck!