Skip to content
This repository has been archived by the owner on Feb 22, 2018. It is now read-only.

feat(WatchGroup): Allow measuring cost of running reaction functions and #1148

Closed

Conversation

mvuksano
Copy link
Contributor

To be able to measure cost of reaction function, dirty check and/or
dirty watch one needs to define ExecutionStats. ExecutionStats is
configured using ExecutionStatsConfig object. This object can be used to
define how many records to display when showing the stats, what's the
minimum threshold (in microseconds) for a record to be considered as
relevant as well as to enable and disable tracking the stats.

One can define ExecutionStats, ExectionStatsEmitter and
ExecutionStatsConfig like:

  m..bind(ExectionStats)
   ..bind(ExectionStatsEmitter)
   ..bind(ExectionStatsConfig, toFactory: (i) => new ExectionStats(...))

The results can be displayed using ngStats object. To obtain a reference
to an ngStats object type (in the console):

  ng = ngStats();

To display maxNumber (defined using ExectionStatsConfig object) of the
most expensive reaction functions type:

  ng.showReactionFnStats();

To display maxNumber of most expensive evals type:

  ng.showEvalStats();

To display maxNumber of most expensive dirty checks type:

  ng.showDirtyCheckStats();

One can use ng.enable() method to enable tracking and ng.disable()
method to disable trackin. To clear the results use ng.reset()
method.

To achieve least performance impact on the application make sure
that the ExectionStats is null.

  m..bind(ExectionStats, toValue: null);

That way no extra objects will be created and the impact on the
actual application will be minimal.

@mvuksano mvuksano added cla: yes and removed cla: no labels Jun 12, 2014
void addTime(ReactionFnStatsEntry entry) {
try {
stats[count++] = entry;
} on RangeError {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using exceptions for control flow? why not check if stats length is lower than count++?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, exceptions are very expensive. Change to range check.

@IgorMinar
Copy link
Contributor

I don't know enough about the previous conversations about this feature, but the current implementation looks pretty heavy weight.

wouldn't a sufficient goal be to report just two number - the total cost of reactions per digest and total number of reactions per digest? we could then report this along with dirty checking and flush.

@jeffbcross
Copy link
Contributor

Why aren't there any tests included with this PR?

@jeffbcross
Copy link
Contributor

Where is ReactionFnStats defined?

@mvuksano
Copy link
Contributor Author

@jeffbcross I'm still refactoring this. It's actually been renamed to ExecutionStats. I should be done with refactorings soon.

@jeffbcross
Copy link
Contributor

Great! Are you adding tests as you refactor?

@mvuksano
Copy link
Contributor Author

Yes, I will do that. Is there any specific behaviour that you'd like to see how to use? Or was this just a general question?

@jeffbcross
Copy link
Contributor

A few reasons for asking about tests:

  • I'd like to look at tests to see your code in action, and how I can use it
  • I'd like to know that everything works before I try to integrate it
  • We generally require tests for all new angular functionality, even utilities like this (but I'm a noob on angular.dart, and maybe there's a reason why tests aren't required for this; I defer to @mhevery)

void showEvalStats(ExecutionStats fnStats) {
_printLine('Time (us)', 'Fn/Name');
fnStats.getEvalStats().forEach((ExecutionEntry entry) {
var text = (entry.value as EvalWatchRecord).fn != null ?
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mhevery What would be useful information to display about EvalWatchRecord?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, figured out that EvalWatchRecord.toString will do just fine.

@mvuksano
Copy link
Contributor Author

@mhevery Could you have a look at this. There's still some more work to be done here but feedback would be useful. I'm doubt that extracting info from EvalWatchRecord using mirrors is a good idea. If not, what's your suggestion?

@mvuksano
Copy link
Contributor Author

Mirrors seems like a bad idea... They will increase the size of compiled code...


Iterable<ExecutionEntry> getReactionFnStats() {
_shrinkDirtyWatch();
return dirtyWatchStats.getRange(0, _capacity);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should all the get<...>() be true getters ?

reporting it.

To be able to measure cost of reaction function, dirty check and/or
dirty watch one needs to define ExecutionStats. ExecutionStats is
configured using ExecutionStatsConfig object. This object can be used to
define how many records to display when showing the stats, what's the
minimum threshold (in microseconds) for a record to be considered as
relevant as well as to enable and disable tracking the stats.

One can define ExecutionStats, ExectionStatsEmitter and
ExecutionStatsConfig like:

  m..bind(ExectionStats)
   ..bind(ExectionStatsEmitter)
   ..bind(ExectionStatsConfig, toFactory: (i) => new ExectionStats(...))

The results can be displayed using ngStats object. To obtain a reference
to an ngStats object type (in the console):

  ng = ngStats();

To display maxNumber (defined using ExectionStatsConfig object) of the
most expensive reaction functions type:

  ng.showReactionFnStats();

To display maxNumber of most expensive evals type:

  ng.showEvalStats();

To display maxNumber of most expensive dirty checks type:

  ng.showDirtyCheckStats();

One can use ng.enable() method to enable tracking and ng.disable()
method to disable trackin. To clear the results use ng.reset()
method.

To achieve least performance impact on the application make sure
that the ExectionStats is null.

  m..bind(ExectionStats, toValue: null);

That way no extra objects will be created and the impact on the
actual application will be minimal.
@@ -312,7 +315,19 @@ class DirtyCheckingChangeDetector<H> extends DirtyCheckingChangeDetectorGroup<H>
int count = 0;
while (current != null) {
try {
if (current.check()) changeTail = changeTail._nextChange = current;
if (executionStats != null && executionStats.config.enabled) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a better way to do this is to duplicate the whole while loop. One while loop is instrumented and the other is not.

The stats objects should be injected in the constructor and should have on off switch which would then control which while loop to chose. That way one could turn on/off the stats during the lifetime.

@mhevery
Copy link
Contributor

mhevery commented Jul 21, 2014

@chirayuk Do we still need this or does your change supersedes this one?

@mhevery
Copy link
Contributor

mhevery commented Aug 26, 2014

Superseded by @chirayuk's wor.

@mhevery mhevery closed this Aug 26, 2014
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Development

Successfully merging this pull request may close these issues.

6 participants