Skip to content

JIT startsWith and endsWith #159

@Uzlopak

Description

@Uzlopak

I proposed this already in fastify/help long time ago, but maybe it has a use in nodejs core?

fastify/help#711

true startsWith x 57,424,304 ops/sec ±0.61% (95 runs sampled)
true startsWith jit x 1,359,500,378 ops/sec ±0.12% (99 runs sampled)
true endsWith x 55,539,779 ops/sec ±0.73% (93 runs sampled)
true endsWith jit x 136,694,982 ops/sec ±0.92% (95 runs sampled)
false startsWith x 208,705,495 ops/sec ±0.52% (97 runs sampled)
false startsWith jit x 1,360,481,699 ops/sec ±0.15% (97 runs sampled)
false endsWith x 208,323,815 ops/sec ±0.83% (93 runs sampled)
false endsWith jit x 135,154,350 ops/sec ±0.74% (87 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)
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions