Repository navigation
JIT-startsWith and endsWith #711
Copy link
Copy link
Closed
Labels
help wantedExtra attention is neededExtra attention is needed
Description
I wonder if a startsWith JIT makes any sense for this or any project?
true startsWith x 95,549,028 ops/sec ±0.93% (95 runs sampled)
true startsWith jit x 1,409,981,216 ops/sec ±0.23% (95 runs sampled)
true endsWith x 98,114,558 ops/sec ±0.36% (99 runs sampled)
true endsWith jit x 199,258,515 ops/sec ±0.30% (97 runs sampled)
false startsWith x 125,452,965 ops/sec ±0.57% (94 runs sampled)
false startsWith jit x 1,409,183,606 ops/sec ±0.44% (99 runs sampled)
false endsWith x 126,028,596 ops/sec ±0.55% (95 runs sampled)
false endsWith jit x 198,250,186 ops/sec ±0.55% (98 runs sampled)
'use strict'
const assert = require('assert')
const Benchmark = require('benchmark')
const test = 'Lorem Ipsum I dolor'
const startsWithJit = startsWithJitGen('Lorem')
const endsWithJit = endsWithJitGen('dolor')
const Lorem = 'Lorem'
const dolor = 'dolor'
const lorem = 'lorem'
const Dolor = 'Dolor'
assert.ok(startsWithJit('ol') === false)
assert.ok(startsWithJit('or') === false)
assert.ok(startsWithJit('Dolor') === false)
assert.ok(startsWithJit('dolor') === false)
assert.ok(startsWithJit('lorem') === false)
assert.ok(startsWithJit('Lorem') === true)
assert.ok(endsWithJit('ol') === false)
assert.ok(endsWithJit('or') === false)
assert.ok(endsWithJit('Dolor') === false)
assert.ok(endsWithJit('dolor') === true)
new Benchmark.Suite()
.add('true startsWith', function () { test.startsWith(Lorem) })
.add('true startsWith jit', function () { startsWithJit(test) })
.add('true endsWith', function () { test.endsWith(dolor) })
.add('true endsWith jit', function () { endsWithJit(test) })
.add('false startsWith', function () { test.startsWith(lorem) })
.add('false startsWith jit', function () { startsWithJit(test) })
.add('false endsWith', function () { test.endsWith(Dolor) })
.add('false endsWith jit', function () { endsWithJit(test) })
.on('cycle', function (event) { console.log(String(event.target)) })
.run()
function startsWithJitGen(sequence) {
const chain = []
chain.push('(typeof value === \'string\')')
chain.push(`(value.length >= ${sequence.length})`)
for (let i = 0, il = sequence.length; i < il; ++i) {
chain.push(`(value[${i}] === '${sequence[i]}')`)
}
const fnBody = 'return ' + chain.join(' && ')
return new Function('value', fnBody)
}
function endsWithJitGen(sequence) {
const chain = []
chain.push('(typeof value === \'string\')')
chain.push(`(value.length >= ${sequence.length})`)
for (let i = 0, il = sequence.length; i < il; ++i) {
if (i === 0) {
chain.push(`(value[len] === '${sequence[i]}')`)
continue
}
chain.push(`(value[len + ${i}] === '${sequence[i]}')`)
}
const fnBody = `const len = value.length - ${sequence.length}; return ` + chain.join(' && ')
return new Function('value', fnBody)
}
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
help wantedExtra attention is neededExtra attention is needed
Activity
mcollina commentedon Jul 7, 2022
I don't understand the question.
Uzlopak commentedon Jul 7, 2022
Is it worth in your opinion to create a project/package and to integrate into fastify to replace the calls of startsWith and endsWith calls in fastify with my implementation of startswith/endswith?
mcollina commentedon Jul 7, 2022
Do you know if/where startWith is a bottleneck?
jsumners commentedon Jul 7, 2022
I'm uneasy about using
new Function()with arbitrary strings that.startsWithwill be used against.Uzlopak commentedon Jul 8, 2022
Its is actually more about the cases, were the string is not arbitrary like:
https://github.com/fastify/fastify-reply-from/blob/6a31087a14558289dedc11650a3dcbc3dde42c5d/lib/request.js#L63
https://github.com/fastify/fastify-static/blob/ce4b768dda82df6ba41b0b43964a0c7e05fbaeee/index.js#L94
https://github.com/fastify/fastify-vite/blob/6dba5545915834e9b567113d7841daa72bbcc506/packages/fastify-vite/ioutils.js#L8
jsumners commentedon Jul 8, 2022
I don't think the complexity is necessary in any of those cases.
Eomm commentedon Mar 5, 2023
These
endsWithJitGenandstartsWithJitGencould be a nodejs module to use - in order to hide complexity tbhUzlopak commentedon Mar 5, 2023
@Eomm
The performance gain comes imho by avoiding the string parsing every time you call startsWith or endsWith.
E.g. in fastify/busboy we have the regex which is /^--boundary/ or so. So actually we could avoid that regex by using a function which checks the string line above.
gurgunday commentedon Apr 12, 2024
nodejs/performance#159 (comment)
Summary:
indexOfgives similar improvementsMoral of the story: avoid
startsWith/endsWithUzlopak commentedon Apr 12, 2024
This is not correct. indexOf searches the whole string. So imagine 1 mb of string and at the end is the sequence you are looking for. In that case startsWith is faster as it will only check the beginning. The JIT variant is basically faster than startsWith as it does not have to process the "needle" everytime.
gurgunday commentedon Apr 12, 2024
Ok, I corrected it, that's a non issue when we slice
PTAL again