Skip to content

Commit e5912de

Browse files
committed
perf: version based tracking and link reuse
1 parent 66e44b6 commit e5912de

2 files changed

Lines changed: 192 additions & 20 deletions

File tree

packages/rescript-signals/src/signals/Core.res

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ let flag_dirty = 1
66
let flag_pending = 2
77
let flag_running = 4
88

9+
// Global tracking version counter for O(1) duplicate detection
10+
let trackingVersion: ref<int> = ref(0)
11+
912
// Observer kind tag
1013
type kind = [#Effect | #Computed]
1114

@@ -21,6 +24,8 @@ type rec link = {
2124
// Links in the signal's subscriber chain
2225
mutable nextSub: option<link>,
2326
mutable prevSub: option<link>,
27+
// Version stamp for O(1) duplicate detection within a compute cycle
28+
mutable lastTrackedVersion: int,
2429
}
2530

2631
// Signal subscriber list (head/tail of linked list)
@@ -36,6 +41,10 @@ and subs = {
3641
mutable lastDep: option<link>,
3742
mutable flags: int,
3843
mutable level: int,
44+
// Current tracking version for this compute cycle (for link reuse)
45+
mutable currentTrackingVersion: int,
46+
// Whether the last recompute changed the value (for equality short-circuit)
47+
mutable valueChanged: bool,
3948
}
4049

4150
// Observer for effects only (computeds use subs directly)
@@ -50,6 +59,8 @@ and observer = {
5059
name: option<string>,
5160
// For computed observers: direct reference to backing subs (the combined object)
5261
mutable backingSubs: option<subs>,
62+
// Current tracking version for this run cycle (for link reuse)
63+
mutable currentTrackingVersion: int,
5364
}
5465

5566
// Create empty subscriber list (for plain signals)
@@ -62,6 +73,8 @@ let makeSubs = (): subs => {
6273
lastDep: None,
6374
flags: 0,
6475
level: 0,
76+
currentTrackingVersion: 0,
77+
valueChanged: true, // Plain signals always "changed"
6578
}
6679

6780
// Create subs for a computed (with compute function)
@@ -74,6 +87,8 @@ let makeComputedSubs = (compute: unit => unit): subs => {
7487
lastDep: None,
7588
flags: flag_dirty, // start dirty
7689
level: 0,
90+
currentTrackingVersion: 0,
91+
valueChanged: true, // Start as changed for initial computation
7792
}
7893

7994
// Create observer
@@ -93,6 +108,7 @@ let makeObserver = (
93108
level: 0,
94109
name,
95110
backingSubs,
111+
currentTrackingVersion: 0,
96112
}
97113

98114
// Flag operations for observer (using Int.Bitwise module)
@@ -126,6 +142,7 @@ let makeLink = (subs: subs, observer: observer): link => {
126142
prevDep: None,
127143
nextSub: None,
128144
prevSub: None,
145+
lastTrackedVersion: 0,
129146
}
130147

131148
// Add link to signal's subscriber list
@@ -179,6 +196,20 @@ let unlinkFromDeps = (observer: observer, link: link): unit => {
179196
link.nextDep = None
180197
}
181198

199+
// Remove link from subs's dependency list (for computeds - subs IS the observer)
200+
let unlinkFromSubsDeps = (s: subs, link: link): unit => {
201+
switch link.prevDep {
202+
| Some(prev) => prev.nextDep = link.nextDep
203+
| None => s.firstDep = link.nextDep
204+
}
205+
switch link.nextDep {
206+
| Some(next) => next.prevDep = link.prevDep
207+
| None => s.lastDep = link.prevDep
208+
}
209+
link.prevDep = None
210+
link.nextDep = None
211+
}
212+
182213
// Clear all dependencies from observer (unlinks from all signals)
183214
let clearDeps = (observer: observer): unit => {
184215
let link = ref(observer.firstDep)

0 commit comments

Comments
 (0)