Milliseconds to Millions!

Benchmarking & Optimization for fun and profit!!​

Hello World!

SYRIAN-CANADIAN, Entrepreneur, Hacker, Developer, Open Source Advocate & Dog Lover

@AhmadNassri

http://ahmad.codes

Principal Architect at TELUS Digital, Founder at Bench CI, Founder at Tech Masters, Mentor at Node School Toronto, Board Member at Full Stack Toronto, Organizer at Functions Conf, Editor at The RESTful Web.


Tech Outlaw, Wanted by a third-world dictator
(true story)

AGENDA

  • What is benchmarking?
  • Why should you care?
  • When & Where to start
  • How!?
  • Q&A
     

OUTCOME

  • Get excited about performance & optimization
  • Learn about the tools, tricks and Gotchas!
  • Benchmark all the things!!!
  • Save your company $$$

the WHAT

PERFORMANCe!

... maybe?

 PERFORMANCE on the Front End

RAIL: A User-Centric Model For Performance

PERFORMANCE Engineering

  • Response time
    The amount of time that it takes for a server to respond to a request
     
  • Throughput 
    The number of requests that can be served by your application per unit time
     
  • Resource utilization
    CPU, memory, disk I/O, and network I/O
     
  • Workload
    The total number of users and concurrent active users, data volumes, and transaction volumes

WHY SHOULD YOU CARE?

Bank SCenario

Consider a system that processes CAD GBP transactions

 

Throughput of 1 Billion CAD GBP worth of transactions per minute

 

You are the best coder in the world, but you're not perfect

Your error rate is 0.1%


A single bug could cost the company millions, but it's detectable

 

Performance degradation could cost billions!

Not easily detectable

WHY SHOULD YOU CARE?

WHY SHOULD YOU CARE?

WHY SHOULD YOU CARE?

Measure Performance

Code quality is not enough, keep track of performance and efficient resource usage.

Reduce Resource Consumption

Memory capacity & CPU cycles are precious in resource constrained environments

Ship With Confidence

Ensure your software operates on and within any hardware & resource constraints.

WHEN & WHERE

WHEN & WHERE TO MEASURE PERFORMANCE?

MOST COMMON...

Benchmarking

&

Profiling

DO IT AS EARLY AS POSSIBLE

OK, HOW?

FINALlY!

Benchmarking

It's EAsy!

Discover optimizations that could impact the largest number of users

$ time for a in {1..10}; do node index.js; done

real    0m13.042s
user    0m0.021s
sys     0m0.044s

IT'S SUPER EASY!

  • real: time from start to finish. (all elapsed time including time slices used by other processes + process block)
     
  • user: actual CPU time used in executing the process
     
  • sys: CPU time spent in system calls (within the kernel)

FUN IN THE BROWSER

LET's GO Deeper!

Benchmark.js

https://benchmarkjs.com

new Benchmark.Suite()
  .add({
    name: 'JSON.parse',
    fn: () => JSON.parse(json)
  })

  .add({
    name: 'jju',
    fn: () => jju.parse(json)
  })

  ...

  .add({
    name: 'vuvuzela',
    fn: () => vuvuzela.parse(json)
  })

  .run()

Benchmark.js

 clarinet          x  2,636 ops/sec ±2.13% (78 runs sampled)
 JSON.parse        x 80,639 ops/sec ±2.14% (88 runs sampled)
 jju               x  7,343 ops/sec ±3.12% (84 runs sampled)
 jsonparse         x 10,719 ops/sec ±4.54% (45 runs sampled)
 json-parse-stream x  2,039 ops/sec ±6.39% (69 runs sampled)
 stream-json       x  1,775 ops/sec ±1.14% (82 runs sampled)
 vuvuzela          x 15,644 ops/sec ±4.07% (87 runs sampled)

Profiling!

Okay, this one is not as easy

BROWSER: BUILT IN Profiler!

app.get('/auth', function (req, res) {
  const username = req.query.username || ''
  const password = req.query.password || ''

  username = username.replace(/[!@#$%^&*]/g, '')

  if (!username || !password || !users[username]) {
    return res.sendStatus(400)
  }

  const hash = crypto.pbkdf2Sync(password, users[username].salt, 10000, 512)

  if (users[username].hash.toString() === hash.toString()) {
    res.sendStatus(200)
  } else {
    res.sendStatus(401)
  }
})

Node.js Profiler

[Summary]:
   ticks  total  nonlib   name
     79    0.2%    0.2%  JavaScript
  36703   97.2%   99.2%  C++
      7    0.0%    0.0%  GC
    767    2.0%          Shared libraries
    215    0.6%          Unaccounted

Node.js Profiler

$ node --prof ./app.js
$ node --prof-process ./log-file
 [C++]:
   ticks  total  nonlib   name
  19557   51.8%   52.9%  node::crypto::PBKDF2(v8::FunctionCallbackInfo<v8::Value> const&)
   4510   11.9%   12.2%  _sha1_block_data_order
   3165    8.4%    8.6%  _malloc_zone_malloc
 [Bottom up (heavy) profile]
   ticks parent  name
  19557   51.8%  node::crypto::PBKDF2(v8::FunctionCallbackInfo<v8::Value> const&)
  19557  100.0%    v8::internal::Builtins::~Builtins()
  19557  100.0%      LazyCompile: ~pbkdf2 crypto.js:557:16

   4510   11.9%  _sha1_block_data_order
   4510  100.0%    LazyCompile: *pbkdf2 crypto.js:557:16
   4510  100.0%      LazyCompile: *exports.pbkdf2Sync crypto.js:552:30

   3165    8.4%  _malloc_zone_malloc
   3161   99.9%    LazyCompile: *pbkdf2 crypto.js:557:16
   3161  100.0%      LazyCompile: *exports.pbkdf2Sync crypto.js:552:30

Node.js Profiler

Node.js Profiler

app.get('/auth', function (req, res) {
  const username = req.query.username || ''
  const password = req.query.password || ''

  username = username.replace(/[!@#$%^&*]/g, '')

  if (!username || !password || !users[username]) {
    return res.sendStatus(400);
  }

  crypto.pbkdf2(password, users[username].salt, 10000, 512, function (err, hash) {
    if (users[username].hash.toString() === hash.toString()) {
      res.sendStatus(200)
    } else {
      res.sendStatus(401)
    }
  })
})

PRO MODE++

GOtchas

Transpilers!

Benchmarking != Monitoring

System calls!

Dependencies

END-2-END

Thank you!

@AhmadNassri

http://ahmad.codes

Slides & Links:
ahmad.codes/talks
TechMasters.chat
Functions.world
FSTO.co

Milliseconds to Millions - FullStack London 2017

By Ahmad Nassri

Milliseconds to Millions - FullStack London 2017

Benchmarking & Optimization for fun and profit

  • 1,053