Skip to content

Optimize flushChanges via WeakMap<Atom, ArraySet<EffectScheduler>> #73

@justjake

Description

@justjake

This strategy comes from https://github.com/starbeamjs/starbeam

Currently when we write to an Atom, we recursively search the children to notify EffectScheduler subscribers. The problem with this kind of graph traversal in Javascript is that it's not very cache friendly. We can skip some work using the lastTraversedEpoch which effectively memoizes the traversal for already-notified subscribers. The work skipping helps in the best case, but doesn't reduce time spent in the worst case.

My proposed optimization is that we maintain a const ATOM_SUBSCRIBERS = new WeakMap<Atom, ArraySet<EffectScheduler>> which stores a direct mapping from mounted atoms to effects. If we can correctly maintain this set, the notification algorithm becomes strictly O(subscribers) instead of O(graph):

function flushChanges(atoms: Iterable<_Atom<any>>) {
  const reactors = new Set<EffectScheduler<unknown>>()
  for (const atom of atoms) {
    ATOM_SUBSCRIBERS.get(atom)?.forEach(effect => reactors.add(effect))
  }
  reactors.forEach(effect => r.maybeScheduleEffect())
} 

I haven't spent much time on the maintenance algorithm for ATOM_SUBSCRIBERS, but we should be able to do it in stopCapturingParents or similar by doing an upwards crawl of the parent. I don't recall how Starbeam implements it.

Maybe the maintenance time plus the storage space of any required memoize overwhelm the savings, but I think it could be a profitable optimization strategy.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions