Skip to content
This repository was archived by the owner on Mar 13, 2018. It is now read-only.

Commit fca8a33

Browse files
committed
more perf work on shared observations
R=arv BUG= Review URL: https://codereview.appspot.com/84840043
1 parent 982e428 commit fca8a33

File tree

1 file changed

+41
-86
lines changed

1 file changed

+41
-86
lines changed

src/observe.js

+41-86
Original file line numberDiff line numberDiff line change
@@ -401,113 +401,84 @@
401401
};
402402
}
403403

404+
/*
405+
* The observedSet abstraction is a perf optimization which reduces the total
406+
* number of Object.observe observations of a set of objects. The idea is that
407+
* groups of Observers will have some object dependencies in common and this
408+
* observed set ensures that each object in the transitive closure of
409+
* dependencies is only observed once. The observedSet acts as a write barrier
410+
* such that whenever any change comes through, all Observers are checked for
411+
* changed values.
412+
*
413+
* Note that this optimization is explicitly moving work from setup-time to
414+
* change-time.
415+
*
416+
* TODO(rafaelw): Implement "garbage collection". In order to move work off
417+
* the critical path, when Observers are closed, their observed objects are
418+
* not Object.unobserve(d). As a result, it's possible that if the observedSet
419+
* is kept open, but some Observers have been closed, it could case "leaks"
420+
* (reprevent otherwise collectable objects from being collected). At some
421+
* point, we should implement incremental "gc" which keeps a list of
422+
* observedSets which may need clean-up and does small amounts of cleanup on a
423+
* timeout until all is clean.
424+
*/
425+
404426
function getObservedObject(observer, object, arrayObserve) {
405427
var dir = observedObjectCache.pop() || newObservedObject();
406428
dir.open(observer);
407429
dir.observe(object, arrayObserve);
408430
return dir;
409431
}
410432

411-
var emptyArray = [];
412433
var observedSetCache = [];
413434

414435
function newObservedSet() {
415-
var observers = [];
416436
var observerCount = 0;
437+
var observers = [];
417438
var objects = [];
418-
var toRemove = emptyArray;
419-
var resetNeeded = false;
420-
var resetScheduled = false;
421439

422440
function observe(obj) {
423441
if (!obj)
424442
return;
425443

426-
var index = toRemove.indexOf(obj);
427-
if (index >= 0) {
428-
toRemove[index] = undefined;
429-
objects.push(obj);
430-
} else if (objects.indexOf(obj) < 0) {
444+
if (objects.indexOf(obj) < 0) {
431445
objects.push(obj);
432446
Object.observe(obj, callback);
433447
}
434448

435449
observe(Object.getPrototypeOf(obj));
436450
}
437451

438-
function reset() {
439-
var objs = toRemove === emptyArray ? [] : toRemove;
440-
toRemove = objects;
441-
objects = objs;
442-
443-
var observer;
444-
for (var id in observers) {
445-
observer = observers[id];
446-
if (!observer || observer.state_ != OPENED)
447-
continue;
448-
449-
observer.iterateObjects_(observe);
450-
}
451-
452-
for (var i = 0; i < toRemove.length; i++) {
453-
var obj = toRemove[i];
454-
if (obj)
455-
Object.unobserve(obj, callback);
456-
}
457-
458-
toRemove.length = 0;
459-
}
460-
461-
function scheduledReset() {
462-
resetScheduled = false;
463-
if (!resetNeeded)
464-
return;
465-
466-
reset();
467-
}
468-
469-
function scheduleReset() {
470-
if (resetScheduled)
471-
return;
472-
473-
resetNeeded = true;
474-
resetScheduled = true;
475-
runEOM(scheduledReset);
476-
}
477-
478452
function callback() {
479-
reset();
480-
481453
var observer;
454+
for (var i = 0; i < observers.length; i++) {
455+
observer = observers[i];
456+
if (observer.state_ == OPENED) {
457+
observer.iterateObjects_(observe);
458+
}
459+
}
482460

483-
for (var id in observers) {
484-
observer = observers[id];
485-
if (!observer || observer.state_ != OPENED)
486-
continue;
487-
488-
observer.check_();
461+
for (var i = 0; i < observers.length; i++) {
462+
observer = observers[i];
463+
if (observer.state_ == OPENED) {
464+
observer.check_();
465+
}
489466
}
490467
}
491468

492469
var record = {
493470
object: undefined,
494471
objects: objects,
495472
open: function(obs) {
496-
observers[obs.id_] = obs;
473+
observers.push(obs);
497474
observerCount++;
498475
obs.iterateObjects_(observe);
499476
},
500477
close: function(obs) {
501-
var anyLeft = false;
502-
503-
observers[obs.id_] = undefined;
504478
observerCount--;
505-
506-
if (observerCount) {
507-
scheduleReset();
479+
if (observerCount > 0) {
508480
return;
509481
}
510-
resetNeeded = false;
511482

512483
for (var i = 0; i < objects.length; i++) {
513484
Object.unobserve(objects[i], callback);
@@ -517,8 +488,7 @@
517488
observers.length = 0;
518489
objects.length = 0;
519490
observedSetCache.push(this);
520-
},
521-
reset: scheduleReset
491+
}
522492
};
523493

524494
return record;
@@ -901,37 +871,22 @@
901871
}
902872
}
903873

904-
if (this.directObserver_) {
905-
if (needsDirectObserver) {
906-
this.directObserver_.reset();
907-
return;
908-
}
909-
this.directObserver_.close();
910-
this.directObserver_ = undefined;
911-
return;
912-
}
913-
914874
if (needsDirectObserver)
915875
this.directObserver_ = getObservedSet(this, object);
916876
},
917877

918-
closeObservers_: function() {
878+
disconnect_: function() {
919879
for (var i = 0; i < this.observed_.length; i += 2) {
920880
if (this.observed_[i] === observerSentinel)
921881
this.observed_[i + 1].close();
922882
}
923883
this.observed_.length = 0;
924-
},
925-
926-
disconnect_: function() {
927-
this.value_ = undefined;
884+
this.value_.length = 0;
928885

929886
if (this.directObserver_) {
930887
this.directObserver_.close(this);
931888
this.directObserver_ = undefined;
932889
}
933-
934-
this.closeObservers_();
935890
},
936891

937892
addPath: function(object, path) {
@@ -954,7 +909,7 @@
954909
throw Error('Can only reset while open');
955910

956911
this.state_ = RESETTING;
957-
this.closeObservers_();
912+
this.disconnect_();
958913
},
959914

960915
finishReset: function() {

0 commit comments

Comments
 (0)