Skip to content

Commit

Permalink
fix(core): conditional deps duplication
Browse files Browse the repository at this point in the history
This change improves memory and CPU usage for most cases and has 2-10 times CPU degradation for hundreds and thousands of spy in one atom (such as reducing under atomized array).
  • Loading branch information
artalar committed Oct 25, 2023
1 parent 2b679f0 commit c8968a9
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 18 deletions.
32 changes: 32 additions & 0 deletions packages/core/src/atom.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,38 @@ test('ctx collision', () => {
;`👍` //?
})

test('conditional deps duplication', () => {
const listAtom = atom([1, 2, 3])

const filterAtom = atom<'odd' | 'even'>('odd')

const filteredListAtom = atom((ctx) => {
if (ctx.spy(filterAtom) === 'odd') {
return ctx.spy(listAtom).filter((n) => n % 2 === 1)
} else if (ctx.spy(filterAtom) === 'even') {
return ctx.spy(listAtom).filter((n) => n % 2 === 0)
}
return ctx.spy(listAtom)
})

const ctx = createCtx()

const track = mockFn()

ctx.subscribe(filteredListAtom, track)
assert.equal(track.lastInput(), [1, 3])

filterAtom(ctx, 'even')
assert.equal(track.lastInput(), [2])

filterAtom(ctx, 'odd')
assert.equal(track.lastInput(), [1, 3])

filterAtom(ctx, 'even')
assert.equal(track.lastInput(), [2])
;`👍` //?
})

// test(`maximum call stack`, () => {
// const atoms = new Map<AtomProto, Atom>()
// let i = 0
Expand Down
29 changes: 11 additions & 18 deletions packages/core/src/atom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -337,8 +337,7 @@ export const createCtx = ({

let actualizePubs = (patchCtx: Ctx, patch: AtomCache) => {
let { proto, pubs } = patch
let toDisconnect = new Set<AtomProto>()
let toConnect = new Set<AtomProto>()
let isDepsChanged = false

if (
pubs.length === 0 ||
Expand All @@ -358,19 +357,15 @@ export const createCtx = ({
? pubs[newPubs.length - 1]
: undefined
let isDepChanged = prevDepPatch?.proto !== depPatch.proto

if (isDepChanged) {
if (prevDepPatch) toDisconnect.add(prevDepPatch.proto)
toConnect.add(depProto)
}
isDepsChanged ||= isDepChanged

let state =
depProto.isAction && !isDepChanged
? depPatch.state.slice(prevDepPatch!.state.length)
: depPatch.state

if (cb && (isDepChanged || !Object.is(state, prevDepPatch!.state))) {
if (depProto.isAction) (state as any[]).forEach((call) => cb(call))
if (depProto.isAction) for (const call of state) cb(call)
else cb(state, prevDepPatch?.state)
} else {
return state
Expand All @@ -383,18 +378,16 @@ export const createCtx = ({
patch.state = patch.proto.computer!(patchCtx as CtxSpy, patch.state)
patch.pubs = newPubs

for (let i = newPubs.length; i < pubs.length; i++) {
toDisconnect.add(pubs[i]!.proto)
}

if (toDisconnect.size + toConnect.size && isConnected(patch)) {
for (let depProto of toDisconnect) {
toConnect.has(depProto) ||
if ((isDepsChanged || pubs.length > newPubs.length) && isConnected(patch)) {
for (let { proto: depProto } of pubs) {
if (newPubs.every((dep) => dep.proto !== depProto)) {
disconnect(proto, depProto.patch ?? read(depProto)!)
}
}

for (let depProto of toConnect) {
connect(proto, depProto.patch ?? read(depProto)!)
for (let { proto: depProto } of newPubs) {
if (pubs.every((dep) => dep.proto !== depProto)) {
connect(proto, depProto.patch ?? read(depProto)!)
}
}
}
}
Expand Down

1 comment on commit c8968a9

@vercel
Copy link

@vercel vercel bot commented on c8968a9 Oct 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.