-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Compatibility with external deep proxies #39
Conversation
use a one liner to clone the object where possible
- changed _treeMetadataMap to use the unproxified version of the tree, instead of proxified - merged _parenthoodMap into _treeMetadataMap - added a new symbol _metadataSymbol used to access the metadata from the proxified object, but only if you have a reference to the symbol
skip generation of a replace operation, if an object within the tree is replaced with a proxified version of itself by external code
Could you add a test for creating external deep proxy? |
There is a test in the commit c461b49
Consider this. There are two ways of accessing an object:
All places in JSONPatcherProxy where there was a need to use This is because in the ES Proxy world, the proxy instances have different identity than the original object: var obj = {};
var p1 = new Proxy(obj, {});
var p2 = new Proxy(p1, {});
obj === p1; //false
p1 === p2; //false The method used in this PR works by recognizing the object already proxified by an instance of JSONPatcherProxy not by keeping a reference to it, but by marking it from the inside: var obj = {};
var meta = {};
var symb = Symbol("Symbol for getting the tree metadata from external access.");
obj[symb] = meta;
var p1 = new Proxy(obj, {});
var p2 = new Proxy(p1, {});
obj[symb] === p1[symb]; //true
p1[symb] === p2[symb]; //true |
Sorry my bad Thanks, now I get why having Map of object references will not work. I'm a little bit picky, as I remember we used to have implementation that was using |
Warning, this commit changes the performance of the benchmark. Generation of tested object is now 2x faster. This is good for the benchmark, because cheaper object generation allows the benchmark to be more sensitive in catching the differences in the performance of JSONPatcherProxy. Just remember that the results from before and after this commit are not comparable. Benchmark results before this commit ============================= jsonpatcherproxy generate operation Ops/sec: 132404 ±2.55% Ran 8276 times in 0.063 seconds. jsonpatch generate operation Ops/sec: 106585 ±6.73% Ran 6434 times in 0.060 seconds. jsonpatcherproxy mutation - huge object Ops/sec: 906 ±2.45% Ran 51 times in 0.056 seconds. jsonpatch mutation - huge object Ops/sec: 862 ±1.52% Ran 47 times in 0.055 seconds. jsonpatcherproxy generate operation - huge object Ops/sec: 811 ±1.99% Ran 46 times in 0.057 seconds. jsonpatch generate operation - huge object Ops/sec: 847 ±1.20% Ran 47 times in 0.055 seconds. PROXIFY big object Ops/sec: 1813 ±2.00% Ran 102 times in 0.056 seconds. DIRTY-OBSERVE big object Ops/sec: 1625 ±1.48% Ran 91 times in 0.056 seconds. Benchmark results after this commit ============================= jsonpatcherproxy generate operation Ops/sec: 136915 ±2.46% Ran 8566 times in 0.063 seconds. jsonpatch generate operation Ops/sec: 104113 ±7.19% Ran 6330 times in 0.061 seconds. jsonpatcherproxy mutation - huge object Ops/sec: 1960 ±3.53% Ran 130 times in 0.066 seconds. jsonpatch mutation - huge object Ops/sec: 1607 ±1.84% Ran 88 times in 0.055 seconds. jsonpatcherproxy generate operation - huge object Ops/sec: 1562 ±3.40% Ran 101 times in 0.065 seconds. jsonpatch generate operation - huge object Ops/sec: 1620 ±2.04% Ran 90 times in 0.056 seconds. PROXIFY big object Ops/sec: 3968 ±4.07% Ran 294 times in 0.074 seconds. DIRTY-OBSERVE big object Ops/sec: 3285 ±1.83% Ran 180 times in 0.055 seconds.
the benchmark methods are almost exactly the same, but I have made the following changes: - group benchmarks by the topic - give more meaningful titles - wherever the preparation code is not meaningful for the benchmark, move it outside the benchmark - use code blocks to give scope to preparation variables this is a change that makes the benchmark results incomparable with the previous versions. however, it will work if you copy this file onto an older version and run it.
as requested in #39 (comment) the benchmark simply serializes the whole object, which triggers the "get" trap on every subtree
I made a benchmark for reads, see my last 4 commits. You are right, this PR introduces about 25% performance degradation in reads. We should care about this, because performance in reads is already terrible compared to lack of proxy (see full results below). Anyway, I think that a fix for the problem #35 is absolutely necessary. Do you have any other suggestions? Before this PRObserve and generate, small object (JSONPatcherProxy)
Ops/sec: 94422 ±2.81% Ran 5965 times in 0.063 seconds.
Observe and generate (JSONPatcherProxy)
Ops/sec: 1924 ±2.29% Ran 190 times in 0.099 seconds.
Primitive mutation (JSONPatcherProxy)
Ops/sec: 480043 ±2.65% Ran 27032 times in 0.056 seconds.
Complex mutation (JSONPatcherProxy)
Ops/sec: 6996 ±5.30% Ran 421 times in 0.060 seconds.
Serialization (JSONPatcherProxy)
Ops/sec: 1252 ±0.77% Ran 66 times in 0.053 seconds. After this PRObserve and generate, small object (JSONPatcherProxy)
Ops/sec: 94633 ±2.61% Ran 5925 times in 0.063 seconds.
Observe and generate (JSONPatcherProxy)
Ops/sec: 2010 ±2.06% Ran 194 times in 0.097 seconds.
Primitive mutation (JSONPatcherProxy)
Ops/sec: 414442 ±1.92% Ran 22812 times in 0.055 seconds.
Complex mutation (JSONPatcherProxy)
Ops/sec: 6106 ±4.07% Ran 350 times in 0.057 seconds.
Serialization (JSONPatcherProxy)
Ops/sec: 935 ±0.82% Ran 49 times in 0.052 seconds. Full results after this PRObserve and generate, small object (noop)
Ops/sec: 12577915 ±0.20% Ran 641041 times in 0.051 seconds.
Observe and generate, small object (JSONPatcherProxy)
Ops/sec: 92129 ±2.62% Ran 5823 times in 0.063 seconds.
Observe and generate, small object (fast-json-patch)
Ops/sec: 69657 ±1.80% Ran 3888 times in 0.056 seconds.
Observe and generate (noop)
Ops/sec: 160466 ±0.26% Ran 8364 times in 0.052 seconds.
Observe and generate (JSONPatcherProxy)
Ops/sec: 1958 ±1.39% Ran 236 times in 0.121 seconds.
Observe and generate (fast-json-patch)
Ops/sec: 1698 ±1.10% Ran 93 times in 0.055 seconds.
Primitive mutation (noop)
Ops/sec: 2549711 ±0.60% Ran 130739 times in 0.051 seconds.
Primitive mutation (JSONPatcherProxy)
Ops/sec: 403460 ±3.16% Ran 22659 times in 0.056 seconds.
Primitive mutation (fast-json-patch)
Ops/sec: 7835 ±0.20% Ran 401 times in 0.051 seconds.
Complex mutation (noop)
Ops/sec: 6551384 ±0.20% Ran 331525 times in 0.051 seconds.
Complex mutation (JSONPatcherProxy)
Ops/sec: 6035 ±6.15% Ran 354 times in 0.059 seconds.
Complex mutation (fast-json-patch)
Ops/sec: 7707 ±0.62% Ran 406 times in 0.053 seconds.
Serialization (noop)
Ops/sec: 7206 ±0.41% Ran 375 times in 0.052 seconds.
Serialization (JSONPatcherProxy)
Ops/sec: 948 ±0.35% Ran 50 times in 0.053 seconds.
Serialization (fast-json-patch)
Ops/sec: 7101 ±0.40% Ran 373 times in 0.053 seconds. |
by default such comparison will be excluded. Benefits of this: - faster execution - shorter report to copy to PRs
Have you tried
|
I agree it's necessary to fix that problem over the performance, but we should do our best not to degrade it too much. Therefore if the idea of keeping a map of parent+key does not work, let's merge it. |
to make functionality equal to fast-json-patch'es
I fixed the After this PRObserve and generate, small object (JSONPatcherProxy)
Ops/sec: 95913 ±2.54% Ran 5936 times in 0.062 seconds.
Observe and generate (JSONPatcherProxy)
Ops/sec: 2008 ±1.49% Ran 210 times in 0.105 seconds.
Primitive mutation (JSONPatcherProxy)
Ops/sec: 436081 ±0.48% Ran 22251 times in 0.051 seconds.
Complex mutation (JSONPatcherProxy)
Ops/sec: 7526 ±0.37% Ran 387 times in 0.051 seconds.
Serialization (JSONPatcherProxy)
Ops/sec: 916 ±0.29% Ran 47 times in 0.051 seconds. Some numbers acutally went up, because before this commit, the queue of recorded patches was growing infinitely. Now, the queue is flushed after each repetition. |
I pushed an additional test to check if double proxification by |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, we just need to keen an eye on performance of getters, and head open for new solution ideas.
I am not merging this until we find it absolutely necessary for view binding. One thing that I find very annoyning about this change is how it affects debugging. Any attempt to read a property from the observed object causes the debugger to step into the |
as requested in #39 (comment) the benchmark simply serializes the whole object, which triggers the "get" trap on every subtree
I made a new PR that contains only the test refactorings from the current PR: #42 |
This PR was rebased on current |
Fixes #35 through refactoring of the metadata access layer and adding a new check to the trap of "set".
Benchmark results
Before this PR
After this PR