Skip to content

Commit 7bf5ab8

Browse files
committed
feat(view_pool): adds a view pool of dehydrated views per protoview.
1 parent 617206b commit 7bf5ab8

File tree

9 files changed

+118
-3
lines changed

9 files changed

+118
-3
lines changed

modules/angular2/src/change_detection/dynamic_change_detector.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,13 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
3939
this.formatters = formatters;
4040

4141
this.values = ListWrapper.createFixedSize(protoRecords.length + 1);
42-
ListWrapper.fill(this.values, uninitialized);
43-
4442
this.changes = ListWrapper.createFixedSize(protoRecords.length + 1);
4543

4644
this.protos = protoRecords;
4745
}
4846

4947
setContext(context:any) {
48+
ListWrapper.fill(this.values, uninitialized);
5049
this.values[0] = context;
5150
}
5251

modules/angular2/src/core/compiler/view.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,17 @@ import {ViewPort} from './viewport';
1515
import {Content} from './shadow_dom_emulation/content_tag';
1616
import {LightDom, DestinationLightDom} from './shadow_dom_emulation/light_dom';
1717
import {ShadowDomStrategy} from './shadow_dom_strategy';
18+
import {ViewPool} from './view_pool';
1819

1920
const NG_BINDING_CLASS = 'ng-binding';
2021
const NG_BINDING_CLASS_SELECTOR = '.ng-binding';
2122
// TODO(tbosch): Cannot use `const` because of Dart.
2223
var NO_FORMATTERS = MapWrapper.create();
2324

25+
// TODO(rado): make this configurable/smarter.
26+
var VIEW_POOL_CAPACITY = 10000;
27+
var VIEW_POOL_PREFILL = 0;
28+
2429
/**
2530
* Const of making objects: http://jsperf.com/instantiate-size-of-object
2631
*/
@@ -268,6 +273,7 @@ export class ProtoView {
268273
rootBindingOffset:int;
269274
isTemplateElement:boolean;
270275
shadowDomStrategy: ShadowDomStrategy;
276+
_viewPool: ViewPool;
271277
constructor(
272278
template:Element,
273279
protoChangeDetector:ProtoChangeDetector,
@@ -284,10 +290,23 @@ export class ProtoView {
284290
? 1 : 0;
285291
this.isTemplateElement = this.element instanceof TemplateElement;
286292
this.shadowDomStrategy = shadowDomStrategy;
293+
this._viewPool = new ViewPool(VIEW_POOL_CAPACITY);
287294
}
288295

289296
// TODO(rado): hostElementInjector should be moved to hydrate phase.
290297
instantiate(hostElementInjector: ElementInjector):View {
298+
if (this._viewPool.length() == 0) this._preFillPool(hostElementInjector);
299+
var view = this._viewPool.pop();
300+
return isPresent(view) ? view : this._instantiate(hostElementInjector);
301+
}
302+
303+
_preFillPool(hostElementInjector: ElementInjector) {
304+
for (var i = 0; i < VIEW_POOL_PREFILL; i++) {
305+
this._viewPool.push(this._instantiate(hostElementInjector));
306+
}
307+
}
308+
309+
_instantiate(hostElementInjector: ElementInjector): View {
291310
var rootElementClone = this.instantiateInPlace ? this.element : DOM.clone(this.element);
292311
var elementsWithBindingsDynamic;
293312
if (this.isTemplateElement) {
@@ -409,6 +428,10 @@ export class ProtoView {
409428
return view;
410429
}
411430

431+
returnToPool(view: View) {
432+
this._viewPool.push(view);
433+
}
434+
412435
static _addNativeEventListener(element: Element, eventName: string, expr: AST, view: View) {
413436
var locals = MapWrapper.create();
414437
var innerCallback = ProtoView.buildInnerCallback(expr, view, locals);
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import {ListWrapper, MapWrapper, StringMapWrapper, List} from 'angular2/src/facade/collection';
2+
import {View} from './view';
3+
4+
export class ViewPool {
5+
_views: List<View>;
6+
_capacity: number;
7+
constructor(capacity: number) {
8+
this._views = [];
9+
this._capacity = capacity;
10+
}
11+
12+
pop(): View {
13+
return ListWrapper.isEmpty(this._views) ? null : ListWrapper.removeLast(this._views);
14+
}
15+
16+
push(view: View) {
17+
if (this._views.length < this._capacity) {
18+
ListWrapper.push(this._views, view);
19+
}
20+
}
21+
22+
length() {
23+
return this._views.length;
24+
}
25+
}
26+

modules/angular2/src/core/compiler/viewport.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ export class ViewPort {
9292
if (atIndex == -1) atIndex = this._views.length - 1;
9393
var view = this.detach(atIndex);
9494
view.dehydrate();
95+
// TODO(rado): this needs to be delayed until after any pending animations.
96+
this.defaultProtoView.returnToPool(view);
9597
// view is intentionally not returned to the client.
9698
}
9799

modules/angular2/src/facade/collection.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ class ListWrapper {
110110
list.remove(items[i]);
111111
}
112112
}
113+
static removeLast(List list) => list.removeLast();
113114
static bool remove(List list, item) => list.remove(item);
114115
static void clear(List l) {
115116
l.clear();

modules/angular2/src/facade/collection.es6

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,9 @@ export class ListWrapper {
149149
list.splice(index, 1);
150150
}
151151
}
152+
static removeLast(list:List) {
153+
return list.pop();
154+
}
152155
static remove(list, el): boolean {
153156
var index = list.indexOf(el);
154157
if (index > -1) {
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import {describe, xit, it, expect, beforeEach, ddescribe, iit, el} from 'angular2/test_lib';
2+
3+
import {View} from 'angular2/src/core/compiler/view';
4+
import {ViewPool} from 'angular2/src/core/compiler/view_pool';
5+
import {proxy, IMPLEMENTS} from 'angular2/src/facade/lang';
6+
7+
@proxy
8+
@IMPLEMENTS(View)
9+
class FakeView {
10+
noSuchMethod(i) {
11+
super.noSuchMethod(i);
12+
}
13+
}
14+
15+
export function main() {
16+
describe('ViewPool', () => {
17+
var viewPool, capacity = 3;
18+
beforeEach(() => {
19+
viewPool = new ViewPool(capacity);
20+
})
21+
22+
it('should return null when there are no views', () => {
23+
expect(viewPool.pop()).toBeNull();
24+
expect(viewPool.length()).toBe(0);
25+
})
26+
27+
it('should support storing and retrieving a view', () => {
28+
var view = new FakeView();
29+
viewPool.push(view);
30+
expect(viewPool.length()).toBe(1);
31+
32+
expect(viewPool.pop()).toBe(view);
33+
expect(viewPool.length()).toBe(0);
34+
})
35+
36+
it('should not store more views that its capacity', () => {
37+
for (var i = 0; i < capacity * 2; i++) viewPool.push(new FakeView());
38+
expect(viewPool.length()).toBe(capacity);
39+
40+
for (var i = 0; i < capacity; i++) {
41+
expect(viewPool.pop()).not.toBe(null);
42+
}
43+
expect(viewPool.pop()).toBeNull();
44+
})
45+
})
46+
}

modules/angular2/test/core/compiler/view_spec.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@ class FakeViewPort {
3131
}
3232
}
3333

34+
@proxy
35+
@IMPLEMENTS(View)
36+
class FakeView {
37+
noSuchMethod(i) {
38+
super.noSuchMethod(i);
39+
}
40+
}
3441

3542
export function main() {
3643
describe('view', function() {
@@ -68,6 +75,14 @@ export function main() {
6875
view.dehydrate();
6976
expect(view.hydrated()).toBe(false);
7077
});
78+
79+
it('should use the view pool to reuse views', () => {
80+
var pv = new ProtoView(el('<div id="1"></div>'), new DynamicProtoChangeDetector(), null);
81+
var fakeView = new FakeView();
82+
pv.returnToPool(fakeView);
83+
84+
expect(pv.instantiate(null)).toBe(fakeView);
85+
});
7186
});
7287

7388
describe('with locals', function() {

modules/benchmarks/src/tree/tree_benchmark.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ function setupReflector() {
4343
},
4444
template: new TemplateConfig({
4545
directives: [TreeComponent, NgIf],
46-
inline: `<span>{{data.value}}<span template='ng-if data.right != null'><tree [data]='data.right'></tree></span><span template='ng-if data.left != null'><tree [data]='data.left'></tree></span></span>`
46+
inline: `<span> {{data.value}} <span template='ng-if data.right != null'><tree [data]='data.right'></tree></span><span template='ng-if data.left != null'><tree [data]='data.left'></tree></span></span>`
4747
})
4848
})]
4949
});

0 commit comments

Comments
 (0)