luwes / js-diff-benchmark

Simple benchmark for testing your DOM diffing algorithm.
66 stars 5 forks source link

Add 191 bytes implementation #35

Closed dy closed 4 years ago

dy commented 4 years ago

191 bytes spect with better memory/performance profile, deflate-first strategy.

WebReflection commented 4 years ago

This is interesting, but I wonder at this point if you really need two sets, when array.includes works the same as set.has, the array.push works faster than set.add, as no validation is needed, and you wouldn't even need to new Set(b).

However, if you're happy with the two sets, I'm happy to merge this.

WebReflection commented 4 years ago

Example of how I'd write it:

module.exports = (parent, a, b, end = null) => {
  let aidx = [], i = 0, cur, next, bi

  while (bi = a[i++]) b.includes(bi) ? (aidx.push(bi), cur = cur || bi) : parent.removeChild(bi)
  cur = cur || end, i = 0

  while (bi = b[i++]) {
    next = cur.nextSibling

    // skip
    if (cur == bi) cur = next

    else {
      // swap
      if (b[i] === next && aidx.includes(bi)) cur = next

      // insert
      parent.insertBefore(bi, cur)
    }
  }

  return b
}
WebReflection commented 4 years ago

P.S. either ways, accessing an out of bound index would forever de-optimize the array. The entry array a can probably be de-optimized without issues, but I'd really consider using the b.length instead of looping via b[i++], as the b is the array that you return, and the faster it works, the better.

dy commented 4 years ago

I noticed that a.includes or b.includes performs poorly on 10k swap/create, compared to Set, also that forces a to be an Array, not NodesList.

One ridiculous discovery though is that aidx is not needed, so it allowed reducing size to 177b with better performance/memory. The gained space can be used for append-only shortcut (prominent case!), but I wonder if there's a way to generalize it.

dy commented 4 years ago

UPDATE. Switched to no Sets inflate-first strategy (that turns out to be even more compact - 168bytes), but added fast shortcuts for the saved space. Now 208b, but performs better.

WebReflection commented 4 years ago

this is actually how I would write this:

module.exports = (parent, a, b, end = null) => {
  let aLength = a.length, bLength = b.length, i = 0, cur, next, bi;
  while (i < aLength && i < bLength && a[i] === b[i]) i++;
  if (i == aLength)
    while (i < bLength)
      parent.insertBefore(b[i++], end);
  else {
    // in no circumstances a[i] can be falsy here
    cur = a[i];
    while (i < bLength) {
      bi = b[i++];
      next = cur ? cur.nextSibling : end;
      if (cur == bi)
        cur = next;
      else if (i < bLength && b[i] == next) {
        parent.replaceChild(bi, cur);
        cur = next;
      }
      else
        parent.insertBefore(bi, cur);
    }
    while (cur != end) {
      // in no circumstances `cur` can be falsy here ...
      next = cur.nextSibling || end;
      // ... or this would've failed, if `cur` was falsy
      parent.removeChild(cur);
      cur = next;
    }
  }
};