new Map([[fiber, expirationTime]]) is 7x slower than Chrome, affects scheduleWork on TodoMVC-React-Redux
Categories
(Core :: JavaScript Engine, defect, P1)
Tracking
()
| Tracking | Status | |
|---|---|---|
| firefox135 | --- | fixed |
People
(Reporter: mstange, Assigned: jandem)
References
(Blocks 3 open bugs)
Details
(Whiteboard: [sp3][js-perf-next])
Attachments
(5 files)
After the work in bug 1851662, creating Map and Set objects has mostly disappeared from the sp3 comparison reports.
The one remaining instance I found was scheduleWork on TodoMVC-React-Redux, which does new Map([[fiber, expirationTime]]):
function scheduleWork(fiber, expirationTime) {
if (50 < nestedUpdateCount) throw nestedUpdateCount = 0, rootWithNestedUpdates = null, Error(formatProdErrorMessage(185));
fiber = markUpdateTimeFromFiberToRoot(fiber, expirationTime);
if (null !== fiber) {
var priorityLevel = getCurrentPriorityLevel();
1073741823 === expirationTime ? (executionContext & LegacyUnbatchedContext) !== NoContext && (executionContext & (RenderContext | CommitContext)) === NoContext ? performSyncWorkOnRoot(fiber) : (ensureRootIsScheduled(fiber), executionContext === NoContext && flushSyncCallbackQueue()) : ensureRootIsScheduled(fiber);
(executionContext & 4) === NoContext || 98 !== priorityLevel && 99 !== priorityLevel || (null === rootsWithPendingDiscreteUpdates ? rootsWithPendingDiscreteUpdates = new Map([[fiber, expirationTime]]) : (priorityLevel = rootsWithPendingDiscreteUpdates.get(fiber), (void 0 === priorityLevel || priorityLevel > expirationTime) && rootsWithPendingDiscreteUpdates.set(fiber, expirationTime)));
}
}
Profiles:
Firefox: https://share.firefox.dev/3B0jqjO
Chrome: https://share.firefox.dev/4eJAbhb (7x faster)
Fixing this difference would improve Firefox's score on TodoMVC-React-Redux by 1.2% and Firefox's overall sp3 score by 0.1%.
| Reporter | ||
Updated•1 year ago
|
| Reporter | ||
Updated•1 year ago
|
Updated•1 year ago
|
| Assignee | ||
Comment 1•1 year ago
|
||
Calling Map/Set with an argument is exactly the case I didn't fully optimize yet :) Some thoughts:
-
The
Setconstructor has a fast path for packed arrays where it avoids calling from C++ into self-hosted code. We should probably do something similar forMap. At least if the length is small (like in this case) then checkingIsPackedArrayfor each element is definitely much faster than calling into self-hosted code. -
Ideally we'd also do the
Map/Setobject allocation in JIT code in this case. Then we could do a VM call to either initialize the object in C++, or return a value that means "call the self-hostedMapConstructorInitorSetConstructorInitfunction directly from JIT code".
I'll try to get to this the coming weeks. It's probably pretty common and 1.2% on React-Redux is worth it.
| Assignee | ||
Updated•1 year ago
|
| Assignee | ||
Comment 2•1 year ago
|
||
I'll post patches soon that implement item 1 and parts of of item 2 from comment 1. I don't think it will make us 7x faster but it's a good start.
Fun fact: I added some logging and the News-Nuxt subtest has a lot of new Set(undefined) calls. With my patches this is almost as fast as a plain new Set(). Vue has some of this too but less than Nuxt.
| Assignee | ||
Comment 3•1 year ago
|
||
This makes it easier to reuse this function for Map and WeakMap objects.
Updated•1 year ago
|
| Assignee | ||
Comment 4•1 year ago
|
||
This is very similar to the existing optimization for Set and WeakSet, but we
have to ensure the array elements are also packed arrays with at least 2 elements.
This avoids calling the self-hosted MapConstructorInit function. In most cases
on Speedometer 3 the iterable argument is an array with a few elements so it's
much faster to handle this in C++.
| Assignee | ||
Comment 5•1 year ago
|
||
| Assignee | ||
Comment 6•1 year ago
|
||
For consistency with WeakSet and Map/Set.
Existing WeakMap jit-tests cover the obvious edge cases.
| Assignee | ||
Comment 7•1 year ago
|
||
It's pretty common to call the constructor with a null or undefined argument and
with this patch we can stay in JIT code in this case.
If the argument is not null or undefined we can still allocate the object in
JIT code but we then call into C++ to do the initialization.
This could be optimized more later by emitting CacheIR guards to replace the
IsOptimizableInitForMapOrSet call.
Updated•1 year ago
|
Updated•1 year ago
|
https://hg.mozilla.org/mozilla-central/rev/bbb14aa1182d
https://hg.mozilla.org/mozilla-central/rev/f4ded77414ee
https://hg.mozilla.org/mozilla-central/rev/5e1d9bc0078f
https://hg.mozilla.org/mozilla-central/rev/04ea59eeadde
https://hg.mozilla.org/mozilla-central/rev/dfe8a8d407c4
https://hg.mozilla.org/mozilla-central/rev/13919e3e2290
Description
•