[signia] Smart dirty checking of active computeds (#3516)
This is a huge perf win, and it came to me while procrastinating on making dinner. The idea is that we can skip checking the parents of a computed value if - it is being dereferenced during a reaction cycle - the computed value was not traversed during the current reaction cycle This more than doubles the speed of the webgl minimap render on my machine (from 2ms down to like 0.8ms). This will make the biggest difference for anything that derives a value from a large collection of other computed values where typically only a small amount of them change at one time (e.g. iterating over all the shape page bounds to compile an RBush) Most code paths where we see a big chunk of `haveParentsChanged` in flame graphs should be much faster after this. ### Change Type - [x] `sdk` — Changes the tldraw SDK - [x] `improvement` — Improving existing features
This commit is contained in:
parent
dd0b7b882d
commit
741ed00bda
2 changed files with 14 additions and 3 deletions
|
@ -4,7 +4,7 @@ import { HistoryBuffer } from './HistoryBuffer'
|
|||
import { maybeCaptureParent, startCapturingParents, stopCapturingParents } from './capture'
|
||||
import { GLOBAL_START_EPOCH } from './constants'
|
||||
import { EMPTY_ARRAY, equals, haveParentsChanged, singleton } from './helpers'
|
||||
import { getGlobalEpoch } from './transactions'
|
||||
import { getGlobalEpoch, getIsReacting } from './transactions'
|
||||
import { Child, ComputeDiff, RESET_VALUE, Signal } from './types'
|
||||
import { logComputedGetterWarning } from './warnings'
|
||||
|
||||
|
@ -189,8 +189,15 @@ class __UNSAFE__Computed<Value, Diff = unknown> implements Computed<Value, Diff>
|
|||
__unsafe__getWithoutCapture(ignoreErrors?: boolean): Value {
|
||||
const isNew = this.lastChangedEpoch === GLOBAL_START_EPOCH
|
||||
|
||||
if (!isNew && (this.lastCheckedEpoch === getGlobalEpoch() || !haveParentsChanged(this))) {
|
||||
this.lastCheckedEpoch = getGlobalEpoch()
|
||||
const globalEpoch = getGlobalEpoch()
|
||||
|
||||
if (
|
||||
!isNew &&
|
||||
(this.lastCheckedEpoch === globalEpoch ||
|
||||
(this.isActivelyListening && getIsReacting() && this.lastTraversedEpoch < globalEpoch) ||
|
||||
!haveParentsChanged(this))
|
||||
) {
|
||||
this.lastCheckedEpoch = globalEpoch
|
||||
if (this.error) {
|
||||
if (!ignoreErrors) {
|
||||
throw this.error.thrownValue
|
||||
|
|
|
@ -70,6 +70,10 @@ export function getGlobalEpoch() {
|
|||
return inst.globalEpoch
|
||||
}
|
||||
|
||||
export function getIsReacting() {
|
||||
return inst.globalIsReacting
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect all of the reactors that need to run for an atom and run them.
|
||||
*
|
||||
|
|
Loading…
Reference in a new issue