You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
There is a really big gotcha in the lodash isEqual example for a custom selector that leads to terrible performance for large data structures.
The root cause is in defaultMemoize, where if the equality check passes, the lastArgs variable isn't updated (code here). The result is that if you have two objects that are deep equal but not shallow equal, lastArgs will never updated but every change will require a deep equality comparison. If the data structure is complex, this is really expensive and actually way worse than just failing the shallow equality check early.
In the pathological case, you could have the dependencies of a selector go something like the following:
null -> a // a is stored
a -> b // a !== b but _.isEqual(a, b) is true, so b is not stored
b -> b // requires deep equality check when shallow would do
b -> b // and again
...
So even if b never changes the entire rest of the time, it will still require deep equality checks every time the store changes.
Is there any reason not to always save the current args to lastArgs? For our case at least, that seems totally reasonable and allows the shallow equality check in _.isEqual to work most of the time.
At the very least, I think this should be noted in the docs. I'm happy to write the copy if that'd be helpful. I'm also happy to open a PR with the change. Here's the custom memoizer we wrote (in ES6), which comes down to defaultMemoize with the aforementioned tiny tweak:
functioncustomMemoize(func,equalityCheck){letlastArgs=null;letlastResult=null;returnfunction(){let_len=arguments.length;letargs=Array(_len);for(let_key=0;_key<_len;_key++){args[_key]=arguments[_key];}consthasNotChanged=(lastArgs!==null&&lastArgs.length===args.length&&args.every((value,index)=>{returnequalityCheck(value,lastArgs[index]);}));if(!hasNotChanged){lastResult=func(...args);}// Storing this even if hasNotChanged is true is the deviation from `defaultMemoize`lastArgs=args;returnlastResult;};}
The text was updated successfully, but these errors were encountered:
There is a really big gotcha in the lodash isEqual example for a custom selector that leads to terrible performance for large data structures.
The root cause is in
defaultMemoize
, where if the equality check passes, thelastArgs
variable isn't updated (code here). The result is that if you have two objects that are deep equal but not shallow equal,lastArgs
will never updated but every change will require a deep equality comparison. If the data structure is complex, this is really expensive and actually way worse than just failing the shallow equality check early.In the pathological case, you could have the dependencies of a selector go something like the following:
So even if b never changes the entire rest of the time, it will still require deep equality checks every time the store changes.
Is there any reason not to always save the current
args
tolastArgs
? For our case at least, that seems totally reasonable and allows the shallow equality check in_.isEqual
to work most of the time.At the very least, I think this should be noted in the docs. I'm happy to write the copy if that'd be helpful. I'm also happy to open a PR with the change. Here's the custom memoizer we wrote (in ES6), which comes down to
defaultMemoize
with the aforementioned tiny tweak:The text was updated successfully, but these errors were encountered: