Title: [113994] trunk
Revision
113994
Author
[email protected]
Date
2012-04-12 09:29:38 -0700 (Thu, 12 Apr 2012)

Log Message

Web Inspector: show wall time frame duration on the vertical overview
https://bugs.webkit.org/show_bug.cgi?id=83718

Reviewed by Pavel Feldman.

Source/WebCore:

- show frame duration as a contour of the frame bar in vertical overview;
- when scaling, normalize bar to a max of 3 * median frame duration, so too long frames do not dwarf the majority;
- add Array.prototype.qselect() for the above;
- factor our partition algorithm onto Array.prototype for better reuse;

* inspector/front-end/PartialQuickSort.js: factor out partition()
(Object.defineProperty.):
(Object.defineProperty):
* inspector/front-end/TimelineFrameController.js:
(WebInspector.TimelineFrameController.prototype._flushFrame): add frame.duration.
(WebInspector.TimelineFrameController.prototype._createSyntheticFrame): ditto.
* inspector/front-end/TimelineOverviewPane.js:
(WebInspector.TimelineVerticalOverview):
(WebInspector.TimelineVerticalOverview.prototype.reset): this._longestFrameTime is gone.
(WebInspector.TimelineVerticalOverview.prototype.update): ditto.
(WebInspector.TimelineVerticalOverview.prototype._aggregateFrames): return frames to display, not just their timeByCatogory.
(WebInspector.TimelineVerticalOverview.prototype._renderBars): use entire frames, not timeByCategory.
(WebInspector.TimelineVerticalOverview.prototype._renderBar): ditto.
* inspector/front-end/externs.js: Added annotations for partition() and qselect()
(Array.prototype.partition):
(Array.prototype.qselect):
* inspector/front-end/utilities.js:
(.):

LayoutTests:

* inspector/utilities-expected.txt:
* inspector/utilities.html: added results for Array.prototype.qselect()

Modified Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (113993 => 113994)


--- trunk/LayoutTests/ChangeLog	2012-04-12 16:20:48 UTC (rev 113993)
+++ trunk/LayoutTests/ChangeLog	2012-04-12 16:29:38 UTC (rev 113994)
@@ -1,3 +1,13 @@
+2012-04-11  Andrey Kosyakov  <[email protected]>
+
+        Web Inspector: show wall time frame duration on the vertical overview
+        https://bugs.webkit.org/show_bug.cgi?id=83718
+
+        Reviewed by Pavel Feldman.
+
+        * inspector/utilities-expected.txt:
+        * inspector/utilities.html: added results for Array.prototype.qselect()
+
 2012-04-12  Pierre Rossi  <[email protected]>
 
         [Qt] More unreviewed gardening after r113968.

Modified: trunk/LayoutTests/inspector/utilities-expected.txt (113993 => 113994)


--- trunk/LayoutTests/inspector/utilities-expected.txt	2012-04-12 16:20:48 UTC (rev 113993)
+++ trunk/LayoutTests/inspector/utilities-expected.txt	2012-04-12 16:29:38 UTC (rev 113994)
@@ -3,5 +3,25 @@
 
 Running: binaryIndexOfTest
 
+Running: qselectTest
+Array: []
+Reference: {}
+Actual:    {}
+Array: [0]
+Reference: {"min":0,"median":0,"max":0}
+Actual:    {"min":0,"median":0,"max":0}
+Array: [0,0,0,0,0,0,0,0]
+Reference: {"min":0,"median":0,"max":0}
+Actual:    {"min":0,"median":0,"max":0}
+Array: [4,3,2,1]
+Reference: {"min":1,"median":3,"max":4}
+Actual:    {"min":1,"median":3,"max":4}
+Array: [1,2,3,4,5]
+Reference: {"min":1,"median":3,"max":5}
+Actual:    {"min":1,"median":3,"max":5}
+Array: [-1,3,2,7,7,7,10,12,3,4,-1,2]
+Reference: {"min":-1,"median":4,"max":12}
+Actual:    {"min":-1,"median":4,"max":12}
+
 Running: sortRangeTest
 

Modified: trunk/LayoutTests/inspector/utilities.html (113993 => 113994)


--- trunk/LayoutTests/inspector/utilities.html	2012-04-12 16:20:48 UTC (rev 113993)
+++ trunk/LayoutTests/inspector/utilities.html	2012-04-12 16:29:38 UTC (rev 113994)
@@ -37,6 +37,45 @@
             next();
         },
 
+        function qselectTest(next)
+        {
+            var testArrays = [
+                [],
+                [0],
+                [0, 0, 0, 0, 0, 0, 0, 0],
+                [4, 3, 2, 1],
+                [1, 2, 3, 4, 5],
+                [-1, 3, 2, 7, 7, 7, 10, 12, 3, 4, -1, 2]
+            ];
+
+            function testArray(array)
+            {
+                function compare(a, b)
+                {
+                    return a - b;
+                }
+                var sorted = array.slice(0).sort(compare);
+
+                var reference = {
+                    min: sorted[0],
+                    median: sorted[Math.floor(sorted.length / 2)],
+                    max: sorted[sorted.length - 1]
+                }
+
+                var actual = {
+                    min: array.slice(0).qselect(0),
+                    median: array.slice(0).qselect(Math.floor(array.length / 2)),
+                    max: array.slice(0).qselect(array.length - 1)
+                }
+                InspectorTest.addResult("Array: " + JSON.stringify(array));
+                InspectorTest.addResult("Reference: " + JSON.stringify(reference));
+                InspectorTest.addResult("Actual:    " + JSON.stringify(actual));
+            }
+            for (var i = 0, l = testArrays.length; i < l; ++i)
+                testArray(testArrays[i]);
+            next();
+        },
+
         function sortRangeTest(next)
         {
             var testArrays = [

Modified: trunk/Source/WebCore/ChangeLog (113993 => 113994)


--- trunk/Source/WebCore/ChangeLog	2012-04-12 16:20:48 UTC (rev 113993)
+++ trunk/Source/WebCore/ChangeLog	2012-04-12 16:29:38 UTC (rev 113994)
@@ -1,3 +1,34 @@
+2012-04-11  Andrey Kosyakov  <[email protected]>
+
+        Web Inspector: show wall time frame duration on the vertical overview
+        https://bugs.webkit.org/show_bug.cgi?id=83718
+
+        Reviewed by Pavel Feldman.
+
+        - show frame duration as a contour of the frame bar in vertical overview;
+        - when scaling, normalize bar to a max of 3 * median frame duration, so too long frames do not dwarf the majority;
+        - add Array.prototype.qselect() for the above;
+        - factor our partition algorithm onto Array.prototype for better reuse;
+
+        * inspector/front-end/PartialQuickSort.js: factor out partition()
+        (Object.defineProperty.):
+        (Object.defineProperty):
+        * inspector/front-end/TimelineFrameController.js:
+        (WebInspector.TimelineFrameController.prototype._flushFrame): add frame.duration.
+        (WebInspector.TimelineFrameController.prototype._createSyntheticFrame): ditto.
+        * inspector/front-end/TimelineOverviewPane.js:
+        (WebInspector.TimelineVerticalOverview):
+        (WebInspector.TimelineVerticalOverview.prototype.reset): this._longestFrameTime is gone.
+        (WebInspector.TimelineVerticalOverview.prototype.update): ditto.
+        (WebInspector.TimelineVerticalOverview.prototype._aggregateFrames): return frames to display, not just their timeByCatogory.
+        (WebInspector.TimelineVerticalOverview.prototype._renderBars): use entire frames, not timeByCategory.
+        (WebInspector.TimelineVerticalOverview.prototype._renderBar): ditto.
+        * inspector/front-end/externs.js: Added annotations for partition() and qselect()
+        (Array.prototype.partition):
+        (Array.prototype.qselect):
+        * inspector/front-end/utilities.js:
+        (.):
+
 2012-04-12  David Barr  <[email protected]>
 
         Optimize non-rounded double border rendering to avoid transparency layers

Modified: trunk/Source/WebCore/inspector/front-end/PartialQuickSort.js (113993 => 113994)


--- trunk/Source/WebCore/inspector/front-end/PartialQuickSort.js	2012-04-12 16:20:48 UTC (rev 113993)
+++ trunk/Source/WebCore/inspector/front-end/PartialQuickSort.js	2012-04-12 16:29:38 UTC (rev 113994)
@@ -32,34 +32,12 @@
 /** @this {Array} */
 function(comparator, leftBound, rightBound, k)
 {
-    function swap(array, i1, i2)
-    {
-        var temp = array[i1];
-        array[i1] = array[i2];
-        array[i2] = temp;
-    }
-
-    function partition(array, comparator, left, right, pivotIndex)
-    {
-        var pivotValue = array[pivotIndex];
-        swap(array, right, pivotIndex);
-        var storeIndex = left;
-        for (var i = left; i < right; ++i) {
-            if (comparator(array[i], pivotValue) < 0) {
-                swap(array, storeIndex, i);
-                ++storeIndex;
-            }
-        }
-        swap(array, right, storeIndex);
-        return storeIndex;
-    }
-
     function quickSortFirstK(array, comparator, left, right, k)
     {
         if (right <= left)
             return;
         var pivotIndex = Math.floor(Math.random() * (right - left)) + left;
-        var pivotNewIndex = partition(array, comparator, left, right, pivotIndex);
+        var pivotNewIndex = array.partition(comparator, left, right, pivotIndex);
         quickSortFirstK(array, comparator, left, pivotNewIndex - 1, k);
         if (pivotNewIndex < left + k - 1)
             quickSortFirstK(array, comparator, pivotNewIndex + 1, right, k);

Modified: trunk/Source/WebCore/inspector/front-end/TimelineFrameController.js (113993 => 113994)


--- trunk/Source/WebCore/inspector/front-end/TimelineFrameController.js	2012-04-12 16:20:48 UTC (rev 113993)
+++ trunk/Source/WebCore/inspector/front-end/TimelineFrameController.js	2012-04-12 16:29:38 UTC (rev 113994)
@@ -78,6 +78,7 @@
         var frameBeginTime = WebInspector.TimelineModel.startTimeInSeconds(record);
         if (this._lastFrame) {
             this._lastFrame.endTime = frameBeginTime;
+            this._lastFrame.duration = this._lastFrame.endTime - this._lastFrame.startTime;
             this._overviewPane.addFrame(this._lastFrame);
             this._presentationModel.addFrame(this._lastFrame);
         }
@@ -92,7 +93,8 @@
         frame.startTime = WebInspector.TimelineModel.startTimeInSeconds(record);
         frame.startTimeOffset = this._model.recordOffsetInSeconds(record);
         frame.endTime = WebInspector.TimelineModel.endTimeInSeconds(record);
-        frame.cpuTime = WebInspector.TimelineModel.durationInSeconds(record);
+        frame.duration = WebInspector.TimelineModel.durationInSeconds(record);
+        frame.cpuTime = frame.duration;
         return frame;
     },
 

Modified: trunk/Source/WebCore/inspector/front-end/TimelineOverviewPane.js (113993 => 113994)


--- trunk/Source/WebCore/inspector/front-end/TimelineOverviewPane.js	2012-04-12 16:20:48 UTC (rev 113993)
+++ trunk/Source/WebCore/inspector/front-end/TimelineOverviewPane.js	2012-04-12 16:29:38 UTC (rev 113994)
@@ -909,6 +909,7 @@
     this._borderStyles.loading = "rgb(106, 152, 213)";
     this._borderStyles.scripting = "rgb(223, 175, 77)";
     this._borderStyles.rendering = "rgb(130, 59, 190)";
+    this._borderStyles._frameLength = "rgb(90, 90, 90)";
 }
 
 WebInspector.TimelineVerticalOverview.prototype = {
@@ -916,7 +917,6 @@
     {
         this._recordsPerBar = 1;
         this._barTimes = [];
-        this._longestBarTime = 0;
         this._frames = [];
     },
 
@@ -925,13 +925,12 @@
         const minBarWidth = 4;
         var framesPerBar = Math.max(1, this._frames.length * minBarWidth / this.element.clientWidth);
         this._barTimes = [];
-        this._longestBarTime = 0;
-        var barHeights = this._aggregateFrames(framesPerBar);
+        var visibleFrames = this._aggregateFrames(framesPerBar);
 
         const paddingTop = 4;
-        var scale = (this.element.clientHeight - paddingTop) / this._longestBarTime;
+        var scale = (this.element.clientHeight - paddingTop) / this._normalBarLength;
 
-        this._renderBars(barHeights, scale);
+        this._renderBars(visibleFrames, scale);
     },
 
     /**
@@ -947,64 +946,89 @@
      */
     _aggregateFrames: function(framesPerBar)
     {
-        var statistics = [];
+        var visibleFrames = [];
+        var durations = [];
+        var longestFrameTime = 0;
+
         for (var barNumber = 0, currentFrame = 0; currentFrame < this._frames.length; ++barNumber) {
             var barStartTime = this._frames[currentFrame].startTime;
             var longestFrame = null;
 
             for (var lastFrame = Math.min(Math.floor((barNumber + 1) * framesPerBar), this._frames.length);
                  currentFrame < lastFrame; ++currentFrame) {
-                if (!longestFrame || longestFrame.cpuTime < this._frames[currentFrame].cpuTime)
+                if (!longestFrame || longestFrame.duration < this._frames[currentFrame].duration)
                     longestFrame = this._frames[currentFrame];
             }
             var barEndTime = this._frames[currentFrame - 1].endTime;
             if (longestFrame) {
-                this._longestBarTime = Math.max(this._longestBarTime, longestFrame.cpuTime);
-                statistics.push(longestFrame.timeByCategory);
+                longestFrameTime = Math.max(longestFrameTime, longestFrame.duration);
+                visibleFrames.push(longestFrame);
                 this._barTimes.push({ startTime: barStartTime, endTime: barEndTime });
+                durations.push(longestFrame.duration);
             }
         }
-        return statistics;
+        // Do not let occasional very long frames to dwarf the majority -- use at most 3 * median frame length for scale.
+        this._normalBarLength = Math.min(longestFrameTime, 3 * durations.qselect(Math.floor(durations.length / 2)));
+        return visibleFrames;
     },
 
-    _renderBars: function(allBarHeights, scale)
+    _renderBars: function(frames, scale)
     {
         // Use real world, 1:1 coordinates in canvas. This will also take care of clearing it.
         this.element.width = this.element.clientWidth;
         this.element.height = this.element.clientHeight;
 
         const maxPadding = 5;
-        this._actualOuterBarWidth = Math.min((this.element.width - 2 * this._outerPadding) / allBarHeights.length, this._maxInnerBarWidth + maxPadding);
+        this._actualOuterBarWidth = Math.min((this.element.width - 2 * this._outerPadding) / frames.length, this._maxInnerBarWidth + maxPadding);
         this._actualPadding = Math.min(Math.floor(this._actualOuterBarWidth / 3), maxPadding);
 
-        for (var i = 0; i < allBarHeights.length; ++i)
-            this._renderBar(this._outerPadding + this._actualOuterBarWidth * i, this._actualOuterBarWidth - this._actualPadding, allBarHeights[i], scale);
+        for (var i = 0; i < frames.length; ++i) {
+            var x = this._outerPadding + this._actualOuterBarWidth * i;
+            var width = this._actualOuterBarWidth - this._actualPadding;
+            this._renderBar(x, width, frames[i], scale);
+        }
     },
 
-    _renderBar: function(left, width, barHeights, scale)
+    _renderBar: function(left, width, frame, scale)
     {
-        var categories = Object.keys(barHeights);
+        var categories = Object.keys(frame.timeByCategory);
         if (!categories.length)
             return;
+        var x = Math.floor(left) + 0.5;
+        width = Math.floor(width);
+
         for (var i = 0, bottomOffset = this.element.height; i < categories.length; ++i) {
             var category = categories[i];
-            var duration = barHeights[category];
+            var duration = frame.timeByCategory[category];
 
             if (!duration)
                 continue;
             var height = duration * scale;
+            var y = Math.floor(bottomOffset - height) + 0.5;
 
             this._context.save();
-            this._context.translate(Math.floor(left) + 0.5, 0);
+            this._context.translate(x, 0);
             this._context.scale(width / this._maxInnerBarWidth, 1);
             this._context.fillStyle = this._fillStyles[category];
-            this._context.fillRect(0, bottomOffset - height, this._maxInnerBarWidth, height);
+            this._context.fillRect(0, y, this._maxInnerBarWidth, Math.floor(height));
             this._context.restore();
 
             this._context.strokeStyle = this._borderStyles[category];
-            this._context.strokeRect(Math.floor(left) + 0.5, Math.floor(bottomOffset - height) + 0.5, Math.floor(width), Math.floor(height));
+            this._context.strokeRect(x, y, width, Math.floor(height));
             bottomOffset -= height - 1;
         }
+        // Draw a contour for the rest of frame time that we did not instrument.
+        var nonCPUTime = frame.duration - frame.cpuTime;
+        var y0 = Math.floor(bottomOffset - nonCPUTime * scale) + 0.5;
+        var y1 = Math.floor(bottomOffset) + 0.5;
+
+        this._context.strokeStyle = this._borderStyles._frameLength;
+        this._context.beginPath();
+        this._context.moveTo(x, y1);
+        this._context.lineTo(x, y0);
+        this._context.lineTo(x + width, y0);
+        this._context.lineTo(x + width, y1);
+        this._context.stroke();
     },
 
     getWindowTimes: function(windowLeft, windowRight)

Modified: trunk/Source/WebCore/inspector/front-end/externs.js (113993 => 113994)


--- trunk/Source/WebCore/inspector/front-end/externs.js	2012-04-12 16:20:48 UTC (rev 113993)
+++ trunk/Source/WebCore/inspector/front-end/externs.js	2012-04-12 16:29:38 UTC (rev 113994)
@@ -78,7 +78,24 @@
 Array.prototype.binaryIndexOf = function(anchor) {}
 Array.prototype.sortRange = function(comparator, leftBound, rightBound, k) {}
 
+/**
+ * @this {Array.<number>}
+ * @param {function(number,number):boolean} comparator
+ * @param {number} left
+ * @param {number} right
+ * @param {number} pivotIndex
+ * @return {number}
+ */
+Array.prototype.partition = function(comparator, left, right, pivotIndex) {}
 
+/**
+ * @this {Array.<number>}
+ * @param {number} k
+ * @param {function(number,number):boolean=} comparator
+ * @return {number}
+ */
+Array.prototype.qselect = function(k, comparator) {}
+
 DOMApplicationCache.prototype.UNCACHED = 0;
 DOMApplicationCache.prototype.IDLE = 1;
 DOMApplicationCache.prototype.CHECKING = 2;

Modified: trunk/Source/WebCore/inspector/front-end/utilities.js (113993 => 113994)


--- trunk/Source/WebCore/inspector/front-end/utilities.js	2012-04-12 16:20:48 UTC (rev 113993)
+++ trunk/Source/WebCore/inspector/front-end/utilities.js	2012-04-12 16:29:38 UTC (rev 113994)
@@ -716,6 +716,66 @@
     }
 });
 
+Object.defineProperty(Array.prototype, "partition",
+{
+    /**
+     * @this {Array.<number>}
+     * @param {function(number,number):boolean} comparator
+     * @param {number} left
+     * @param {number} right
+     * @param {number} pivotIndex
+     */
+    value: function(comparator, left, right, pivotIndex)
+    {
+        function swap(array, i1, i2)
+        {
+            var temp = array[i1];
+            array[i1] = array[i2];
+            array[i2] = temp;
+        }
+
+        var pivotValue = this[pivotIndex];
+        swap(this, right, pivotIndex);
+        var storeIndex = left;
+        for (var i = left; i < right; ++i) {
+            if (comparator(this[i], pivotValue) < 0) {
+                swap(this, storeIndex, i);
+                ++storeIndex;
+            }
+        }
+        swap(this, right, storeIndex);
+        return storeIndex;
+    }
+});
+
+Object.defineProperty(Array.prototype, "qselect",
+{
+    /**
+     * @this {Array.<number>}
+     * @param {number} k
+     * @param {function(number,number):boolean=} comparator
+     */
+    value: function(k, comparator)
+    {
+        if (k < 0 || k >= this.length)
+            return;
+        if (!comparator)
+            comparator = function(a, b) { return a - b; }
+
+        var low = 0;
+        var high = this.length - 1;
+        for (;;) {
+            var pivotPosition = this.partition(comparator, low, high, Math.floor((high + low) / 2));
+            if (pivotPosition === k)
+                return this[k];
+            else if (pivotPosition > k)
+                high = pivotPosition - 1;
+            else
+                low = pivotPosition + 1;
+        }
+    }
+});
+
 Array.diff = function(left, right)
 {
     var o = left;
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to