Slide 1

Slide 1 text

Andrii Chyzh @ Wix.com Node.js Performance [email protected] github.com/andriichyzh

Slide 2

Slide 2 text

Standard Slide ● Software Engineer / Team Leader at Wix.com ● Independent project development consulting ● More than 8 years in professional software development ● Latest 6 years Node.js my main and beloved technology

Slide 3

Slide 3 text

AGENDA 1. Update To The Latest Version of Node.js 2. Set NODE_ENV = production 3. Avoid The Heavy Synchronous Code 4. Speed Up JSON Serialization 5. Write The Asynchronous Code Correctly 6. Use The Streams Correctly 7. Try To Optimize V8 GC 8. Move CPU Bound Tasks Out Of The Node.js Code 9. Try To Use C++ Extensions 10. Use Tools To Locate Performance Issues

Slide 4

Slide 4 text

Update To The Latest Version of Node.js 01

Slide 5

Slide 5 text

THE LATEST VERSION OF NODE.JS Reducing raw server costs by circa 40–50% after upgrading Node.js version from 6 to 8 (experience of Ably Realtime) Profit from update to the next version

Slide 6

Slide 6 text

Google V8 Benchmark: async-heavy code THE LATEST VERSION OF NODE.JS Execution time, ms (lower is better)

Slide 7

Slide 7 text

Google V8 Benchmark: code with Promise.all THE LATEST VERSION OF NODE.JS Execution time, ms (lower is better)

Slide 8

Slide 8 text

Google V8 Benchmark: next versions THE LATEST VERSION OF NODE.JS Execution time, ms (lower is better)

Slide 9

Slide 9 text

Real-world benchmarks of frameworks THE LATEST VERSION OF NODE.JS Throughput, req/sec (higher is better)

Slide 10

Slide 10 text

Performance impact ▪ Upcoming LTS version of Node.js can give a 15-20% of performance boost in real-world scenarios (in compare with the current LTS version). THE LATEST VERSION OF NODE.JS

Slide 11

Slide 11 text

Set NODE_ENV = production 02

Slide 12

Slide 12 text

Setting NODE_ENV to "production" makes ▪ Middleware and other dependencies switch to use efficient code path; ▪ Cache view templates (ie Express); ▪ Cache CSS files generated from CSS extensions (ie Express); ▪ Frameworks generate less verbose error messages. NODE_ENV

Slide 13

Slide 13 text

CPU usage vs number of requests NODE_ENV https://dynatrace.com/news/blog/the-drastic-effects-of-omitting-node-env-in-your-express-js-applications/

Slide 14

Slide 14 text

CPU profiling of Express application NODE_ENV NODE_ENV = development (default) NODE_ENV = production

Slide 15

Slide 15 text

Performance impact ▪ Complex Express application with NODE_ENV = production can be 2-3 times faster than with default NODE_ENV = development NODE_ENV

Slide 16

Slide 16 text

Avoid The Heavy Synchronous Code 03

Slide 17

Slide 17 text

API without limits and pagination AVOID THE SYNCHRONOUS CODE Case Size, KB JSON.stringify, ms JSON.parse, ms Max throughput, RPS 25 items 26 0.231 0.292 3,400 50 items 56 0.339 0.327 2,950 100 items 115 0.727 1.636 610 250 items 264 1.688 2.135 470 500 items 529 3.345 5.548 180 1000 items 1058 9.498 11.255 88 * MacBook Pro, Intel Core i7 2.7 GHz, Node.js 12.11.1

Slide 18

Slide 18 text

Not effective usage of databases or storages ▪ Large arrays of objects for parsing / iteration / transformation (ie queries without limits and projections); ▪ Not using DB features (ie GROUP BY, aggregations) where it can be effective. AVOID THE SYNCHRONOUS CODE

Slide 19

Slide 19 text

Technical debt and people’s mistakes ▪ Using sync methods in web services (ie fs.readFileSync, fs.writeFileSync, child_process.execSync); ▪ Using recursive sync functions; ▪ Wrap heavy sync functions to async/await. AVOID THE SYNCHRONOUS CODE

Slide 20

Slide 20 text

Wrap heavy sync function into async/await (anti-pattern!)

Slide 21

Slide 21 text

Performance impact ▪ Only one place with a blocking event loop can decrease throughput to super low values. AVOID THE SYNCHRONOUS CODE

Slide 22

Slide 22 text

Speed Up JSON Serialization 04

Slide 23

Slide 23 text

Try to use fast-json-stringify

Slide 24

Slide 24 text

It can be 10x faster than JSON.stringify SPEED UP JSON SERIALIZATION Throughput, ops/sec (higher is better)

Slide 25

Slide 25 text

Write The Asynchronous Code Correctly 05

Slide 26

Slide 26 text

Try to use Promise.all

Slide 27

Slide 27 text

Use The Streams Correctly 06

Slide 28

Slide 28 text

Use the streams instead of loading all the data into memory

Slide 29

Slide 29 text

Use the streams instead of rendering all the data to the string

Slide 30

Slide 30 text

STREAMS String vs Stream for server-side rendering

Slide 31

Slide 31 text

Try To Optimize V8 GC 07

Slide 32

Slide 32 text

Using the large object as a cache, resulting in slower garbage collection

Slide 33

Slide 33 text

V8 GC Main V8 GC (full mark-compact) https://v8.dev/blog/trash-talk

Slide 34

Slide 34 text

We can use V8 flags for tuning an application according to load (Node.js 10)

Slide 35

Slide 35 text

Trace GC output (Node.js 12)

Slide 36

Slide 36 text

Move CPU Bound Tasks Out Of The Node.js Code 08

Slide 37

Slide 37 text

What we can move to reverse proxy? ▪ Gzip; ▪ HTTP/2; ▪ SSL; ▪ Serving of static files; ▪ Request/response logging; ▪ Metrics/Monitoring; ▪ Authorization. REVERSE PROXY FOR CPU BOUND TASKS

Slide 38

Slide 38 text

Try To Use C++ Extensions 09

Slide 39

Slide 39 text

µWebSockets.js ▪ µWebSockets is optimized implementation of HTTP and WebSockets; ▪ The implementation is header-only C++ 17 (7K LOC), cross-platform and compiles down to a tiny binary; ▪ µWebSockets.js is the Google V8 bindings to µWebSockets. C++ EXTENSIONS

Slide 40

Slide 40 text

Example of echo WebSocket server

Slide 41

Slide 41 text

C++ EXTENSIONS µWebSockets.js: HTTP Server (official) Throughput, req/sec (higher is better)

Slide 42

Slide 42 text

C++ EXTENSIONS µWebSockets.js: WebSocket Server (official) Throughput, msg/sec (higher is better)

Slide 43

Slide 43 text

C++ EXTENSIONS µWebSockets.js: HTTP server Throughput, req/sec (higher is better)

Slide 44

Slide 44 text

C++ EXTENSIONS Performance impact (in compare with core http module) ▪ 3.2x better for JSON responses with timestamp ▪ 1.8x better for cache server case (with Redis as storage)

Slide 45

Slide 45 text

C++ EXTENSIONS µWebSockets.js: Results with connections

Slide 46

Slide 46 text

C++ EXTENSIONS µWebSockets.js: Results with messages

Slide 47

Slide 47 text

C++ EXTENSIONS Performance impact (in compare with ws package) ▪ 23x better short message throughput ▪ 21x better connection performance ▪ 52x less memory usage

Slide 48

Slide 48 text

Use Tools To Locate Performance Issues 10

Slide 49

Slide 49 text

Amazing presentation about load testing TOOLING https://www.youtube.com/watch?v=unhG-WUOHnE

Slide 50

Slide 50 text

What can we use for loading the application? ▪ WRK; ▪ Apache Benchmark (ab); ▪ AutoCannon; ▪ Own handmade loaders; ▪ Nginx Mirroring. TOOLING

Slide 51

Slide 51 text

Nginx Mirroring (from version 1.13)

Slide 52

Slide 52 text

Clinic.js: An open source performance profiling suite TOOLING https://clinicjs.org/

Slide 53

Slide 53 text

TOOLING clinic doctor --on-port 'autocannon localhost:$PORT' -- node server.js Clinic.js Doctor: Diagnose performance issues

Slide 54

Slide 54 text

TOOLING clinic doctor --on-port 'autocannon localhost:$PORT' -- node server.js Clinic.js Doctor: Diagnose performance issues

Slide 55

Slide 55 text

Clinic.js Doctor: diagnose performance issues TOOLING

Slide 56

Slide 56 text

Clinic.js Bubbleprof: Tracks latency between operations TOOLING clinic bubbleprof --on-port 'autocannon localhost:$PORT' -- node server.js

Slide 57

Slide 57 text

Clinic.js Bubbleprof: Tracks latency between operations TOOLING

Slide 58

Slide 58 text

TOOLING clinic flame --on-port 'autocannon localhost:$PORT' -- node server.js Clinic.js Flame: Uncovers The Bottlenecks And Hot Paths With Flamegraphs

Slide 59

Slide 59 text

Clinic.js Flame: Uncovers the bottlenecks and hot paths TOOLING

Slide 60

Slide 60 text

TOOLING Clinic.js: Examples and demos for study ▪ A set of simple Doctor examples (slow event loop, slow GC, sync IO etc); ▪ A set of simple Bubbleprof examples; ▪ A MongoDB-based Bubbleprof demo/example; ▪ A Flame demo/example. https://github.com/nearform/node-clinic#examples-and-demos

Slide 61

Slide 61 text

0x: Single-command flamegraph profiling TOOLING https://github.com/davidmarkclements/0x https://habr.com/ru/post/414541/

Slide 62

Slide 62 text

Conclusion

Slide 63

Slide 63 text

1. Use the latest LTS version of Node.js 2. Never ever run Express in production without NODE_ENV = production 3. Add limits and pagination to API 4. Review project and use Promise.all where it is possible 5. Use streams for working with large data 6. Try to optimize V8 GC by flags 7. Try to use C++ extensions in critical parts of the application 8. Use load and diagnostic tools for locating performance issues CONCLUSIONS

Slide 64

Slide 64 text

Thank You [email protected] github.com/andriichyzh

Slide 65

Slide 65 text

Q&A [email protected] github.com/andriichyzh