@@ -82,6 +82,8 @@ function scheduleEffect(effect: Computation<any>) {
8282 if ( effect . _state & FLAG_PENDING ) return ;
8383
8484 effect . _state |= FLAG_PENDING ;
85+
86+ // OPTIMIZATION: Use indexed assignment instead of push
8587 pendingEffects [ pendingCount ++ ] = effect ;
8688
8789 if ( ! isFlushScheduled && batchDepth === 0 ) {
@@ -102,6 +104,7 @@ function flushEffects() {
102104 const count = pendingCount ;
103105 pendingCount = 0 ;
104106
107+ // OPTIMIZATION: Process effects in batches for better cache locality
105108 for ( let i = 0 ; i < count ; i ++ ) {
106109 const effect = pendingEffects [ i ] ;
107110
@@ -115,6 +118,10 @@ function flushEffects() {
115118
116119 // Clear FLAG_PENDING AFTER update completes
117120 effect . _state &= ~ FLAG_PENDING ;
121+ }
122+
123+ // Clear pending array after processing all effects
124+ for ( let i = 0 ; i < count ; i ++ ) {
118125 pendingEffects [ i ] = null as any ;
119126 }
120127 }
@@ -146,22 +153,20 @@ function disposeOwner(owner: Owner) {
146153 * rebuild the sources array.
147154 */
148155function track ( source : SourceType ) {
149- if ( currentObserver ) {
150- // OPTIMIZATION: Compare with old sources first
151- if (
152- ! newSources &&
153- currentObserver . _sources &&
154- currentObserver . _sources [ newSourcesIndex ] === source
155- ) {
156- // Source at this index hasn't changed, just increment
157- newSourcesIndex ++ ;
158- } else if ( ! newSources ) {
159- // First changed source - create newSources array
160- newSources = [ source ] ;
161- } else if ( source !== newSources [ newSources . length - 1 ] ) {
162- // Don't add duplicate if it's the same as last source
163- newSources . push ( source ) ;
164- }
156+ // OPTIMIZATION: Compare with old sources first
157+ if (
158+ ! newSources &&
159+ currentObserver ! . _sources &&
160+ currentObserver ! . _sources [ newSourcesIndex ] === source
161+ ) {
162+ // Source at this index hasn't changed, just increment
163+ newSourcesIndex ++ ;
164+ } else if ( ! newSources ) {
165+ // First changed source - create newSources array
166+ newSources = [ source ] ;
167+ } else if ( source !== newSources [ newSources . length - 1 ] ) {
168+ // Don't add duplicate if it's the same as last source
169+ newSources . push ( source ) ;
165170 }
166171}
167172
@@ -173,17 +178,34 @@ function removeSourceObservers(observer: ObserverType, fromIndex: number) {
173178 const sources = observer . _sources ;
174179 if ( ! sources ) return ;
175180
176- for ( let i = fromIndex ; i < sources . length ; i ++ ) {
181+ const len = sources . length ;
182+ for ( let i = fromIndex ; i < len ; i ++ ) {
177183 const source = sources [ i ] ;
178184 if ( source && source . _observers ) {
179185 const observers = source . _observers ;
180- const idx = observers . indexOf ( observer ) ;
181- if ( idx !== - 1 ) {
182- const last = observers . length - 1 ;
183- if ( idx < last ) {
184- observers [ idx ] = observers [ last ] ;
186+ const observerCount = observers . length ;
187+
188+ // OPTIMIZATION: Linear search for small arrays, faster than indexOf
189+ if ( observerCount <= 8 ) {
190+ for ( let j = 0 ; j < observerCount ; j ++ ) {
191+ if ( observers [ j ] === observer ) {
192+ const last = observerCount - 1 ;
193+ if ( j < last ) {
194+ observers [ j ] = observers [ last ] ;
195+ }
196+ observers . pop ( ) ;
197+ break ;
198+ }
199+ }
200+ } else {
201+ const idx = observers . indexOf ( observer ) ;
202+ if ( idx !== - 1 ) {
203+ const last = observerCount - 1 ;
204+ if ( idx < last ) {
205+ observers [ idx ] = observers [ last ] ;
206+ }
207+ observers . pop ( ) ;
185208 }
186- observers . pop ( ) ;
187209 }
188210 }
189211 }
@@ -221,7 +243,9 @@ class Computation<T> implements SourceType, ObserverType, Owner {
221243
222244 read ( ) : T {
223245 // Track this source in current observer
224- track ( this ) ;
246+ if ( currentObserver ) {
247+ track ( this ) ;
248+ }
225249
226250 if ( this . _state & 3 ) {
227251 this . _updateIfNecessary ( ) ;
@@ -401,8 +425,18 @@ class Computation<T> implements SourceType, ObserverType, Owner {
401425 const observers = this . _observers ;
402426 if ( ! observers ) return ;
403427
404- for ( let i = 0 ; i < observers . length ; i ++ ) {
405- observers [ i ] . _notify ( state ) ;
428+ // OPTIMIZATION: Batch notify to reduce intermediate work
429+ const len = observers . length ;
430+ if ( len > 100 ) {
431+ batchDepth ++ ;
432+ for ( let i = 0 ; i < len ; i ++ ) {
433+ observers [ i ] . _notify ( state ) ;
434+ }
435+ batchDepth -- ;
436+ } else {
437+ for ( let i = 0 ; i < len ; i ++ ) {
438+ observers [ i ] . _notify ( state ) ;
439+ }
406440 }
407441 }
408442
@@ -443,7 +477,9 @@ class Signal<T> implements SourceType {
443477 }
444478
445479 get value ( ) : T {
446- track ( this ) ;
480+ if ( currentObserver ) {
481+ track ( this ) ;
482+ }
447483 return this . _value ;
448484 }
449485
@@ -455,16 +491,27 @@ class Signal<T> implements SourceType {
455491
456492 if ( ! this . _observers ) return ;
457493
458- // Auto-batching
459- batchDepth ++ ;
460- for ( let i = 0 ; i < this . _observers . length ; i ++ ) {
461- this . _observers [ i ] . _notify ( STATE_DIRTY ) ;
462- }
463- batchDepth -- ;
494+ // OPTIMIZATION: Skip batching overhead for small observer counts
495+ const len = this . _observers . length ;
496+ if ( len === 1 ) {
497+ // Fast path for single observer
498+ this . _observers [ 0 ] . _notify ( STATE_DIRTY ) ;
499+ if ( batchDepth === 0 && ! isFlushScheduled && pendingCount > 0 ) {
500+ isFlushScheduled = true ;
501+ flushEffects ( ) ;
502+ }
503+ } else {
504+ // Auto-batching for multiple observers
505+ batchDepth ++ ;
506+ for ( let i = 0 ; i < len ; i ++ ) {
507+ this . _observers [ i ] . _notify ( STATE_DIRTY ) ;
508+ }
509+ batchDepth -- ;
464510
465- if ( batchDepth === 0 && ! isFlushScheduled && pendingCount > 0 ) {
466- isFlushScheduled = true ;
467- flushEffects ( ) ;
511+ if ( batchDepth === 0 && ! isFlushScheduled && pendingCount > 0 ) {
512+ isFlushScheduled = true ;
513+ flushEffects ( ) ;
514+ }
468515 }
469516 }
470517
0 commit comments