Chromium Code Reviews| Index: tracing/tracing/metrics/v8/utils.html |
| diff --git a/tracing/tracing/metrics/v8/utils.html b/tracing/tracing/metrics/v8/utils.html |
| index cf9fae1e34644a40d98ce8ebbf895f7a8ab13e42..f09c0988c161f450297bac28b85727057acc2729 100644 |
| --- a/tracing/tracing/metrics/v8/utils.html |
| +++ b/tracing/tracing/metrics/v8/utils.html |
| @@ -4,7 +4,9 @@ Copyright 2016 The Chromium Authors. All rights reserved. |
| Use of this source code is governed by a BSD-style license that can be |
| found in the LICENSE file. |
| --> |
| +<link rel="import" href="/tracing/base/piecewise_linear_function.html"> |
| <link rel="import" href="/tracing/base/range.html"> |
| +<link rel="import" href="/tracing/base/range_utils.html"> |
| <link rel="import" href="/tracing/metrics/metric_registry.html"> |
| <link rel="import" href="/tracing/value/numeric.html"> |
| <link rel="import" href="/tracing/value/unit.html"> |
| @@ -17,6 +19,9 @@ tr.exportTo('tr.metrics.v8.utils', function() { |
| // The title of the idle task event. |
| var IDLE_TASK_EVENT = 'SingleThreadIdleTaskRunner::RunTask'; |
| + // V8 execution event. |
| + var V8_EXECUTE = 'V8.Execute'; |
| + |
| // GC events start with this prefix. |
| var GC_EVENT_PREFIX = 'V8.GC'; |
| @@ -38,8 +43,8 @@ tr.exportTo('tr.metrics.v8.utils', function() { |
| }; |
| /** |
| - * Finds the first parent of the |event| for which the |predicate| holds. |
| - */ |
| + * Finds the first parent of the |event| for which the |predicate| holds. |
| + */ |
| function findParent(event, predicate) { |
| var parent = event.parentSlice; |
| while (parent) { |
| @@ -59,6 +64,14 @@ tr.exportTo('tr.metrics.v8.utils', function() { |
| return event.title === LOW_MEMORY_EVENT; |
| } |
| + function isV8ExecuteEvent(event) { |
| + return event.title === V8_EXECUTE; |
| + } |
| + |
| + function isTopV8ExecuteEvent(event) { |
| + return isV8ExecuteEvent(event) && findParent(isV8ExecuteEvent) === null; |
| + } |
| + |
| function isGarbageCollectionEvent(event) { |
| // Low memory notification is handled specially because it contains |
| // several full mark compact events. |
| @@ -96,15 +109,146 @@ tr.exportTo('tr.metrics.v8.utils', function() { |
| return prefix + '.' + name; |
| } |
| + /** |
| + * Given a list of intervals, returns a new list with all overalapping |
| + * intervals merged into a single interval. |
| + */ |
| + function unionOfIntervals(intervals) { |
| + if (intervals.length === 0) |
| + return []; |
| + return tr.b.mergeRanges( |
| + intervals.map(x => ({min: x.start, max: x.end})), 1e-6, |
| + function(ranges) { |
| + return { |
| + start: ranges.reduce((acc, x) => Math.min(acc, x.min), ranges[0].min), |
| + end: ranges.reduce((acc, x) => Math.max(acc, x.max), ranges[0].max) |
| + }; |
| + } |
| + ); |
| + } |
| + |
| + /** |
| + * An end-point of a window that is sliding from left to right |
| + * over |points| starting from time |start|. |
| + * It is intended to be used only by the |mutatorUtilization| function. |
| + * @constructor |
| + */ |
| + function WindowEndpoint(start, points) { |
| + this.points = points; |
| + // The index of the last passed point. |
| + this.lastIndex = -1; |
| + // The position of the end-point in the time line. |
| + this.position = start; |
| + // The distance until the next point. |
| + this.distanceUntilNextPoint = points[0].position - start; |
| + // The cumulative duration of GC pauses until this position. |
| + this.cummulativePause = 0; |
| + // The number of entered GC intervals. |
| + this.stackDepth = 0; |
| + }; |
|
petrcermak
2016/04/25 12:31:17
nit: no need for semicolon here.
ulan
2016/04/25 12:46:26
Done.
|
| + |
| + WindowEndpoint.prototype = { |
| + // Advance the end-point by the given |delta|. |
| + advance: function(delta) { |
| + var points = this.points; |
| + if (delta < this.distanceUntilNextPoint) { |
| + this.position += delta; |
| + this.cummulativePause += this.stackDepth > 0 ? delta : 0; |
| + this.distanceUntilNextPoint = |
| + points[this.lastIndex + 1].position - this.position; |
| + } else { |
| + this.position += this.distanceUntilNextPoint; |
| + this.cummulativePause += |
| + this.stackDepth > 0 ? this.distanceUntilNextPoint : 0; |
| + this.distanceUntilNextPoint = 0; |
| + this.lastIndex++; |
| + if (this.lastIndex < points.length) { |
| + this.stackDepth += points[this.lastIndex].delta; |
| + if (this.lastIndex + 1 < points.length) |
| + this.distanceUntilNextPoint = |
| + points[this.lastIndex + 1].position - this.position; |
| + } |
| + } |
| + } |
| + }; |
| + |
| + /** |
| + * Returns mutator utilization as a piecewise linear function. |
| + * Mutator utilization for a window size w is a function of time mu_w(t) |
| + * that shows how much time in [t, t+w] is left for the mutator relative |
| + * to the time window size. |
| + * More formally: |
| + * mu_w(t) = (w - total_time_spent_in_gc_in(t, t + w)) / w. |
| + * The range of mu_w(t) is [0..1]. |
| + * See "A Parallel, Real-Time Garbage Collector" by Cheng et. al. for |
| + * more info [https://www.cs.cmu.edu/~guyb/papers/gc2001.pdf]. |
| + * |
| + * All parameters must use the same time unit. |
| + * @param {number} start The start time of execution. |
| + * @param {number} end The end time of execution. |
| + * @param {number} timeWindow The size of the time window. |
| + * @param {!Array<!{start: number, end: number}>} intervals The list of |
| + * GC pauses. |
| + */ |
| + function mutatorUtilization(start, end, timeWindow, intervals) { |
| + var mu = new tr.b.PiecewiseLinearFunction(); |
| + // If the interval is smaller than the time window, then the function is |
| + // empty. |
| + if (end - start <= timeWindow) |
| + return mu; |
| + // If there are GC pauses then the mutator utilization is 1.0. |
| + if (intervals.length === 0) { |
| + mu.push(start, 1.0, end - timeWindow, 1.0); |
| + return mu; |
| + } |
| + intervals = unionOfIntervals(intervals); |
| + // Create a point for the start and the end of each interval. |
| + var points = []; |
| + intervals.forEach(function(interval) { |
| + points.push({position: interval.start, delta: 1}); |
| + points.push({position: interval.end, delta: -1}); |
| + }); |
| + points.sort((a, b) => a.position - b.position); |
| + points.push({position: end, delta: 0}); |
| + // The left and the right limit of the sliding window. |
| + var left = new WindowEndpoint(start, points); |
| + var right = new WindowEndpoint(start, points); |
| + // Advance the right end-point until we get the correct window size. |
| + while (right.position - left.position < timeWindow) |
| + right.advance(timeWindow - (right.position - left.position)); |
| + while (right.lastIndex < points.length) { |
| + // Advance the window end-points by the largest possible amount |
| + // without jumping over a point. |
| + var distanceUntilNextPoint = |
| + Math.min(left.distanceUntilNextPoint, right.distanceUntilNextPoint); |
| + var position1 = left.position; |
| + var value1 = right.cummulativePause - left.cummulativePause; |
| + left.advance(distanceUntilNextPoint); |
| + right.advance(distanceUntilNextPoint); |
| + // Add a new mutator utilization segment only if it is non-trivial. |
| + if (distanceUntilNextPoint > 0) { |
| + var position2 = left.position; |
| + var value2 = right.cummulativePause - left.cummulativePause; |
| + mu.push(position1, 1.0 - value1 / timeWindow, |
| + position2, 1.0 - value2 / timeWindow); |
| + } |
| + } |
| + return mu; |
| + } |
| + |
| return { |
| findParent: findParent, |
| isIdleTask: isIdleTask, |
| isLowMemoryEvent: isLowMemoryEvent, |
| + isV8ExecuteEvent: isV8ExecuteEvent, |
| + isTopV8ExecuteEvent: isTopV8ExecuteEvent, |
| isGarbageCollectionEvent: isGarbageCollectionEvent, |
| isTopGarbageCollectionEvent: isTopGarbageCollectionEvent, |
| isSubGarbageCollectionEvent: isSubGarbageCollectionEvent, |
| topGarbageCollectionEventName: topGarbageCollectionEventName, |
| - subGarbageCollectionEventName: subGarbageCollectionEventName |
| + subGarbageCollectionEventName: subGarbageCollectionEventName, |
| + unionOfIntervals: unionOfIntervals, |
| + mutatorUtilization: mutatorUtilization |
| }; |
| }); |
| </script> |