Slide 1

Slide 1 text

Wrap up: High Performance JavaScript Sho Miyamoto (@shqld)

Slide 2

Slide 2 text

Sho Miyamoto (@shqld) - Web Developer - At a certain NewsPaper Company - {Edge, Server, Client}-side Engineer - JavaScript Enthusiast - (Web)Performance Enthusiast - CDN, Node, Loading, Rendering, ... Who I am 2

Slide 3

Slide 3 text

Goal: Wrap-up JavaScript performance aspects explaining platform-specific things. - Layers - πŸ™†β€β™‚ Runtime- - πŸ™†β€β™‚ Engine- - πŸ™…β€β™‚ Product- - Optimization - Profiling Agenda 3

Slide 4

Slide 4 text

Introduction 4

Slide 5

Slide 5 text

JavaScript Layers Product (application, library, ...) Runtime (Node.js, browsers, ...) Engine (V8, SpiderMonkey, …) 5

Slide 6

Slide 6 text

- Server-side: - Node.js(V8), deno(V8) - QuickJS, ... - Client-side: - Browsers - Chrome(V8), FireFox(SpiderMonkey), Safari(JavaScriptCore), IE(Chakra), Flow(SpiderMonkey), ... - ReactNative - iOS(JavaScriptCore), Android(V8, hermes) - Electron(V8), ... (*) : Embedded engine JavaScript Implementations 6

Slide 7

Slide 7 text

Runtime & Engine 7

Slide 8

Slide 8 text

Runtimes & Engines Engines - V8 - SpiderMonkey - JavaScriptCore - Chakra - Hermes - JerryScript - ... Runtimes - Browsers - Node.js - deno - ReactNative - Electron - ... 8

Slide 9

Slide 9 text

Engine - parses, compiles, executes - JIT, VM, ... Runtime - has an embedded engine inside - runtime platform for JS - has each own Global Object (window, process, …) - can override own EventLoop - ... Runtime vs. Engine 9

Slide 10

Slide 10 text

Levels of optimization 10

Slide 11

Slide 11 text

Levels of optimization Product-level - memoization, cache, algorithm, … Runtime-level - async operation, code fetching, … Engine-level - IC, SMI/HeapNumber, Packed/Holey Array, … Be aware of the level of what you are doing for performance 11

Slide 12

Slide 12 text

Impacts: Product >>>>>> Runtime >>>>>> Engine Levels of optimization (2) 12

Slide 13

Slide 13 text

Micro-optimization β€œDon’t focus on manual micro-optimizations, instead write readable code and let JS engines do their job!” https://twitter.com/mathias/status/1171664472035004423 JavaScript is fast enough 13

Slide 14

Slide 14 text

Optimization 14

Slide 15

Slide 15 text

Goal: Make each operation fast πŸ™…β€β™‚ Out of scope - Options (UV_THREADPOOL_SIZE, ...) Node.js Server-side runtime 15

Slide 16

Slide 16 text

- Server-side - Node.js, deno - QuickJS - ... Server-side runtimes 16

Slide 17

Slide 17 text

I/O I/O (Network, FileSystem, …) operation tends to be bottlenecks for web applications. Let’s focus on this here. 17

Slide 18

Slide 18 text

- Single-threaded - Async non-blocking IO - Embedding V8 as the engine Node.js 18

Slide 19

Slide 19 text

Powered by libuv (which is tokio in deno) - Every I/O operation is asynchronous - Network, Socket, FileSystem, … - Also able to opt-out to sync ones - fs.readFileSync, … Node.js: Async Non-blocking I/O 19

Slide 20

Slide 20 text

Basically, just a while loop while (true) { // various operations if (isEnd) break; } What’s EventLoop 20

Slide 21

Slide 21 text

EventLoop Flow (Node.js) β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€>β”‚ timers β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ pending callbacks β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ idle, prepare β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ incoming: β”‚ β”‚ β”‚ poll β”‚<────── connections, β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ data, etc. β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ check β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” └─── close callbacks β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ https://nodejs.org/de/docs/guides/event-loop-timers-and-nexttick/ setTimeout I/O operations 21

Slide 22

Slide 22 text

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) http://www.whatislamp.com/2017/04/nodejs-blockingnon-blocking-and-event.html 22

Slide 23

Slide 23 text

Avoid sync operations Hint: avoid sync I/O operations - many standard module methods named like -Sync - readFile >>> readFileSync But… still sync long-tasks (not I/O) are inevitable somewhere. 23

Slide 24

Slide 24 text

EventLoop Flow (2) β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€>β”‚ timers β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ pending callbacks β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ idle, prepare β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ incoming: β”‚ β”‚ β”‚ poll β”‚<────── connections, β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ data, etc. β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ check β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” └─── close callbacks β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ https://nodejs.org/de/docs/guides/event-loop-timers-and-nexttick/ LongTask!! Pausing loop... 24

Slide 25

Slide 25 text

Scheduling sync operations 25 See a sample: Async handling for sync operations on server-side, leveraging EventLoop (Sho Miyamoto) Hint: schedule sync operations with timers to avoid blocking

Slide 26

Slide 26 text

- Effective API for streaming data - Operations look like piping stdin between commands e.g. - http.{ClientRequest, IncomingMessage} - fs.{ReadStream, WriteStream} - process.{stdin, stdout, stderr}, ... Node.js: Stream API 26

Slide 27

Slide 27 text

Imagine you read a large file and compress it dynamically and serve it. Without Stream Source: File Binary/String serve transform 27

Slide 28

Slide 28 text

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) - nested - less memory-efficient Without Stream 28

Slide 29

Slide 29 text

Imagine you read a large file and compress it dynamically and serve it. Note that reading, transforming and serving run at the same time. With Stream Source: File Buffer: Stream serve transform read 29

Slide 30

Slide 30 text

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) - no nest - memory-efficient - progressively rendering on browser - declarative With Stream 30

Slide 31

Slide 31 text

React.renderToNodeStream ReactDOMServer .renderToNodeStream(element).pipe(res) Hint: Use Stream for memory-intensive operations or sending large data where possible Node.js: Stream API 31

Slide 32

Slide 32 text

Goal: make page loading and navigation fast πŸ™…β€β™‚ Out of scope - Rendering & Parsing HTML - General web performance Chrome Client-side runtime 32

Slide 33

Slide 33 text

- Client-side: - Browsers - Chrome, FireFox, Safari, IE, Flow, ... - Electron - React-Native - iOS, Android Client-side runtimes 33

Slide 34

Slide 34 text

Parse & Compile Parsing & compiling are heaviest in JavaScript execution https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency /javascript-startup-optimization 34

Slide 35

Slide 35 text

Parse & Compile https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/javascript-startup-optimization 35

Slide 36

Slide 36 text

- Chrome: a product - Chromium: a project & its software - Rendering Engine: blink - JavaScript Engine: V8 - Also has its own EventLoop system Chrome 36

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

Code Caching Caching scripts to skip parsing & compiling - Scripts that are executed twice in 72 hours - Scripts of Service Worker that are executed twice in 72 hours - Scripts stored in Cache Storage via Service Worker in the first execution Hint: Cache scripts in CacheStorage via ServiceWorker as possible 38

Slide 39

Slide 39 text

- Modern browsers now support ESModules natively - import/export - Dynamic imports, Code Splitting - Differential Loading ESModules on browsers 39

Slide 40

Slide 40 text

Preloading scripts https://medium.com/reloading/preload-prefetch-and-priorities-in-chrome-776165961bbf40

Slide 41

Slide 41 text

`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 Preloading scripts 41

Slide 42

Slide 42 text

- Idle Until Urgent: Scheduling code execution requestIdleCallback, cancelIdleCallback cf: https://github.com/GoogleChromeLabs/idlize Hint: Avoid long-tasks blocking main thread for better FID & TTI unless it’s needed idling (idlization*) https://philipwalton.com/articles/idle-until-urgent/ 42

Slide 43

Slide 43 text

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) - iife, pife Lazy parsing 43

Slide 44

Slide 44 text

V8 Goal: Reduce the overhead of each process across the board πŸ™…β€β™‚ Out of scope - Hacks (...) Engine 44

Slide 45

Slide 45 text

- Server-side: - Node.js(V8), deno(V8) - QuickJS, ... - Client-side: - Browsers - Chrome(V8), FireFox(SpiderMonkey), Safari(JavaScriptCore), ... - ReactNative - iOS(JavaScriptCore), Android(V8, hermes) - Electron(V8), ... (*) : Embedded Engine Engines 45

Slide 46

Slide 46 text

- The most used JavaScript (& Wasm) engine - typically Chrome and Node.js V8 46

Slide 47

Slide 47 text

V8: Pipeline Many engines have similar systems. 47

Slide 48

Slide 48 text

Type: (See v8/src/builtins/base.tq for details) - Object => Smi | HeapObject - Number => Smi | HeapNumber Values: - Smi: 31-bit signed integer - HeapNumber: other numbers V8: Internal Representation (examples) https://v8.dev/blog/react-cliff 48

Slide 49

Slide 49 text

V8: Hidden Class const obj = {} obj.x = 1; obj.y = 2; Shared maps for objects’ shapes https://richardartoul.github.io/jekyll/u pdate/2015/04/26/hidden-classes.ht ml 49

Slide 50

Slide 50 text

V8: Hidden Class Not only Shape but also Representation matter https://v8.dev/blog/react-cliff 50

Slide 51

Slide 51 text

Caching for property access & method calls based on HiddenClass Hint: Always keep objects’ shape and types as possible V8: Inline Cache 51

Slide 52

Slide 52 text

V8: Element types - In-object properties - Fast properties - Slow properties 52

Slide 53

Slide 53 text

V8: Element kinds - Packed/Holey... 53

Slide 54

Slide 54 text

- 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 Takeaways 54

Slide 55

Slide 55 text

Thank you. 55