Upgrade to Pro — share decks privately, control downloads, hide ads and more …

High Performance JavaScript / jsconfjp2019

High Performance JavaScript / jsconfjp2019

https://jsconf.jp/2019/ でお話させていただいた発表の資料です。

96030bc60c6fc8a91f25ccda9b413b24?s=128

Sho Miyamoto

March 14, 2021
Tweet

Transcript

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

  2. 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
  3. Goal: Wrap-up JavaScript performance aspects explaining platform-specific things. - Layers

    - 🙆‍♂ Runtime- - 🙆‍♂ Engine- - 🙅‍♂ Product- - Optimization - Profiling Agenda 3
  4. Introduction 4

  5. JavaScript Layers Product (application, library, ...) Runtime (Node.js, browsers, ...)

    Engine (V8, SpiderMonkey, …) 5
  6. - 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
  7. Runtime & Engine 7

  8. Runtimes & Engines Engines - V8 - SpiderMonkey - JavaScriptCore

    - Chakra - Hermes - JerryScript - ... Runtimes - Browsers - Node.js - deno - ReactNative - Electron - ... 8
  9. 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
  10. Levels of optimization 10

  11. 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
  12. Impacts: Product >>>>>> Runtime >>>>>> Engine Levels of optimization (2)

    12
  13. 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
  14. Optimization 14

  15. Goal: Make each operation fast 🙅‍♂ Out of scope -

    Options (UV_THREADPOOL_SIZE, ...) Node.js Server-side runtime 15
  16. - Server-side - Node.js, deno - QuickJS - ... Server-side

    runtimes 16
  17. I/O I/O (Network, FileSystem, …) operation tends to be bottlenecks

    for web applications. Let’s focus on this here. 17
  18. - Single-threaded - Async non-blocking IO - Embedding V8 as

    the engine Node.js 18
  19. 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
  20. Basically, just a while loop while (true) { // various

    operations if (isEnd) break; } What’s EventLoop 20
  21. 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
  22. 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
  23. 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
  24. 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
  25. 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
  26. - 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
  27. Imagine you read a large file and compress it dynamically

    and serve it. Without Stream Source: File Binary/String serve transform 27
  28. 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
  29. 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
  30. 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
  31. 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
  32. Goal: make page loading and navigation fast 🙅‍♂ Out of

    scope - Rendering & Parsing HTML - General web performance Chrome Client-side runtime 32
  33. - Client-side: - Browsers - Chrome, FireFox, Safari, IE, Flow,

    ... - Electron - React-Native - iOS, Android Client-side runtimes 33
  34. Parse & Compile Parsing & compiling are heaviest in JavaScript

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

  36. - Chrome: a product - Chromium: a project & its

    software - Rendering Engine: blink - JavaScript Engine: V8 - Also has its own EventLoop system Chrome 36
  37. 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
  38. 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
  39. - Modern browsers now support ESModules natively - import/export -

    Dynamic imports, Code Splitting - Differential Loading ESModules on browsers 39
  40. <link rel="preload" href="index.mjs"> <link rel="modulepreload" href="index.mjs"> Preloading scripts https://medium.com/reloading/preload-prefetch-and-priorities-in-chrome-776165961bbf40

  41. <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 Preloading scripts 41
  42. - 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
  43. 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
  44. V8 Goal: Reduce the overhead of each process across the

    board 🙅‍♂ Out of scope - Hacks (...) Engine 44
  45. - 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
  46. - The most used JavaScript (& Wasm) engine - typically

    Chrome and Node.js V8 46
  47. V8: Pipeline Many engines have similar systems. 47

  48. 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
  49. 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
  50. V8: Hidden Class Not only Shape but also Representation matter

    https://v8.dev/blog/react-cliff 50
  51. Caching for property access & method calls based on HiddenClass

    Hint: Always keep objects’ shape and types as possible V8: Inline Cache 51
  52. V8: Element types - In-object properties - Fast properties -

    Slow properties 52
  53. V8: Element kinds - Packed/Holey... 53

  54. - 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
  55. Thank you. 55