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

High Performance JavaScript / jsconfjp2019

High Performance JavaScript / jsconfjp2019

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

Sho Miyamoto

March 14, 2021
Tweet

More Decks by Sho Miyamoto

Other Decks in Technology

Transcript

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

    - 🙆‍♂ Runtime- - 🙆‍♂ Engine- - 🙅‍♂ Product- - Optimization - Profiling Agenda 3
  3. - 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
  4. Runtimes & Engines Engines - V8 - SpiderMonkey - JavaScriptCore

    - Chakra - Hermes - JerryScript - ... Runtimes - Browsers - Node.js - deno - ReactNative - Electron - ... 8
  5. 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
  6. 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
  7. 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
  8. Goal: Make each operation fast 🙅‍♂ Out of scope -

    Options (UV_THREADPOOL_SIZE, ...) Node.js Server-side runtime 15
  9. I/O I/O (Network, FileSystem, …) operation tends to be bottlenecks

    for web applications. Let’s focus on this here. 17
  10. 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
  11. Basically, just a while loop while (true) { // various

    operations if (isEnd) break; } What’s EventLoop 20
  12. 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
  13. 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
  14. 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
  15. 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
  16. 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
  17. - 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
  18. Imagine you read a large file and compress it dynamically

    and serve it. Without Stream Source: File Binary/String serve transform 27
  19. 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
  20. 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
  21. 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
  22. Goal: make page loading and navigation fast 🙅‍♂ Out of

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

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

    execution https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency /javascript-startup-optimization 34
  25. - Chrome: a product - Chromium: a project & its

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

    Dynamic imports, Code Splitting - Differential Loading ESModules on browsers 39
  29. <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
  30. - 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
  31. 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
  32. V8 Goal: Reduce the overhead of each process across the

    board 🙅‍♂ Out of scope - Hacks (...) Engine 44
  33. - 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
  34. 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
  35. 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
  36. Caching for property access & method calls based on HiddenClass

    Hint: Always keep objects’ shape and types as possible V8: Inline Cache 51
  37. - 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