Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit db757fd

Browse files
committed
Faster bcrypt
1 parent 43a40ea commit db757fd

File tree

7 files changed

+245
-177
lines changed

7 files changed

+245
-177
lines changed

packages/bcrypt/Cargo.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ version = "0.1.0"
88
crate-type = ["cdylib"]
99

1010
[dependencies]
11-
bcrypt = "0.13"
12-
blowfish = { version = "0.9", features = ["bcrypt"] }
11+
bcrypt = "0.14"
12+
getrandom = "0.2"
1313
global_alloc = { path = "../../crates/alloc" }
1414
napi = { version = "2", default-features = false, features = ["napi3"] }
1515
napi-derive = { version = "2" }
16-
getrandom = "0.2"
17-
base64 = { version = "0.20" }
16+
rand = "0.8"
17+
base64 = { version = "0.21" }
1818

1919
[dev-dependencies]
2020
quickcheck = "1.0"
Lines changed: 207 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -1,144 +1,224 @@
11
const { cpus } = require('os')
2+
const { hrtime } = require('process')
23

34
const { hashSync, hash, compare, genSaltSync } = require('bcrypt')
45
const { hashSync: hashSyncJs, hash: hashJs, compare: compareJs, genSaltSync: genSaltSyncJs } = require('bcryptjs')
56
const { Suite } = require('benchmark')
67
const chalk = require('chalk')
78
const { range } = require('lodash')
9+
const { from, timer, lastValueFrom, Subject } = require('rxjs')
10+
const { mergeMap, takeUntil } = require('rxjs/operators')
811

912
const { hash: napiHash, hashSync: napiHashSync, verify, genSaltSync: napiGenSaltSync } = require('../index')
1013

11-
const parallel = cpus().length
14+
const parallel = cpus().length - 1
1215

1316
const password = 'node-rust-password'
1417

15-
function runAsync(round = 12) {
16-
const asyncHashSuite = new Suite(`Async hash round ${round}`)
17-
return new Promise((resolve) => {
18-
asyncHashSuite
19-
.add('@node-rs/bcrypt', {
20-
defer: true,
21-
fn: (deferred) => {
22-
Promise.all(range(parallel).map(() => napiHash(password, round))).then(() => {
23-
deferred.resolve()
24-
})
25-
},
26-
})
27-
.add('node bcrypt', {
28-
defer: true,
29-
fn: (deferred) => {
30-
Promise.all(range(parallel).map(() => hash(password, round))).then(() => {
31-
deferred.resolve()
32-
})
33-
},
34-
})
35-
.add('bcryptjs', {
36-
defer: true,
37-
fn: (deferred) => {
38-
Promise.all(range(parallel).map(() => hashJs(password, round))).then(() => {
39-
deferred.resolve()
40-
})
41-
},
42-
})
43-
.on('cycle', function (event) {
44-
event.target.hz = event.target.hz * parallel
45-
console.info(String(event.target))
46-
})
47-
.on('complete', function () {
48-
console.info(`${this.name} bench suite: Fastest is ${chalk.green(this.filter('fastest').map('name'))}`)
49-
resolve()
18+
const CPU_LENGTH = cpus().length
19+
20+
const DEFAULT_TOTAL_ITERATIONS = 10000
21+
const DEFAULT_MAX_DURATION = 20000
22+
23+
function bench(name, options = {}) {
24+
const suites = []
25+
return {
26+
add(suiteName, suiteFn) {
27+
suites.push({
28+
name: suiteName,
29+
fn: suiteFn,
5030
})
51-
.run({ async: true })
52-
})
31+
return this
32+
},
33+
run: async () => {
34+
let fastest = {
35+
perf: -1,
36+
name: '',
37+
}
38+
for (const { suiteName, fn: suiteFn } of suites) {
39+
try {
40+
await suiteFn()
41+
} catch (e) {
42+
console.error(`Warming up ${suiteName} failed`)
43+
throw e
44+
}
45+
}
46+
for (const { name: suiteName, fn: suiteFn } of suites) {
47+
const iterations = options.iterations ?? DEFAULT_TOTAL_ITERATIONS
48+
const parallel = options.parallel ?? CPU_LENGTH
49+
const maxDuration = options.maxDuration ?? DEFAULT_MAX_DURATION
50+
const start = hrtime.bigint()
51+
let totalIterations = 0
52+
let finishedIterations = 0
53+
const finish$ = new Subject()
54+
await lastValueFrom(
55+
from({ length: iterations }).pipe(
56+
mergeMap(async () => {
57+
totalIterations++
58+
await suiteFn()
59+
finishedIterations++
60+
if (finishedIterations === totalIterations) {
61+
finish$.next()
62+
finish$.complete()
63+
}
64+
}, parallel),
65+
takeUntil(timer(maxDuration)),
66+
),
67+
)
68+
if (finishedIterations !== totalIterations) {
69+
await lastValueFrom(finish$)
70+
}
71+
const duration = Number(hrtime.bigint() - start)
72+
const currentPerf = totalIterations / duration
73+
if (currentPerf > fastest.perf) {
74+
fastest = {
75+
perf: currentPerf,
76+
name: suiteName,
77+
}
78+
}
79+
console.info(`${suiteName} ${Math.round(currentPerf * 1e9)} ops/s`)
80+
}
81+
console.info(`In ${name} suite, fastest is ${fastest.name}`)
82+
},
83+
}
5384
}
5485

55-
runAsync()
56-
.then(
57-
() =>
58-
new Promise((resolve) => {
59-
const suite = new Suite('Async verify')
60-
const hash = napiHashSync(password)
61-
suite
62-
.add({
63-
name: '@node-rs/bcrypt',
64-
defer: true,
65-
fn: (deferred) => {
66-
Promise.all(range(parallel).map(() => verify(password, hash))).then(() => {
67-
deferred.resolve()
68-
})
69-
},
70-
})
71-
.add({
72-
name: 'node bcrypt',
73-
defer: true,
74-
fn: (deferred) => {
75-
Promise.all(range(parallel).map(() => compare(password, hash))).then(() => {
76-
deferred.resolve()
77-
})
78-
},
79-
})
80-
.add({
81-
name: 'bcryptjs',
82-
defer: true,
83-
fn: (deferred) => {
84-
Promise.all(range(parallel).map(() => compareJs(password, hash))).then(() => {
85-
deferred.resolve()
86-
})
87-
},
88-
})
89-
.on('cycle', function (event) {
90-
event.target.hz = event.target.hz * parallel
91-
console.info(String(event.target))
92-
})
93-
.on('complete', function () {
94-
resolve()
95-
console.info(`${this.name} bench suite: Fastest is ${chalk.green(this.filter('fastest').map('name'))}`)
96-
})
97-
.run()
98-
}),
99-
)
100-
.then(() => {
101-
return new Promise((resolve) => {
102-
const syncHashSuite = new Suite(`Hash round 12`)
103-
syncHashSuite
104-
.add('@node-rs/bcrypt', () => {
105-
napiHashSync(password, 12)
106-
})
107-
.add('node bcrypt', () => {
108-
hashSync(password, 12)
109-
})
110-
.add('bcryptjs', () => {
111-
hashSyncJs(password, 12)
112-
})
113-
.on('cycle', function (event) {
114-
console.info(String(event.target))
115-
})
116-
.on('complete', function () {
117-
console.info(`${this.name} bench suite: Fastest is ${chalk.green(this.filter('fastest').map('name'))}`)
118-
resolve()
119-
})
120-
.run()
121-
})
122-
})
123-
.then(() => {
124-
return new Promise((resolve) => {
125-
new Suite('genSaltSync')
126-
.add('@node-rs/bcrypt', () => {
127-
napiGenSaltSync(10, '2b')
128-
})
129-
.add('node bcrypt', () => {
130-
genSaltSync(10, 'b')
131-
})
132-
.add('bcryptjs', () => {
133-
genSaltSyncJs(10)
134-
})
135-
.on('cycle', function (event) {
136-
console.info(String(event.target))
137-
})
138-
.on('complete', function () {
139-
console.info(`${this.name} bench suite: Fastest is ${chalk.green(this.filter('fastest').map('name'))}`)
140-
resolve()
141-
})
142-
.run()
143-
})
86+
bench(`Hash round 12`)
87+
.add('@node-rs/bcrypt', () => napiHash(password, 12))
88+
.add('node bcrypt', () => hash(password, 12))
89+
.run()
90+
.catch((err) => {
91+
console.error(err)
92+
process.exit(1)
14493
})
94+
95+
// function runAsync(round = 12) {
96+
// const asyncHashSuite = new Suite(`Async hash round ${round}`)
97+
// return new Promise((resolve) => {
98+
// asyncHashSuite
99+
// .add('@node-rs/bcrypt', {
100+
// defer: true,
101+
// fn: (deferred) => {
102+
// Promise.all(range(parallel).map(() => napiHash(password, round))).then(() => {
103+
// deferred.resolve()
104+
// })
105+
// },
106+
// })
107+
// .add('node bcrypt', {
108+
// defer: true,
109+
// fn: (deferred) => {
110+
// Promise.all(range(parallel).map(() => hash(password, round))).then(() => {
111+
// deferred.resolve()
112+
// })
113+
// },
114+
// })
115+
// .add('bcryptjs', {
116+
// defer: true,
117+
// fn: (deferred) => {
118+
// Promise.all(range(parallel).map(() => hashJs(password, round))).then(() => {
119+
// deferred.resolve()
120+
// })
121+
// },
122+
// })
123+
// .on('cycle', function (event) {
124+
// event.target.hz = event.target.hz * parallel
125+
// console.info(String(event.target))
126+
// })
127+
// .on('complete', function () {
128+
// console.info(`${this.name} bench suite: Fastest is ${chalk.green(this.filter('fastest').map('name'))}`)
129+
// resolve()
130+
// })
131+
// .run({ async: true })
132+
// })
133+
// }
134+
135+
// runAsync()
136+
// .then(
137+
// () =>
138+
// new Promise((resolve) => {
139+
// const suite = new Suite('Async verify')
140+
// const hash = napiHashSync(password)
141+
// suite
142+
// .add({
143+
// name: '@node-rs/bcrypt',
144+
// defer: true,
145+
// fn: (deferred) => {
146+
// Promise.all(range(parallel).map(() => verify(password, hash))).then(() => {
147+
// deferred.resolve()
148+
// })
149+
// },
150+
// })
151+
// .add({
152+
// name: 'node bcrypt',
153+
// defer: true,
154+
// fn: (deferred) => {
155+
// Promise.all(range(parallel).map(() => compare(password, hash))).then(() => {
156+
// deferred.resolve()
157+
// })
158+
// },
159+
// })
160+
// .add({
161+
// name: 'bcryptjs',
162+
// defer: true,
163+
// fn: (deferred) => {
164+
// Promise.all(range(parallel).map(() => compareJs(password, hash))).then(() => {
165+
// deferred.resolve()
166+
// })
167+
// },
168+
// })
169+
// .on('cycle', function (event) {
170+
// event.target.hz = event.target.hz * parallel
171+
// console.info(String(event.target))
172+
// })
173+
// .on('complete', function () {
174+
// resolve()
175+
// console.info(`${this.name} bench suite: Fastest is ${chalk.green(this.filter('fastest').map('name'))}`)
176+
// })
177+
// .run()
178+
// }),
179+
// )
180+
// .then(() => {
181+
// return new Promise((resolve) => {
182+
// const syncHashSuite = new Suite(`Hash round 12`)
183+
// syncHashSuite
184+
// .add('@node-rs/bcrypt', () => {
185+
// napiHashSync(password, 12)
186+
// })
187+
// .add('node bcrypt', () => {
188+
// hashSync(password, 12)
189+
// })
190+
// .add('bcryptjs', () => {
191+
// hashSyncJs(password, 12)
192+
// })
193+
// .on('cycle', function (event) {
194+
// console.info(String(event.target))
195+
// })
196+
// .on('complete', function () {
197+
// console.info(`${this.name} bench suite: Fastest is ${chalk.green(this.filter('fastest').map('name'))}`)
198+
// resolve()
199+
// })
200+
// .run()
201+
// })
202+
// })
203+
// .then(() => {
204+
// return new Promise((resolve) => {
205+
// new Suite('genSaltSync')
206+
// .add('@node-rs/bcrypt', () => {
207+
// napiGenSaltSync(10, '2b')
208+
// })
209+
// .add('node bcrypt', () => {
210+
// genSaltSync(10, 'b')
211+
// })
212+
// .add('bcryptjs', () => {
213+
// genSaltSyncJs(10)
214+
// })
215+
// .on('cycle', function (event) {
216+
// console.info(String(event.target))
217+
// })
218+
// .on('complete', function () {
219+
// console.info(`${this.name} bench suite: Fastest is ${chalk.green(this.filter('fastest').map('name'))}`)
220+
// resolve()
221+
// })
222+
// .run()
223+
// })
224+
// })

packages/bcrypt/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,9 @@
6969
"devDependencies": {
7070
"@types/bcrypt": "^5.0.0",
7171
"bcrypt": "^5.1.0",
72-
"bcryptjs": "^2.4.3"
72+
"bcryptjs": "^2.4.3",
73+
"cross-env": "^7.0.3",
74+
"rxjs": "^7.8.0"
7375
},
7476
"funding": {
7577
"type": "github",

0 commit comments

Comments
 (0)