Skip to content

Commit

Permalink
try faster shallowEqual
Browse files Browse the repository at this point in the history
  • Loading branch information
theKashey committed Apr 28, 2019
1 parent c18ed39 commit fa66cca
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 45 deletions.
108 changes: 108 additions & 0 deletions benchmarks/deepEqual.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/* eslint-disable flowtype/require-valid-file-annotation */
const Benchmark = require('benchmark');
const {proxyShallowEqual, proxyCompare, proxyEqual, deepDeproxify} = require('../');
const {weakMemoizeArray} = require("../lib/weakMemoize");

const suite = new Benchmark.Suite();

const s1 = {
a: 1,
b: 2,
c: 3,
d: [[
1, 2, 3
]],
e: {a: {b: {c: 42}}}
};

const s2 = s1;

const s3 = Object.assign({}, s1);

const s4 = Object.assign({}, s1, {
e: Object.assign({}, s1.e, {b: {c: 42}})
});

const s5 = Object.assign({}, s1, {
e: Object.assign({}, s1.e, {b: {c: 42}})
});

const left = s1;
const right = s5;

//const locations = [".d", ".d.0", ".d.0.0", ".d.0.1", ".d.0.2"];
const locations = [".e",".e.a",".e.a.b",".a",'.b'];
// const locations = [".a",".b",".c",".cc",".ccc",".cccc",".aa",".bb"];

const get = (target, path) => {
let result = target;
for (let i = 1; i < path.length && result; ++i) {
const key = path[i];
result = result[key]
}
return result;
};

const EDGE = 'EDGE';

const buildObjTrie = (lines) => {
const root = {};
for (let i = 0; i < lines.length; ++i) {
const path = lines[i].split('.');
let node = root;
const lastIndex = path.length - 1;
for (let j = 1; j < lastIndex; ++j) {
const item = path[j];
if (!node[item] || node[item] === EDGE) {
node[item] = {};
}
node = node[item];
}
node[path[lastIndex]] = EDGE;
}
return root; // FIXME
};

const memoizedBuildTrie = weakMemoizeArray(buildObjTrie);

const proxyShallowEqual2 = (a, b, affected) => {
const root = memoizedBuildTrie(affected);
const walk = (la, lb, node) => {
if (la === lb || deepDeproxify(la) === deepDeproxify(lb)) {
return true;
}
if (node === EDGE) {
return false;
}
const items = Object.keys(node);
for (let i = 0; i < items.length; ++i) {
const item = items[i];
if (!walk(
get(la, ['', item]), // FIXME
get(lb, ['', item]), // FIXME
node[item],
)) {
return false;
}
}
return true;
};
return walk(a, b, root);
};

suite.add('proxyShallowEqual-0', () => {
proxyShallowEqual(left, right, locations);
});

suite.add('proxyShallowEqual-2', () => {
proxyShallowEqual2(left, right, locations);
});

suite.add('proxyShallowEqual-3', () => {
proxyShallowEqual(left, right, locations);
});


suite.on('cycle', e => console.log(String(e.target)));

suite.run({async: true});
75 changes: 30 additions & 45 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {str as crc32_str} from "crc-32";
import ProxyPolyfill from './proxy-polyfill';
import {getCollectionHandlers, shouldInstrument} from "./shouldInstrument";
import {weakMemoizeArray} from "./weakMemoize";
import {EDGE, memoizedBuildTrie} from "./objectTrie";

const hasProxy = typeof Proxy !== 'undefined';
const ProxyConstructor = hasProxy ? Proxy : ProxyPolyfill();
Expand All @@ -28,7 +29,7 @@ const isProxyfied = object => object && typeof object === 'object' ? ProxyToStat

const deproxify = (object) => object && typeof object === 'object' ? ProxyToState.get(object) : object || object;

const deepDeproxify = (object) => {
export const deepDeproxify = (object) => {
if (object && typeof object === 'object') {
let current = object;
while (ProxyToState.has(current)) {
Expand Down Expand Up @@ -285,63 +286,47 @@ const proxyCompare = (a, b, locations) => {
return ret;
};

const nestedSort = (a, b) => {
for (let i = 0; ; ++i) {
const ac = a.charCodeAt(i);
const bc = b.charCodeAt(i);
if (!ac && !bc) return 0;
if (ac && !bc) return 1;
if (!ac && bc) return -1;
if (ac > bc) return 1;
if (ac < bc) return -1;
}
};

const sortedLocations = locations => [...locations].sort(nestedSort);

const memoizedSortedLocations = weakMemoizeArray(sortedLocations);
const getterHelper = ['',''];

const proxyShallowEqual = (a, b, locations) => {
DISABLE_ALL_PROXIES = true;
const differ = [];
differs = [];
const ret = (() => {
differs = [];
let valuables = null;
const nestedLocations = memoizedSortedLocations(locations);
let lastEqualKey = '';
for (let i = 0; i < nestedLocations.length; ++i) {
const key = nestedLocations[i];
if (
lastEqualKey &&
key.length > lastEqualKey.length &&
key[lastEqualKey.length] === '.' &&
key.indexOf(lastEqualKey) === 0
) {
continue;
const root = memoizedBuildTrie(locations);
const walk = (la, lb, node) => {
if (la === lb || deepDeproxify(la) === deepDeproxify(lb)) {
return true;
}

const path = key.split('.');
const la = get(a, path);
const lb = get(b, path);

if ((la === lb) || (deepDeproxify(la) === deepDeproxify(lb))) {
lastEqualKey = key;
} else {
if (!valuables) {
valuables = memoizedCollectValuables(locations);
}
if (valuables.indexOf(key) >= 0) {
differs.push([key, 'not equal']);
if (node === EDGE) {
return false;
}
const items = Object.keys(node);
for (let i = 0; i < items.length; ++i) {
const item = items[i];
getterHelper[1]=item;
if (!walk(
get(la, getterHelper),
get(lb, getterHelper),
node[item],
)) {
differ.unshift(item);
return false;
}
}
}

return true;
return true;
};
return walk(a, b, root);
})();
DISABLE_ALL_PROXIES = false;
if(!ret) {
differ.unshift('');
differs.push([differ.join('.'), 'not equal']);
}
return ret;
};


const proxyEqual = (a, b, affected) => {
differs = [];
return proxyCompare(a, b, memoizedCollectValuables(affected));
Expand Down
23 changes: 23 additions & 0 deletions src/objectTrie.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {weakMemoizeArray} from "./weakMemoize";

export const EDGE = 'EDGE';

const buildObjTrie = (lines) => {
const root = {};
for (let i = 0; i < lines.length; ++i) {
const path = lines[i].split('.');
let node = root;
const lastIndex = path.length - 1;
for (let j = 1; j < lastIndex; ++j) {
const item = path[j];
if (!node[item] || node[item] === EDGE) {
node[item] = {};
}
node = node[item];
}
node[path[lastIndex]] = EDGE;
}
return root; // FIXME
};

export const memoizedBuildTrie = weakMemoizeArray(buildObjTrie);

0 comments on commit fa66cca

Please sign in to comment.