This example presents a huge performance drop between React 16.2 and React 16.3.
In investigated it and noticed several things:
It is not a big problem for me, but it looks weird, so I decided to share it on Twitter, then @gaearon invited me to submit an issue about it.
Can you turn this into a smaller self-contained example without third party libs?
I will try to do it but right now I don鈥檛 have time, I think doing a setState every 16ms on 20 components in parallel should be sufficient to make it slow.
Note that if you鈥檙e using the StrictMode component, this is expected since it double renders. If you鈥檙e not, then it is not expected and probably a bug.
Here is a fairly simple comparison. Exactly the same code with different react versions:
Profile of the example provided by @alshakero
Looks like scheduling is somehow off and leaves idle gaps?
We're spending quite a bit more time in performUnitOfWork
(which is the hottest path).
I think this line might be the culprit.
Want to try commenting that line out in the bundle and see if it helps?
@gaearon yep, this is it.
Good. I wonder why. Is it the allocation, the enumeration over a fiber, or changing hidden class of the target itself.
Can you try to set stashedWorkInProgressProperties
to {}
instead of null
here and then do
Object.assign(stashedWorkInProgressProperties, workInProgress);
instead and see if it鈥檚 still as bad?
stashedWorkInProgressProperties
is undefined there, so this errors out.
EDIT: Oh, I see what you want to check. Gimme a sec.
@gaearon
if (true && replayFailedUnitOfWorkWithInvokeGuardedCallback) {
allocStashed();
copyWIP();
}
function allocStashed() {
stashedWorkInProgressProperties = {};
}
function copyWIP() {
Object.assign(stashedWorkInProgressProperties, workInProgress);
}
Seems like it's still quite a lot. I bet we need to preserve the hidden class and explicitly write out all assignments.
Hm, maybe it's possible to write a generic function to do that based on type annotations? blazingFastClone
@gaearon an ad hoc confirmation that hidden class is the issue:
if (true && replayFailedUnitOfWorkWithInvokeGuardedCallback) {
stashedWorkInProgressProperties = createFiber(
workInProgress.tag,
workInProgress.pendingProps,
workInProgress.key,
workInProgress.mode
);
}
I forgot to actually copy.
if (true && replayFailedUnitOfWorkWithInvokeGuardedCallback) {
stashedWorkInProgressProperties = createFiber(
workInProgress.tag,
workInProgress.pendingProps,
workInProgress.key,
workInProgress.mode
);
Object.assign(stashedWorkInProgressProperties, workInProgress);
}
This is still slow, but faster than with stashedWorkInProgressProperties = {}
.
I tried locally and handwritten version seems pretty fast.
Tested on the original example and it is much better, thanks!
Fixed in 16.3.1.
Most helpful comment
Fixed in 16.3.1.