- Fire map generation in the Java background is very simple and there is a tool out there using Arthas
- Reference the flame of arthas graph generating documentation can be: arthas.aliyun.com/doc/profile…
- Node end flame graph performance optimization actual combat, actual combat examples are online predecessors have to share, learn from use, more suitable for demonstration actual combat demo
1. Test code
const crypto = require('crypto') const Paloma = require('paloma') const app = new Paloma() const users = {} app.route({ method: 'GET', path: '/newUser', controller(ctx) { const username = ctx.query.username || 'test' const password = ctx.query.password || 'test' const salt = crypto.randomBytes(128).toString('base64') const hash = crypto.pbkdf2Sync(password, salt, 10000, 64, 'sha512').toString('hex') users[username] = { salt, hash } ctx.status = 204 } }) app.route({ method: 'GET', path: '/auth', controller(ctx) { const username = ctx.query.username || 'test' const password = ctx.query.password || 'test' if (! users[username]) { ctx. throw (400) } const hash = crypto.pbkdf2Sync(password, users[username].salt, 10000, 64, 'sha512').toString('hex') if (users[username].hash === hash) { ctx.status = 204 } else { ctx. throw (403) } } }) app.listen(3000)Copy the code
2. Run the Node. js program using the perf parameter
$ node --perf_basic_prof app.js &
[1] 3590
$ tail /tmp/perf-3590.map
51b87a7b93e 18 Function:~emitListeningNT net.js:1375
51b87a7b93e 18 LazyCompile:~emitListeningNT net.js:1375
51b87a7bad6 39 Function:~emitAfterScript async_hooks.js:443
51b87a7bad6 39 LazyCompile:~emitAfterScript async_hooks.js:443
51b87a7bcbe 77 Function:~tickDone internal/process/next_tick.js:88
51b87a7bcbe 77 LazyCompile:~tickDone internal/process/next_tick.js:88
51b87a7bf36 12 Function:~clear internal/process/next_tick.js:42
51b87a7bf36 12 LazyCompile:~clear internal/process/next_tick.js:42
51b87a7c126 b8 Function:~emitPendingUnhandledRejections internal/process/promises.js:86
51b87a7c126 b8 LazyCompile:~emitPendingUnhandledRejections internal/process/promises.js:86
Copy the code
Note: Use the -perf_basic_prof_only_functions argument to convert the symbol table at code execution to human-readable function names
3. Ab pressure measurement
$ curl "<http://localhost:3000/newUser? username=admin&password=123456>" $ ab -k -c 10 -n 2000 "<http://localhost:3000/auth? username=admin&password=123456>"Copy the code
4. Capture data and draw flame chart
$ sudo perf record -F 99 -p 28671 -g -- sleep 30
$ sudo chown root /tmp/perf-28671.map
$ sudo perf script > perf.stacks
$ ./stackcollapse-perf.pl --kernel < perf.stacks | ./flamegraph.pl --color=js --hash > flamegrap1.svg
Copy the code
Perf record saves the recorded information to the perf.data file in the current execution directory. Perf script reads the trace information of perf.data and writes it to perf.stacks. Then FlameGraph draws the FlameGraph.
— color=js Specifies that SVG is generated for THE JS color scheme, that is:
Green: JavaScript. Blue: Builtin. Yellow: c + +. Red: System (native user-level, and kernel).
The ab pressure test took about 30 seconds. The browser opened Flamegraph. SVG and captured the key parts as shown below:
As you can see from the image above, the top green block (JavaScript code) points to line 18 of test/app.js, the GET /auth route. Further up, the little yellow (C++ code) node::crypto::PBKDF2 takes up a lot of CPU time.
Solution: Change the synchronization to asynchronous, i.e. change crypto. Pbkdf2Sync to crypto.
const crypto = require('crypto') const Paloma = require('paloma') const app = new Paloma() const users = {} app.route({ method: 'GET', path: '/newUser', controller(ctx) { const username = ctx.query.username || 'test' const password = ctx.query.password || 'test' const salt = crypto.randomBytes(128).toString('base64') const hash = crypto.pbkdf2Sync(password, salt, 10000, 64, 'sha512').toString('hex') users[username] = { salt, hash } ctx.status = 204 } }) app.route({ method: 'GET', path: '/auth', async controller(ctx) { const username = ctx.query.username || 'test'const password = ctx.query.password || 'test' if (! users[username]) { ctx. throw (400) } const hash = await new Promise((resolve, reject) = >{ crypto.pbkdf2(password, users[username].salt, 10000, 64, 'sha512', (err, derivedKey) = >{ if (err) { return reject(err) } resolve(derivedKey.toString('hex')) }) }) if (users[username].hash === hash) { ctx.status = 204 } else { ctx. throw (403) } } }) app.listen(3000)Copy the code