Skip to content

Commit

Permalink
feat(bench-trail): new package to benchmark performance
Browse files Browse the repository at this point in the history
  • Loading branch information
Acatl Pacheco committed Jan 29, 2018
1 parent bb6c7bc commit 6f45729
Show file tree
Hide file tree
Showing 7 changed files with 607 additions and 0 deletions.
92 changes: 92 additions & 0 deletions packages/bench-trail/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Bench Trail

> Runs one or multiple benchmark tests
## Install

```bash
npm install -g bench-trail
```

## Usage

```bash
bench-trail benchmarks/map-helper-vs-entity.js -i 5
```

## TL;DR

Runs one (or more) BenchmarkJs test multiple times enough to get less ambiguous results, includes basic testing to make sure tests are reliable.

## Why?

While running [benchmarkjs](https://benchmarkjs.com) to compare different versions of code I found out a couple of things:

- **Ambiguous results**: tests would throw different results every time I ran the test, specially if the difference was minimal. If I ran the same test multiple times the numbers changed, as time went by I would get more and more operations per second on each benchmark. This would only happen if I ran the tests consecutively, the reason of this might be related to the v8 engine warming up and optimizing the code the more I ran it. If I let some time to cool off, the tests would go back down on operations per second.
- **Reliable Execution**: more than once I made changes on the code being tested and never did I notice the change I had made was not even executing correctly. So the results I was getting were really unreliable.

## Solution

- **Ambiguous results**: Run benchmark more than once to get median and average results, because the test will run multiple times the code will get optimized, using the median we can get more reliable results.
- **Reliable Execution**: Run a simple assertion tests on each suite before the actual benchmark runs, this helps us make sure our test are executing correctly.

## API

```bash
bench-trail <file> [-i <iterations>] [-s]
```

- `-i --iterations <iteration>` iterations default to 10 iterations if not provided.
- `-s --skip-tests` if provided, it will skip the assertion tests.

### Writing your benchmark suites

The file you provide to bench-trail should export an `array` of suites, each suite is an object in the form of:

```
{
name: string,
test: function,
benchmark: function
}
```

| Property | Type | Description |
|:---|:---|:---|
| *name* | `String` | Name that describes the test you are running |
| *test* | `function` | function to run assertion test against the result of the code you want to benchmark |
| *benchmark* | `function` | function to pass to benchmarkjs Suite that actually runs the benchmark |

#### Sync vs Async

- Synchronous methods are simple methods that expect a return value.
- Asynchronous methods are a bit different to benchmarkjs async methods, bench-trail expects async methods to follow the [error-first callbacks](https://nodejs.org/api/errors.html#errors_error_first_callbacks).

#### Testing

bench-trail provides a convenience method that accepts the function to execute and a value to check against the result of the code you are testing. It takes care of async vs async depending on how you set the `async` flag.

```js
test(test:function, value:*)
```

to write your manual test see the manual test example below

## Examples

Test synchronous code [example](examples/array-iteration.js)
Test asynchronous code [example](examples/async-example.js)
Write manual test sync/asynchronous code [example](examples/manual-tests.js)


## Acknowledgements

This tool is only a wrapper of [benchmarkjs](https://benchmarkjs.com), so the credit really goes to them.

## <a name="contributing">Contributing</a>

Please read [CONTRIBUTING.md](https://github.com/ViacomInc/data-point/blob/master/CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us.

## <a name="license">License</a>

This project is licensed under the Apache License Version 2.0 - see the [LICENSE](LICENSE) file for details
43 changes: 43 additions & 0 deletions packages/bench-trail/examples/array-iteration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const { test } = require('bench-trail')

const array = Array(100).fill('foo')
const expected = array.join('').length

function forLoop () {
let result = ''
for (let index = 0; index < array.length; index++) {
result = result + array[index]
}

const length = result.length
result = ''
return length
}

function whileLoop () {
let result = ''
let index = 0
while (index !== array.length) {
result = result + array[index]
index++
}

const length = result.length
result = ''
return length
}

module.exports = [
{
async: false,
name: 'while-loop',
test: test(whileLoop, expected),
benchmark: whileLoop
},
{
async: false,
name: 'for-loop',
test: test(forLoop, expected),
benchmark: forLoop
}
]
30 changes: 30 additions & 0 deletions packages/bench-trail/examples/async-example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const { test } = require('bench-trail')

const expected = true

function testPromise (done) {
Promise.resolve(true).then(() => {
done(null, true)
})
}

function testSetTimeOut (done) {
setTimeout(() => {
done(null, true)
}, 0)
}

module.exports = [
{
async: true,
name: 'promise',
test: test(testPromise, expected),
benchmark: testPromise
},
{
async: true,
name: 'timeout',
test: test(testSetTimeOut, expected),
benchmark: testSetTimeOut
}
]
33 changes: 33 additions & 0 deletions packages/bench-trail/examples/manual-tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const assert = require('assert')

const expected = 2

function addsNumbersSync () {
return 1 + 1
}

function addsNumbersAsync (done) {
Promise.resolve().then(() => {
done(null, 1 + 1)
})
}

module.exports = [
{
async: false,
name: 'addsNumbersSync',
test: () => assert.deepEqual(addsNumbersSync(), expected),
benchmark: addsNumbersSync
},
{
async: true,
name: 'addsNumbersAsync',
test: done => {
addsNumbersAsync((e, val) => {
assert.deepEqual(val, expected)
done(null)
})
},
benchmark: addsNumbersAsync
}
]
54 changes: 54 additions & 0 deletions packages/bench-trail/lib/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
const assert = require('assert')

function testSync (method, expected) {
return () => assert.deepEqual(method(), expected)
}

function testAsync (method, expected) {
return done => {
return method((err, value) => {
if (err) {
return done(err)
}
try {
assert.deepEqual(value, expected)
done(null, value)
} catch (e) {
done(e)
}
})
}
}

function test (method, expected) {
return {
test: method,
expected
}
}

test.sync = testSync
test.async = testAsync

function benchmarkSync (method) {
return method
}

function benchmarkAsync (method) {
return deferred => {
return method((err, value) => {
if (err) {
throw err
}
deferred.resolve()
})
}
}

module.exports = {
test,
benchmark: {
sync: benchmarkSync,
async: benchmarkAsync
}
}
22 changes: 22 additions & 0 deletions packages/bench-trail/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "bench-trail",
"version": "2.0.0",
"description": "Runs one or multiple benchmark tests",
"main": "./lib/index.js",
"license": "Apache-2.0",
"engines": {
"node": ">=6"
},
"bin": {
"bench-trail": "runner.js"
},
"author": {
"name": "Acatl Pacheco",
"email": "acatl.pacheco@viacom.com"
},
"devDependencies": {
"benchmark": "2.1.4",
"commander": "2.x",
"chalk": "2.x"
}
}
Loading

0 comments on commit 6f45729

Please sign in to comment.