Skip to content

Commit b349085

Browse files
pgtedaviddias
authored andcommitted
feat: added getMany performance tests (#164)
1 parent ff978d0 commit b349085

File tree

7 files changed

+203
-43
lines changed

7 files changed

+203
-43
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,5 @@ test/test-repo-for*
3636
docs
3737

3838
test/test-repo/datastore
39+
40+
*.flamegraph

README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,35 @@ src
171171
└── index.js
172172
```
173173

174+
## Performance tests
175+
176+
You can run performance tests like this:
177+
178+
```
179+
$ npm run benchmarks
180+
```
181+
182+
### Profiling
183+
184+
You can run each of the individual performance tests with a profiler like 0x.
185+
186+
To do that, you need to install 0x:
187+
188+
```bash
189+
$ npm install 0x --global
190+
```
191+
192+
And then run the test:
193+
194+
```bash
195+
$ 0x test/benchmarks/get-many
196+
```
197+
198+
This will output a flame graph and print the location for it.
199+
Use the browser Chrome to open and inspect the generated graph.
200+
201+
![Flame graph](https://ipfs.io/ipfs/QmVbyLgYfkLewNtzTAFwAEMmP2hTJgs8sSqsRTBNBjyQ1y)
202+
174203
## Contribute
175204

176205
Feel free to join in. All welcome. Open an [issue](https://github.com/ipfs/js-ipfs-bitswap/issues)!

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
"bench": "node benchmarks/index",
1919
"build": "aegir build",
2020
"coverage": "aegir coverage --provider codecov",
21-
"docs": "aegir docs"
21+
"docs": "aegir docs",
22+
"benchmarks": "node test/benchmarks/get-many"
2223
},
2324
"repository": {
2425
"type": "git",
@@ -57,7 +58,8 @@
5758
"peer-info": "~0.11.4",
5859
"pre-commit": "^1.2.2",
5960
"rimraf": "^2.6.2",
60-
"safe-buffer": "^5.1.1"
61+
"safe-buffer": "^5.1.1",
62+
"stats-lite": "^2.1.0"
6163
},
6264
"dependencies": {
6365
"async": "^2.6.0",

test/benchmarks/get-many.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
'use strict'
2+
3+
const distributionTest = require('../utils/distribution-test')
4+
const print = require('./helpers/print-swarm-results')
5+
6+
print('10 nodes, 10 blocks, 5 iterations', distributionTest(10, 10, 5, (err) => {
7+
if (err) {
8+
throw err
9+
}
10+
11+
console.log('Finished. Can kill now...')
12+
}))
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
'use strict'
2+
3+
const stats = require('stats-lite')
4+
5+
module.exports = (suite, emitter) => {
6+
const elapseds = []
7+
emitter.once('start', () => {
8+
console.log('\n------------------------')
9+
console.log(suite)
10+
console.log('started')
11+
})
12+
emitter.once('all connected', () => {
13+
console.log('all nodes connected to each other')
14+
})
15+
emitter.on('getting many', () => {
16+
process.stdout.write('.')
17+
})
18+
emitter.once('stop', () => {
19+
console.log('\nstopping')
20+
})
21+
emitter.once('stopped', () => {
22+
console.log('stopped')
23+
console.log('stats:')
24+
console.log('---------')
25+
console.log('mean: %s', stats.mean(elapseds))
26+
console.log('median: %s', stats.median(elapseds))
27+
console.log('variance: %s', stats.variance(elapseds))
28+
console.log('standard deviation: %s', stats.stdev(elapseds))
29+
console.log('85th percentile: %s', stats.percentile(elapseds, 0.85))
30+
})
31+
32+
emitter.on('got block', (elapsed) => {
33+
process.stdout.write('+')
34+
elapseds.push(elapsed)
35+
})
36+
}

test/swarms.js

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,81 @@
22

33
/* eslint-env mocha */
44

5+
const stats = require('stats-lite')
56
const distributionTest = require('./utils/distribution-test')
67
const test = it
78

89
describe('swarms', () => {
10+
const print = Boolean(process.env.PRINT)
11+
12+
after(() => {
13+
process.exit()
14+
})
15+
916
test('2 nodes, 2 blocks', function (done) {
1017
this.timeout(10 * 1000)
11-
distributionTest(2, 2, done)
18+
maybePrint('2 nodes, 2 blocks', distributionTest(2, 2, done))
1219
})
1320

1421
test('10 nodes, 2 blocks', function (done) {
1522
this.timeout(30 * 1000)
16-
distributionTest(10, 2, done)
23+
maybePrint('10 nodes, 2 blocks', distributionTest(10, 2, done))
24+
})
25+
26+
test.only('10 nodes, 10 blocks', function (done) {
27+
this.timeout(30 * 1000)
28+
maybePrint('10 nodes, 10 blocks', distributionTest(10, 10, 1, done))
29+
})
30+
31+
test('10 nodes, 20 blocks', function (done) {
32+
this.timeout(30 * 1000)
33+
maybePrint('10 nodes, 20 blocks', distributionTest(10, 20, done))
1734
})
1835

1936
test('50 nodes, 2 blocks', function (done) {
2037
this.timeout(600 * 1000)
21-
distributionTest(50, 2, done)
38+
maybePrint('50 nodes, 2 blocks', distributionTest(50, 2, done))
2239
})
2340

2441
test.skip('100 nodes, 2 blocks', function (done) {
2542
this.timeout(600 * 1000)
26-
distributionTest(100, 2, done)
43+
maybePrint('100 nodes, 2 blocks', distributionTest(100, 2, done))
2744
})
2845

2946
test('10 nodes, 100 blocks', function (done) {
3047
this.timeout(600 * 1000)
31-
distributionTest(10, 100, done)
48+
maybePrint('10 nodes, 100 blocks', distributionTest(10, 100, done))
3249
})
50+
51+
function maybePrint (suite, emitter) {
52+
if (!print) {
53+
return
54+
}
55+
const elapseds = []
56+
emitter.once('start', () => {
57+
console.log('\n------------------------')
58+
console.log(suite)
59+
console.log('started')
60+
})
61+
emitter.once('all connected', () => {
62+
console.log('all nodes connected to each other')
63+
})
64+
emitter.once('stop', () => {
65+
console.log('stopping')
66+
})
67+
emitter.once('stopped', () => {
68+
console.log('stopped')
69+
console.log('stats:')
70+
console.log('---------')
71+
console.log('mean: %s', stats.mean(elapseds))
72+
console.log('median: %s', stats.median(elapseds))
73+
console.log('variance: %s', stats.variance(elapseds))
74+
console.log('standard deviation: %s', stats.stdev(elapseds))
75+
console.log('85th percentile: %s', stats.percentile(elapseds, 0.85))
76+
})
77+
78+
emitter.on('got block', (elapsed) => {
79+
elapseds.push(elapsed)
80+
})
81+
}
3382
})

test/utils/distribution-test.js

Lines changed: 66 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3,58 +3,86 @@
33
const range = require('lodash.range')
44
const map = require('async/map')
55
const each = require('async/each')
6-
const parallel = require('async/parallel')
6+
const whilst = require('async/whilst')
77
const series = require('async/series')
88
const waterfall = require('async/waterfall')
99
const chai = require('chai')
1010
chai.use(require('dirty-chai'))
1111
const expect = chai.expect
12+
const EventEmitter = require('events')
1213

1314
const createBitswap = require('./create-bitswap')
1415
const makeBlock = require('./make-block')
1516
const connectAll = require('./connect-all')
1617

17-
module.exports = (instanceCount, blockCount, callback) => {
18+
module.exports = (instanceCount, blockCount, repeats, callback) => {
19+
let pendingRepeats = repeats
1820
let nodes
19-
let blocks
21+
const events = new EventEmitter()
2022

2123
waterfall([
22-
(cb) => parallel(
23-
{
24-
nodes: (cb) => map(range(instanceCount), (_, cb) => createBitswap(cb), cb),
25-
blocks: (cb) => map(range(blockCount), (_, cb) => makeBlock(cb), cb)
26-
},
27-
cb),
28-
(results, cb) => {
29-
nodes = results.nodes
30-
blocks = results.blocks
31-
const first = nodes[0]
32-
33-
parallel([
34-
(cb) => connectAll(results.nodes, cb),
35-
(cb) => each(results.blocks, first.bitswap.put.bind(first.bitswap), cb)
36-
], cb)
24+
(cb) => map(range(instanceCount), (_, cb) => createBitswap(cb), cb),
25+
(_nodes, cb) => {
26+
nodes = _nodes
27+
events.emit('start')
28+
cb()
3729
},
38-
(results, cb) => {
39-
const cids = blocks.map((block) => block.cid)
40-
map(nodes, (node, cb) => node.bitswap.getMany(cids, cb), cb)
30+
(cb) => {
31+
connectAll(nodes, cb)
4132
},
42-
(results, cb) => {
43-
try {
44-
expect(results).have.lengthOf(instanceCount)
45-
results.forEach((nodeBlocks) => {
46-
expect(nodeBlocks).to.have.lengthOf(blocks.length)
47-
nodeBlocks.forEach((block, i) => {
48-
expect(block.data).to.deep.equal(blocks[i].data)
49-
})
50-
})
51-
} catch (err) {
52-
return cb(err)
53-
}
54-
cb()
33+
(cb) => {
34+
events.emit('all connected')
35+
whilst(() => pendingRepeats > 0, (cb) => {
36+
const first = nodes[0]
37+
let blocks
38+
waterfall([
39+
(cb) => map(range(blockCount), (_, cb) => makeBlock(cb), cb),
40+
(_blocks, cb) => {
41+
blocks = _blocks
42+
cb()
43+
},
44+
(cb) => each(blocks, first.bitswap.put.bind(first.bitswap), (err) => {
45+
events.emit('first put')
46+
cb(err)
47+
}),
48+
(cb) => map(nodes, (node, cb) => {
49+
events.emit('getting many')
50+
const cids = blocks.map((block) => block.cid)
51+
const start = Date.now()
52+
node.bitswap.getMany(cids, (err, result) => {
53+
if (err) {
54+
cb(err)
55+
} else {
56+
const elapsed = Date.now() - start
57+
events.emit('got block', elapsed)
58+
cb(null, result)
59+
}
60+
})
61+
}, cb),
62+
(results, cb) => {
63+
try {
64+
expect(results).have.lengthOf(instanceCount)
65+
results.forEach((nodeBlocks) => {
66+
expect(nodeBlocks).to.have.lengthOf(blocks.length)
67+
nodeBlocks.forEach((block, i) => {
68+
expect(block.data).to.deep.equal(blocks[i].data)
69+
})
70+
})
71+
} catch (err) {
72+
return cb(err)
73+
}
74+
cb()
75+
},
76+
(cb) => {
77+
pendingRepeats--
78+
cb()
79+
}
80+
], cb)
81+
}, cb)
5582
}
5683
],
5784
(err) => {
85+
events.emit('stop')
5886
each(
5987
nodes,
6088
(node, cb) => {
@@ -64,12 +92,14 @@ module.exports = (instanceCount, blockCount, callback) => {
6492
(cb) => node.libp2pNode.stop(cb),
6593
(cb) => node.repo.teardown(cb)
6694
],
67-
cb
68-
)
95+
cb)
6996
},
7097
(err2) => {
98+
events.emit('stopped')
7199
callback(err)
72100
}
73101
)
74102
})
103+
104+
return events
75105
}

0 commit comments

Comments
 (0)