Skip to content

Commit f550458

Browse files
authored
perf(agera): speed up effect runs by splitting warmup and rerun paths (#178)
Split `runEffect` into small `warmupEffect` and `runEffect` specializations so V8 can inline them into the effect creation and flush paths, and gate the `noMount` and `notifyMounted` lifecycle hooks behind `isMountableUsed`. Effect benchmark improves from ~345 to ~320 ns/op (~7% faster). Bump size limits by 10-20 B in `agera`, `kida` and `store` to account for the duplicated run bodies.
1 parent 31de1b2 commit f550458

5 files changed

Lines changed: 32 additions & 13 deletions

File tree

packages/agera/.size-limit.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"name": "All publics",
44
"path": "dist/index.js",
55
"import": "*",
6-
"limit": "2.46 kB"
6+
"limit": "2.47 kB"
77
},
88
{
99
"name": "Signal",
@@ -21,6 +21,6 @@
2121
"name": "Popular set",
2222
"path": "dist/index.js",
2323
"import": "{ signal, computed, effect, mountable, onMounted }",
24-
"limit": "1.87 kB"
24+
"limit": "1.88 kB"
2525
}
2626
]

packages/agera/src/effect.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ describe('agera', () => {
459459

460460
$num(1)
461461

462-
expect(fn).toHaveBeenCalledWith(undefined)
462+
expect(fn).toHaveBeenLastCalledWith()
463463

464464
stop()
465465
})

packages/agera/src/internals/system.ts

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -523,7 +523,7 @@ export function effect(fn: EffectCallback, noDefer = false): Destroy {
523523
}
524524
}
525525

526-
runEffect(e, true)
526+
warmupEffect(e)
527527

528528
return effectOper.bind(e)
529529
}
@@ -654,22 +654,41 @@ function updateSignal(s: SignalNode): boolean {
654654
return s.value !== (s.value = s.pendingValue)
655655
}
656656

657-
function runEffect(e: EffectNode, warmup?: true): void {
657+
function warmupEffect(e: EffectNode): void {
658658
const prevSub = pushActiveSub(e)
659659
const prevNoMount = isMountableUsed ? pushNoMount(e.noMount) : undefined
660660

661661
try {
662-
e.destroy = e.fn(warmup) || undefined
662+
e.destroy = e.fn(true) || undefined
663663
} finally {
664-
popNoMount(prevNoMount)
665664
popActiveSub(prevSub)
666665
e.flags &= ~RecursedCheckFlag
667666

668-
if (warmup === undefined) {
669-
purgeDeps(e)
667+
if (isMountableUsed) {
668+
popNoMount(prevNoMount)
669+
notifyMounted(activeSub)
670670
}
671+
}
672+
}
671673

672-
notifyMounted(activeSub)
674+
function runEffect(e: EffectNode): void {
675+
const prevSub = pushActiveSub(e)
676+
const prevNoMount = isMountableUsed ? pushNoMount(e.noMount) : undefined
677+
678+
try {
679+
e.destroy = e.fn() || undefined
680+
} finally {
681+
if (isMountableUsed) {
682+
popNoMount(prevNoMount)
683+
}
684+
685+
popActiveSub(prevSub)
686+
e.flags &= ~RecursedCheckFlag
687+
purgeDeps(e)
688+
689+
if (isMountableUsed) {
690+
notifyMounted(activeSub)
691+
}
673692
}
674693
}
675694

@@ -840,7 +859,7 @@ function runDeferredEffects(link: Link): void {
840859
runDeferredEffects(dep.deps)
841860
}
842861
} else {
843-
runEffect(dep as EffectNode, true)
862+
warmupEffect(dep as EffectNode)
844863
}
845864
}
846865

packages/kida/.size-limit.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@
1515
"name": "Popular set",
1616
"path": "dist/index.js",
1717
"import": "{ signal, record, computed, effect, mountable, onMount }",
18-
"limit": "2.21 kB"
18+
"limit": "2.23 kB"
1919
}
2020
]

packages/store/.size-limit.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@
1515
"name": "Popular set",
1616
"path": "dist/index.js",
1717
"import": "{ signal, record, computed, effect, mountable, onMount }",
18-
"limit": "2.21 kB"
18+
"limit": "2.23 kB"
1919
}
2020
]

0 commit comments

Comments
 (0)