Skip to content

Commit

Permalink
feat(perf): change detection profiler
Browse files Browse the repository at this point in the history
Closes #4000
  • Loading branch information
yjbanov committed Sep 9, 2015
1 parent 28a29f5 commit 8dd6c46
Show file tree
Hide file tree
Showing 12 changed files with 202 additions and 2 deletions.
1 change: 1 addition & 0 deletions modules/angular2/core.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export 'package:angular2/src/core/util.dart';
export 'package:angular2/src/core/di.dart';
export 'package:angular2/src/core/pipes.dart';
export 'package:angular2/src/core/facade.dart';
export 'package:angular2/src/core/application_ref.dart';
// Do not export application for dart. Must import from angular2/bootstrap
//export 'package:angular2/src/core/application.dart';
export 'package:angular2/src/core/services.dart';
Expand Down
1 change: 1 addition & 0 deletions modules/angular2/src/core/facade/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export {win as window};
export var document = window.document;
export var location = window.location;
export var gc = window['gc'] ? () => window['gc']() : () => null;
export var performance = window['performance'] ? window['performance'] : null;
export const Event = Event;
export const MouseEvent = MouseEvent;
export const KeyboardEvent = KeyboardEvent;
Expand Down
64 changes: 64 additions & 0 deletions modules/angular2/src/tools/common_tools.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import {ApplicationRef, LifeCycle} from 'angular2/angular2';
import {isPresent, NumberWrapper} from 'angular2/src/core/facade/lang';
import {performance, window} from 'angular2/src/core/facade/browser';

This comment has been minimized.

Copy link
@gkalpak

gkalpak Sep 9, 2015

Member

performance doesn't seem to be used...


/**
* Entry point for all Angular debug tools. This object corresponds to the `ng`
* global variable accessible in the dev console.
*/
export class AngularTools {
profiler: AngularProfiler;

constructor(appRef: ApplicationRef) { this.profiler = new AngularProfiler(appRef); }
}

/**
* Entry point for all Angular profiling-related debug tools. This object
* corresponds to the `ng.profiler` in the dev console.
*/
export class AngularProfiler {
lifeCycle: LifeCycle;

constructor(appRef: ApplicationRef) { this.lifeCycle = appRef.injector.get(LifeCycle); }

/**
* Exercises change detection in a loop and then prints the average amount of
* time in milliseconds how long a single round of change detection takes for
* the current state of the UI. It runs a minimum of 5 rounds for a minimum
* of 500 milliseconds.
*
* Optionally, a user may pass a `config` parameter containing a map of
* options. Supported options are:
*
* `record` (boolean) - causes the profiler to record a CPU profile while
* it exercises the change detector. Example:
*
* ```
* ng.profiler.timeChangeDetection({record: true})
* ```
*/
timeChangeDetection(config: any) {
var record = isPresent(config) && config['record'];
var profileName = 'Change Detection';
if (record) {
window.console.profile(profileName);
}
var start = window.performance.now();
var numTicks = 0;
while (numTicks < 5 || (window.performance.now() - start) < 500) {
this.lifeCycle.tick();
numTicks++;
}
var end = window.performance.now();
if (record) {
// need to cast to <any> because type checker thinks there's no argument
// while in fact there is:
//
// https://developer.mozilla.org/en-US/docs/Web/API/Console/profileEnd
(<any>window.console.profileEnd)(profileName);
}
var msPerTick = (end - start) / numTicks;
window.console.log(`ran ${numTicks} change detection cycles`);
window.console.log(`${NumberWrapper.toFixed(msPerTick, 2)} ms per check`);
}
}
34 changes: 34 additions & 0 deletions modules/angular2/src/tools/tools.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
library angular2.src.tools.tools;

import 'dart:js';
import 'package:angular2/angular2.dart' show ApplicationRef;
import 'common_tools.dart' show AngularTools;

/**
* Enabled Angular 2 debug tools that are accessible via your browser's
* developer console.
*
* Usage:
*
* 1. Open developer console (e.g. in Chrome Ctrl + Shift + j)
* 1. Type `ng.` (usually the console will show auto-complete suggestion)
* 1. Try the change detection profiler `ng.profiler.timeChangeDetection()`
* then hit Enter.
*/
void enableDebugTools(ApplicationRef appRef) {
final tools = new AngularTools(appRef);
context['ng'] = new JsObject.jsify({
'profiler': {
'timeChangeDetection': ([config]) {
tools.profiler.timeChangeDetection(config);
}
}
});
}

/**
* Disables Angular 2 tools.
*/
void disableDebugTools() {
context.deleteProperty('ng');
}
27 changes: 27 additions & 0 deletions modules/angular2/src/tools/tools.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {global} from 'angular2/src/core/facade/lang';
import {ApplicationRef} from 'angular2/angular2';
import {AngularTools} from './common_tools';

var context = <any>global;

/**
* Enabled Angular 2 debug tools that are accessible via your browser's
* developer console.
*
* Usage:
*
* 1. Open developer console (e.g. in Chrome Ctrl + Shift + j)
* 1. Type `ng.` (usually the console will show auto-complete suggestion)
* 1. Try the change detection profiler `ng.profiler.timeChangeDetection()`
* then hit Enter.
*/
export function enableDebugTools(appRef: ApplicationRef): void {
context.ng = new AngularTools(appRef);
}

/**
* Disables Angular 2 tools.
*/
export function disableDebugTools(): void {
context.ng = undefined;
}
27 changes: 27 additions & 0 deletions modules/angular2/test/tools/spies.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import 'package:angular2/test_lib.dart' show SpyObject;
import 'package:angular2/core.dart'
show ApplicationRef, LifeCycle, Injector, bind;
import 'dart:js';

@proxy
class SpyLifeCycle extends SpyObject implements LifeCycle {
noSuchMethod(m) => super.noSuchMethod(m);
}

@proxy
class SpyApplicationRef extends SpyObject implements ApplicationRef {
Injector injector;

SpyApplicationRef() {
this.injector = Injector.resolveAndCreate([
bind(LifeCycle).toClass(SpyLifeCycle)
]);
}

noSuchMethod(m) => super.noSuchMethod(m);
}

void callNgProfilerTimeChangeDetection([config]) {
context['ng']['profiler'].callMethod('timeChangeDetection',
config != null ? [config] : []);
}
15 changes: 15 additions & 0 deletions modules/angular2/test/tools/spies.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {SpyObject} from 'angular2/test_lib';
import {ApplicationRef, LifeCycle, Injector, bind} from 'angular2/angular2';
import {global} from 'angular2/src/core/facade/lang';

export class SpyApplicationRef extends SpyObject {
injector;
constructor() {
super();
this.injector = Injector.resolveAndCreate([bind(LifeCycle).toValue({tick: () => {}})]);
}
}

export function callNgProfilerTimeChangeDetection(config?): void {
(<any>global).ng.profiler.timeChangeDetection(config);
}
27 changes: 27 additions & 0 deletions modules/angular2/test/tools/tools_spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {
afterEach,
beforeEach,
ddescribe,
describe,
expect,
iit,
inject,
it,
xit
} from 'angular2/test_lib';

import {enableDebugTools, disableDebugTools} from 'angular2/tools';
import {SpyApplicationRef, callNgProfilerTimeChangeDetection} from './spies';

export function main() {
describe('profiler', () => {
beforeEach(() => { enableDebugTools((<any>new SpyApplicationRef())); });

afterEach(() => { disableDebugTools(); });

it('should time change detection', () => { callNgProfilerTimeChangeDetection(); });

it('should time change detection with recording',
() => { callNgProfilerTimeChangeDetection({'record': true}); });
});
}
4 changes: 4 additions & 0 deletions modules/angular2/tools.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/*
* Debugging and profiling tools for Angular 2
*/
export {enableDebugTools, disableDebugTools} from 'angular2/src/tools/tools';
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: angular
environment:
sdk: '>=1.12.0 <2.0.0'
sdk: '>=1.12.0-dev.5.10 <2.0.0'
dependencies:
observe: '0.13.1'
dev_dependencies:
Expand Down
1 change: 1 addition & 0 deletions tools/broccoli/trees/node_tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ module.exports = function makeNodeTree(destinationPath) {
'angular2/test/test_lib/fake_async_spec.ts',
'angular2/test/core/render/xhr_impl_spec.ts',
'angular2/test/core/forms/**',
'angular2/test/tools/tools_spec.ts',
'angular1_router/**'
]
});
Expand Down
1 change: 0 additions & 1 deletion tools/build/file2modulename.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ function file2moduleName(filePath) {
return filePath.replace(/\\/g, '/')
// module name should be relative to `modules` and `tools` folder
.replace(/.*\/modules\//, '')
.replace(/.*\/tools\//, '')
// and 'dist' folder
.replace(/.*\/dist\/js\/dev\/es5\//, '')
// module name should not include `lib`, `web` folders
Expand Down

0 comments on commit 8dd6c46

Please sign in to comment.