Who I am
Sho Miyamoto (@shqld)
2
Agenda
Goal: Wrap-up JavaScript performance aspects explaining platform-specific things.
3
Introduction
4
JavaScript Layers
Product�(application, library, ...)
Runtime�(Node.js, browsers, ...)
Engine�(V8, SpiderMonkey, …)
5
JavaScript Implementations
(*) : Embedded engine
6
Runtime &
Engine
7
Runtimes & Engines
Engines
Runtimes
8
Runtime vs. Engine
Engine
Runtime
9
Levels of optimization
10
Levels of optimization
Product-level
Runtime-level
Engine-level
Be aware of the level of what you are doing for performance
11
Levels of optimization (2)
Impacts:
Product >>>>>> Runtime >>>>>> Engine
12
Micro-optimization
“Don’t focus on manual micro-optimizations,
instead write readable code
and let JS engines do their job!”
JavaScript is fast enough
13
Optimization
14
Server-side runtime
Node.js
Goal: Make each operation fast
🙅♂️ Out of scope
15
Server-side runtimes
16
I/O
I/O (Network, FileSystem, …) operation tends to be bottlenecks for web applications.
Let’s focus on this here.
17
Node.js
18
Node.js: Async Non-blocking I/O
Powered by libuv (which is tokio in deno)
19
What’s EventLoop
Basically, just a while loop
while (true) {
// various operations
if (isEnd) break;
}
20
EventLoop Flow
(Node.js)
┌───────────────────────────┐
┌─>│ timers │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ pending callbacks │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ idle, prepare │
│ └─────────────┬─────────────┘ ┌───────────────┐
│ ┌─────────────┴─────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └─────────────┬─────────────┘ │ data, etc. │
│ ┌─────────────┴─────────────┐ └───────────────┘
│ │ check │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
└──┤ close callbacks │
└───────────────────────────┘
21
setTimeout
I/O operations
Sync I/O operation blocking
Sync (blocking) operations: can block other sync/async operations for a long time i.e. can pause eventloop
This could be matter (typically on servers)
22
Avoid sync operations
Hint: avoid sync I/O operations
But… still sync long-tasks (not I/O) are inevitable somewhere.
23
EventLoop Flow (2)
┌───────────────────────────┐
┌─>│ timers │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ pending callbacks │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ idle, prepare │
│ └─────────────┬─────────────┘ ┌───────────────┐
│ ┌─────────────┴─────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └─────────────┬─────────────┘ │ data, etc. │
│ ┌─────────────┴─────────────┐ └───────────────┘
│ │ check │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
└──┤ close callbacks │
└───────────────────────────┘
24
LongTask!!�
Pausing loop...
Scheduling sync operations
See a sample: Async handling for sync operations on server-side, leveraging EventLoop (Sho Miyamoto)
Hint: schedule sync operations with timers to avoid blocking
25
Node.js: Stream API
e.g.
26
Without Stream
Imagine you read a large file and compress it dynamically and serve it.
27
Source: File
Binary/String
serve
transform
Without Stream
http
.createServer((req, res) => {
res.setHeader('Content-Type', 'text/plain')
res.setHeader('Content-Encoding', 'gzip')
fs.readFile('hello.txt', (err, file) => {
zlib.gzip(file, (err, data) => {
res.end(data, () => {
console.log('done')
})
})
})
})
.listen(3000)
28
With Stream
Imagine you read a large file and compress it dynamically and serve it.
Note that reading, transforming and serving run at the same time.
29
Source: File
Buffer: Stream
serve
transform
read
With Stream
http
.createServer((req, res) => {
res.setHeader('Content-Type', 'text/plain')
res.setHeader('Content-Encoding', 'gzip')
fs.createReadStream('hello.txt')
.pipe(zlib.createGzip())
.pipe(res)
.addListener('close', () => {
console.log('done')
})
})
.listen(3000)
30
Node.js: Stream API
ReactDOMServer.renderToNodeStream(element).pipe(res)
Hint: Use Stream for memory-intensive operations or sending large data where possible
31
Client-side runtime
Chrome
Goal: make page loading and navigation fast
🙅♂️ Out of scope
32
Client-side runtimes
33
Parse & Compile
Parsing & compiling are heaviest in JavaScript execution
34
Parse & Compile
35
Chrome
36
Code Caching
Before execution, Chrome(V8) parses and compiles scripts, which can take long time
By Code Caching, parsing and compiling can be skipped
37
Code Caching
Caching scripts to skip parsing & compiling
Hint: Cache scripts in CacheStorage via ServiceWorker as possible
38
ESModules on browsers
39
Preloading scripts
<link rel="preload" href="index.mjs">
<link rel="modulepreload" href="index.mjs">
40
Preloading scripts
<link rel="modulepreload" href="index.mjs">
`modulepreload` is optimized for modules
modulepreload’ed scripts are parsed and compiled as soon as fetching is done, like Code Caching
Hint: when you use `type modules`, adding `link modulepreload` would have effect
41
idling (idlization*)
requestIdleCallback, cancelIdleCallback
cf: https://github.com/GoogleChromeLabs/idlize
Hint: Avoid long-tasks blocking main thread for better FID & TTI unless it’s needed
42
Lazy parsing
It’s not always every single part of scripts are parsed and evaluated.
For some performance purposes, Chrome parses only immediately needed parts (Lazy parsing)
43
Engine
V8
Goal: Reduce the overhead of each process across the board
🙅♂️ Out of scope
44
Engines
(*) : Embedded Engine
45
V8
46
V8: Pipeline
Many engines have similar systems.
47
V8: Internal Representation (examples)
Type: (See v8/src/builtins/base.tq for details)
Values:
48
V8: Hidden Class
Shared maps for objects’ shapes
https://richardartoul.github.io/jekyll/update/2015/04/26/hidden-classes.html
49
const obj = {}
obj.x = 1; obj.y = 2;
V8: Hidden Class
Not only Shape but also Representation matter
50
V8: Inline Cache
Caching for property access & method calls based on HiddenClass
Hint: Always keep objects’ shape and types as possible
51
V8: Element types
52
V8: Element kinds
53
Takeaways
- Avoid sync I/O operations�- Schedule sync operations with timers to avoid blocking�- Use Stream for memory-intensive operations or sending large data where possible�- Cache scripts in CacheStorage via ServiceWorker as possible�- When you use `type modules`, adding `link modulepreload` would have effect�- Avoid long-tasks blocking main thread unless it’s needed�- Always keep objects’ shape and types as possible
54
Thank you.
55