diff options
author | Dan Smilkov <smilkov@google.com> | 2016-06-28 09:29:32 -0800 |
---|---|---|
committer | TensorFlower Gardener <gardener@tensorflow.org> | 2016-06-28 10:32:17 -0700 |
commit | f76c72ad4852bcf7059ffdf8e66ffa1ee526d483 (patch) | |
tree | 8f4c151220909a7521b37a4bc070bd0684841dba | |
parent | fbad3029807ccfd1048dc2fcd0d66c81f1b0b181 (diff) |
Adding a device checkbox list for excluding devices when computing stats since some device stats can skew results.
Change: 126089107
13 files changed, 333 insertions, 117 deletions
@@ -70,7 +70,7 @@ new_git_repository( name = "iron_a11y_keys_behavior", build_file = "bower.BUILD", remote = "https://github.com/polymerelements/iron-a11y-keys-behavior.git", - tag = "v1.1.4", + tag = "v1.1.5", ) new_git_repository( @@ -389,6 +389,13 @@ new_git_repository( ) new_git_repository( + name = "paper_tooltip", + build_file = "bower.BUILD", + remote = "https://github.com/polymerelements/paper-tooltip.git", + tag = "v1.1.2", +) + +new_git_repository( name = "plottable", build_file = "bower.BUILD", remote = "https://github.com/palantir/plottable.git", diff --git a/bower.BUILD b/bower.BUILD index ea393cafe3..1a4b6158a4 100644 --- a/bower.BUILD +++ b/bower.BUILD @@ -533,6 +533,14 @@ filegroup( ) filegroup( + name = "paper_tooltip", + srcs = [ + "index.html", + "paper-tooltip.html", + ], +) + +filegroup( name = "plottable", srcs = [ "plottable.css", diff --git a/tensorflow/tensorboard/bower.json b/tensorflow/tensorboard/bower.json index b470a75f71..c410fd8dc0 100644 --- a/tensorflow/tensorboard/bower.json +++ b/tensorflow/tensorboard/bower.json @@ -84,6 +84,7 @@ "paper-tabs": "PolymerElements/paper-tabs#1.6.2", "paper-toggle-button": "PolymerElements/paper-toggle-button#1.1.2", "paper-toolbar": "PolymerElements/paper-toolbar#1.1.4", + "paper-tooltip": "PolymerElements/paper-tooltip#1.1.2", "plottable": "1.16.1", "polymer": "1.5.0", "promise-polyfill": "polymerlabs/promise-polyfill#1.0.0", @@ -158,6 +159,7 @@ "paper-tabs": "1.6.2", "paper-toggle-button": "1.1.2", "paper-toolbar": "1.1.4", + "paper-tooltip": "1.1.2", "plottable": "1.16.1", "polymer": "1.5.0", "promise-polyfill": "1.0.0", diff --git a/tensorflow/tensorboard/bower/BUILD b/tensorflow/tensorboard/bower/BUILD index 10d5b464e8..90d9910205 100644 --- a/tensorflow/tensorboard/bower/BUILD +++ b/tensorflow/tensorboard/bower/BUILD @@ -57,6 +57,7 @@ filegroup( "@paper_tabs//:paper_tabs", "@paper_toggle_button//:paper_toggle_button", "@paper_toolbar//:paper_toolbar", + "@paper_tooltip//:paper_tooltip", "@plottable//:plottable", "@polymer//:polymer", "@promise_polyfill//:promise_polyfill", diff --git a/tensorflow/tensorboard/components/tf-graph-board/tf-graph-board.html b/tensorflow/tensorboard/components/tf-graph-board/tf-graph-board.html index e352038ea6..61e42982a7 100644 --- a/tensorflow/tensorboard/components/tf-graph-board/tf-graph-board.html +++ b/tensorflow/tensorboard/components/tf-graph-board/tf-graph-board.html @@ -117,6 +117,7 @@ paper-progress { basic-graph="[[graph]]" hierarchy-params="[[hierarchyParams]]" render-hierarchy="{{_renderHierarchy}}" + devices-for-stats="[[devicesForStats]]" stats="[[stats]]" selected-node="{{_selectedNode}}" highlighted-node="{{_highlightedNode}}" diff --git a/tensorflow/tensorboard/components/tf-graph-common/lib/graph.ts b/tensorflow/tensorboard/components/tf-graph-common/lib/graph.ts index d843aafec4..d3567641e6 100644 --- a/tensorflow/tensorboard/components/tf-graph-common/lib/graph.ts +++ b/tensorflow/tensorboard/components/tf-graph-common/lib/graph.ts @@ -379,8 +379,16 @@ export function createMetanode(name: string, opt = {}): Metanode { * graph information. */ export function joinStatsInfoWithGraph( - graph: SlimGraph, stats: tf.graph.proto.StepStats): void { + graph: SlimGraph, stats: tf.graph.proto.StepStats, + devicesForStats?: {[device: string]: boolean}): void { + // Reset stats for each node. + _.each(graph.nodes, node => { node.stats = null; }); + _.each(stats.dev_stats, devStats => { + // Ignore devices that are not selected. + if (devicesForStats && !devicesForStats[devStats.device]) { + return; + } _.each(devStats.node_stats, nodeStats => { // Lookup the node in the graph by its original name, e.g. A. If not // found, lookup by the rewritten name A/(A) in case the name is both @@ -484,7 +492,7 @@ export class NodeStats { * Total number of bytes used for the node. Sum of all children * if it is a Group node. */ - totalBytes: number; + totalBytes = 0; /** * Total number of compute time in microseconds used for the node. * Sum of all children if it is a Group node. Null if it is unknown. diff --git a/tensorflow/tensorboard/components/tf-graph-common/lib/util.ts b/tensorflow/tensorboard/components/tf-graph-common/lib/util.ts index 8e6e92361d..1625c723c2 100644 --- a/tensorflow/tensorboard/components/tf-graph-common/lib/util.ts +++ b/tensorflow/tensorboard/components/tf-graph-common/lib/util.ts @@ -193,4 +193,38 @@ module tf.graph.util { } return false; } + + /** + * Given a list of strings, it returns a new list of strings with the longest + * common prefix removed. If the common prefix is one of the strings in the + * list, it returns the original strings. + */ + export function removeCommonPrefix(strings: string[]) { + if (strings.length < 2) { + return strings; + } + + let index = 0; + let largestIndex = 0; + // Find the shortest name across all strings. + let minLength = _.min(_.map(strings, str => str.length)); + while (true) { + index++; + let prefixes = _.map(strings, str => str.substring(0, index)); + let allTheSame = prefixes.every((prefix, i) => { + return (i === 0 ? true : prefix === prefixes[i - 1]); + }); + if (allTheSame) { + if (index >= minLength) { + // There is a string whose whole name is a prefix to other string. + // In this case, we return the original list of string. + return strings; + } + largestIndex = index; + } else { + break; + } + } + return _.map(strings, str => str.substring(largestIndex)); + } } diff --git a/tensorflow/tensorboard/components/tf-graph-common/test/index.html b/tensorflow/tensorboard/components/tf-graph-common/test/index.html index 927d2fef93..1897893833 100644 --- a/tensorflow/tensorboard/components/tf-graph-common/test/index.html +++ b/tensorflow/tensorboard/components/tf-graph-common/test/index.html @@ -11,5 +11,6 @@ <script src="graph-test.js"></script> <script src="hierarchy-test.js"></script> <script src="layout-test.js"></script> + <script src="util-test.js"></script> </body> </html> diff --git a/tensorflow/tensorboard/components/tf-graph-common/test/util-test.ts b/tensorflow/tensorboard/components/tf-graph-common/test/util-test.ts new file mode 100644 index 0000000000..0b8d9c9d49 --- /dev/null +++ b/tensorflow/tensorboard/components/tf-graph-common/test/util-test.ts @@ -0,0 +1,46 @@ +/* Copyright 2016 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the 'License'); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an 'AS IS' BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +suite('util', () => { + let assert = chai.assert; + + test('remove common prefix', () => { + + // Empty array. + let result = tf.graph.util.removeCommonPrefix([]); + assert.deepEqual(result, []); + + // No common prefix. + result = tf.graph.util.removeCommonPrefix(['a', 'b', 'c']); + assert.deepEqual(result, ['a', 'b', 'c']); + + // One of the elements is empty string. + result = tf.graph.util.removeCommonPrefix(['a/b', '', 'a/c']); + assert.deepEqual(result, ['a/b', '', 'a/c']); + + // Only one string. + result = tf.graph.util.removeCommonPrefix(['a/b/c']); + assert.deepEqual(result, ['a/b/c']); + + // `q/w/` is the common prefix. Expect `q/w/` to be removed. + result = tf.graph.util.removeCommonPrefix(['q/w/a', 'q/w/b', 'q/w/c/f']); + assert.deepEqual(result, ['a', 'b', 'c/f']); + + // `q/w/` is the common prefix and also an element. Expect nothing to be + // removed since the common prefix is also an element in the array. + result = tf.graph.util.removeCommonPrefix(['q/w/', 'q/w/b', 'q/w/c/f']); + assert.deepEqual(result, ['q/w/', 'q/w/b', 'q/w/c/f']); + }); +}); diff --git a/tensorflow/tensorboard/components/tf-graph-dashboard/tf-graph-dashboard.html b/tensorflow/tensorboard/components/tf-graph-dashboard/tf-graph-dashboard.html index 42e2e44b73..3697c97597 100644 --- a/tensorflow/tensorboard/components/tf-graph-dashboard/tf-graph-dashboard.html +++ b/tensorflow/tensorboard/components/tf-graph-dashboard/tf-graph-dashboard.html @@ -26,6 +26,7 @@ by default. The user can select a different run from a dropdown menu. <tf-dashboard-layout> <div class="sidebar"> <tf-graph-controls id="controls" + devices-for-stats="{{_devicesForStats}}" color-by-params="[[_colorByParams]]" stats="[[_stats]]" color-by="{{_colorBy}}", @@ -48,6 +49,7 @@ by default. The user can select a different run from a dropdown menu. </div> <div class="center"> <tf-graph-board id="graphboard" + devices-for-stats="[[_devicesForStats]]" graph-hierarchy="[[_graphHierarchy]]" graph="[[_graph]]" stats="[[_stats]]" diff --git a/tensorflow/tensorboard/components/tf-graph/demo/tf-graph-demo.html b/tensorflow/tensorboard/components/tf-graph/demo/tf-graph-demo.html index 05256ba399..7bd31d6208 100644 --- a/tensorflow/tensorboard/components/tf-graph/demo/tf-graph-demo.html +++ b/tensorflow/tensorboard/components/tf-graph/demo/tf-graph-demo.html @@ -44,6 +44,7 @@ Example <div class="all"> <div class="side"> <tf-graph-controls + devices-for-stats="{{_devicesForStats}}" color-by-params="[[colorByParams]]" stats="[[stats]]" color-by="{{colorBy}}" @@ -66,6 +67,7 @@ Example </div> <div class="main"> <tf-graph-board id="graphboard" + devices-for-stats="[[_devicesForStats]]" graph-hierarchy="[[graphHierarchy]]" graph="[[graph]]" stats="[[stats]]" diff --git a/tensorflow/tensorboard/components/tf-graph/tf-graph-controls.html b/tensorflow/tensorboard/components/tf-graph/tf-graph-controls.html index 8e445dff1c..09bcff9630 100644 --- a/tensorflow/tensorboard/components/tf-graph/tf-graph-controls.html +++ b/tensorflow/tensorboard/components/tf-graph/tf-graph-controls.html @@ -2,6 +2,7 @@ <link rel="import" href="../paper-menu/paper-menu.html"> <link rel="import" href="../paper-dropdown-menu/paper-dropdown-menu.html"> <link rel="import" href="../paper-radio-group/paper-radio-group.html"> +<link rel="import" href="../paper-tooltip/paper-tooltip.html"> <link rel="import" href="../tf-dashboard-common/tensorboard-color.html"> <dom-module id="tf-graph-controls"> @@ -108,6 +109,12 @@ svg.icon { height: 24px; } +.help-icon { + height: 15px; + margin: 0; + padding: 0; +} + .gray { color: #666; } @@ -121,7 +128,7 @@ svg.icon { font-weight: normal; } .deviceList { - max-height: 100px; + max-height: 200px; overflow-y: auto; } @@ -267,7 +274,7 @@ span.counter { </paper-radio-group> </div> <div> - <template is="dom-if" if="[[_isGradientColoring(colorBy)]]"> + <template is="dom-if" if="[[_isGradientColoring(stats, colorBy)]]"> <svg width="140" height="20" style="margin: 0 5px" class="color-text"> <defs> <linearGradient id="linearGradient" x1="0%" y1="0%" x2="100%" y2="0%"> @@ -285,6 +292,27 @@ span.counter { <div class="domainEnd">[[_currentGradientParams.maxValue]]</div> </div> <br style="clear: both"> + <div>Devices included in stats:</div> + <div class="deviceList"> + <table> + <template is="dom-repeat" items="[[_getDevices(devicesForStats)]]"> + <tr> + <td> + <input type="checkbox" value$="[[item.device]]" checked$="[[item.used]]" on-click="_deviceCheckboxClicked"/> + </td> + <td> + <div> + <span>[[item.suffix]]</span> + <template is="dom-if" if="[[item.ignoredMsg]]"> + <paper-icon-button icon="help" class="help-icon"></paper-icon-button> + <paper-tooltip position="right" animation-delay="0">[[item.ignoredMsg]]</paper-tooltip> + </template> + </div> + </td> + </tr> + </template> + </table> + </div> </template> <template is="dom-if" if="[[_equals(colorBy, 'structure')]]"> <div class="color-text"> @@ -339,115 +367,143 @@ span.counter { </div> </template> </div> - <div class="legend-holder"> - <table> - <tr> - <td><div class="title">Graph</div></td> - <td>(* = expandable)</td> - </tr> - <tr> - <td> - <svg class="icon"> - <rect transform="translate(3, 1)" height="14" width="35" - rx="5" ry="5"/> - </svg> - </td> - <td>Namespace<span class="gray">*</span></td> - </tr> - <tr> - <td> - <svg class="icon" preserveAspectRatio="xMinYMid meet" - viewBox="0 0 10 10"> - <use xlink:href="#op-node-stamp" fill="white" stroke="#ccc" x="9.5" - y="6" /> - </svg> - </td> - <td>OpNode</td> - </tr> - <tr> - <td> - <svg class="icon" height="15px" preserveAspectRatio="xMinYMid meet" - viewBox="0 0 12 12"> - <use xlink:href="#op-series-horizontal-stamp" fill="white" - stroke="#ccc" x="2" y="2"/> - </svg> - </td> - <td>Unconnected series<span class="gray">*</span></td> - </tr> - <tr> - <td> - <svg class="icon" height="15px" - preserveAspectRatio="xMinYMid meet" viewBox="0 0 15 15"> - <use xlink:href="#op-series-vertical-stamp" - fill="white" stroke="#ccc" x="2" y="2"/> - </svg> - </td> - <td>Connected series<span class="gray">*</span></td> - </tr> - <tr> - <td> - <svg class="icon"> - <circle fill="white" stroke="#848484" cx="10" cy="10" r="5"/> - </svg> - </td> - <td>Constant</td> - </tr> - <tr> - <td> - <svg class="image-icon" viewBox="0 0 12 12" width="24" height="24"> - <use x="0" y="0" class="image-icon" xlink:href="#summary-icon"/> - </svg> - </td> - <td>Summary</td> - </tr> - <tr> - <td> - <svg class="icon" height="15px" - preserveAspectRatio="xMinYMid meet" viewBox="0 0 15 15"> - <defs> - <marker id="ref-arrowhead-legend" fill="#bbb" markerWidth="10" - markerHeight="10" refX="1" refY="5" orient="auto"> - <path d="M 10,0 L 0,5 L 10,10 C 7,7 7,3 10,0"/> - </marker> - </defs> - <path stroke="#bbb" - d="M2 9 l 23 0" stroke-linecap="round" /> - </svg> - </td> - <td>Dataflow edge</td> - </tr> - <tr> - <td> - <svg class="icon" height="15px" - preserveAspectRatio="xMinYMid meet" viewBox="0 0 15 15"> - <path stroke="#bbb" - d="M2 9 l 23 0" stroke-linecap="round" stroke-dasharray="2, 2" /> - </svg> - </td> - <td>Control dependency edge</td> - </tr> - <tr> - <td> - <svg class="icon" height="15px" - preserveAspectRatio="xMinYMid meet" viewBox="0 0 15 15"> - <path marker-start="url(#ref-arrowhead-legend)" - stroke="#bbb" d="M2 9 l 23 0" - stroke-linecap="round" /> - </svg> - </td> - <td>Reference edge</td> - </tr> - </table> - </div> + <!-- + Due to limited vertical space on the left sidebar, hide the legend whenever + we show a list of devices to include in stats. + --> + <template is="dom-if" if="[[!_isGradientColoring(stats, colorBy)]]"> + <div class="legend-holder"> + <table> + <tr> + <td><div class="title">Graph</div></td> + <td>(* = expandable)</td> + </tr> + <tr> + <td> + <svg class="icon"> + <rect transform="translate(3, 1)" height="14" width="35" + rx="5" ry="5"/> + </svg> + </td> + <td>Namespace<span class="gray">*</span></td> + </tr> + <tr> + <td> + <svg class="icon" preserveAspectRatio="xMinYMid meet" + viewBox="0 0 10 10"> + <use xlink:href="#op-node-stamp" fill="white" stroke="#ccc" x="9.5" + y="6" /> + </svg> + </td> + <td>OpNode</td> + </tr> + <tr> + <td> + <svg class="icon" height="15px" preserveAspectRatio="xMinYMid meet" + viewBox="0 0 12 12"> + <use xlink:href="#op-series-horizontal-stamp" fill="white" + stroke="#ccc" x="2" y="2"/> + </svg> + </td> + <td>Unconnected series<span class="gray">*</span></td> + </tr> + <tr> + <td> + <svg class="icon" height="15px" + preserveAspectRatio="xMinYMid meet" viewBox="0 0 15 15"> + <use xlink:href="#op-series-vertical-stamp" + fill="white" stroke="#ccc" x="2" y="2"/> + </svg> + </td> + <td>Connected series<span class="gray">*</span></td> + </tr> + <tr> + <td> + <svg class="icon"> + <circle fill="white" stroke="#848484" cx="10" cy="10" r="5"/> + </svg> + </td> + <td>Constant</td> + </tr> + <tr> + <td> + <svg class="image-icon" viewBox="0 0 12 12" width="24" height="24"> + <use x="0" y="0" class="image-icon" xlink:href="#summary-icon"/> + </svg> + </td> + <td>Summary</td> + </tr> + <tr> + <td> + <svg class="icon" height="15px" + preserveAspectRatio="xMinYMid meet" viewBox="0 0 15 15"> + <defs> + <marker id="ref-arrowhead-legend" fill="#bbb" markerWidth="10" + markerHeight="10" refX="1" refY="5" orient="auto"> + <path d="M 10,0 L 0,5 L 10,10 C 7,7 7,3 10,0"/> + </marker> + </defs> + <path stroke="#bbb" + d="M2 9 l 23 0" stroke-linecap="round" /> + </svg> + </td> + <td>Dataflow edge</td> + </tr> + <tr> + <td> + <svg class="icon" height="15px" + preserveAspectRatio="xMinYMid meet" viewBox="0 0 15 15"> + <path stroke="#bbb" + d="M2 9 l 23 0" stroke-linecap="round" stroke-dasharray="2, 2" /> + </svg> + </td> + <td>Control dependency edge</td> + </tr> + <tr> + <td> + <svg class="icon" height="15px" + preserveAspectRatio="xMinYMid meet" viewBox="0 0 15 15"> + <path marker-start="url(#ref-arrowhead-legend)" + stroke="#bbb" d="M2 9 l 23 0" + stroke-linecap="round" /> + </svg> + </td> + <td>Reference edge</td> + </tr> + </table> + </div> + </template> </div> </template> <script> (function() { // Private scope. +/** + * Stats from device names that match these regexes will be excluded by default. + * The user can still turn on a device by selecting the checkbox in the device list. + * See b/29089982 for context. + */ +var DEVICE_NAMES_EXCLUDE = [ + { + regex: /gpu:[0-9]+$/, + msg: 'Excluded by default since this is a CPU thread setting up GPU kernels.' + } +]; + Polymer({ is: 'tf-graph-controls', properties: { // Public API. - stats: Object, + stats: { + value: null, + type: Object, + observer: '_statsChanged' + }, + devicesForStats: { + value: null, + type: Object, + notify: true, + readonly: true, + }, colorBy: { type: String, value: 'structure', @@ -486,6 +542,55 @@ Polymer({ _statsNotNull: function(stats) { return stats != null; }, + _statsChanged: function(stats) { + if (stats == null) { + return; + } + var devicesForStats = {}; + var devices = _.each(stats.dev_stats, function(d) { + // Avoid device names that are ignored by default. + var exclude = _.some(DEVICE_NAMES_EXCLUDE, function(rule) { + return rule.regex.test(d.device); + }); + if (!exclude) { + devicesForStats[d.device] = true; + } + }); + this.set('devicesForStats', devicesForStats); + }, + _getDevices: function(devicesForStats) { + var devices = _.map(this.stats.dev_stats, function(d) { + return d.device; + }); + // Devices names can be long so we remove the longest common prefix + // before showing the devices in a list. + var suffixes = tf.graph.util.removeCommonPrefix(devices); + return _.map(devices, function(device, i) { + var ignoredMsg = null; + _.each(DEVICE_NAMES_EXCLUDE, function(rule) { + if (rule.regex.test(device)) { + ignoredMsg = rule.msg; + } + }); + return { + device: device, + suffix: suffixes[i], + used: devicesForStats[device], + ignoredMsg: ignoredMsg + }; + }); + }, + _deviceCheckboxClicked: function(checkbox) { + // Update the device map. + var devicesForStats = _.extend({}, this.devicesForStats); + var device = checkbox.target.value; + if (checkbox.target.checked) { + devicesForStats[device] = true; + } else { + delete devicesForStats[device]; + } + this.set('devicesForStats', devicesForStats); + }, _numSessionRuns: function(metadataTags) { return metadataTags != null ? metadataTags.length : 0; }, @@ -495,14 +600,15 @@ Polymer({ fit: function() { document.querySelector('#scene').fit(); }, - _isGradientColoring: function(colorBy) { - return ["compute_time", "memory"].indexOf(colorBy) !== -1; + _isGradientColoring: function(stats, colorBy) { + return ["compute_time", "memory"].indexOf(colorBy) !== -1 + && stats != null; }, _equals: function(a, b) { return a === b; }, _getCurrentGradientParams: function(colorByParams, colorBy) { - if (!this._isGradientColoring(colorBy)) { + if (!this._isGradientColoring(this.stats, colorBy)) { return; } var params = colorByParams[colorBy]; diff --git a/tensorflow/tensorboard/components/tf-graph/tf-graph.html b/tensorflow/tensorboard/components/tf-graph/tf-graph.html index bf7785bd20..883ce5d14c 100644 --- a/tensorflow/tensorboard/components/tf-graph/tf-graph.html +++ b/tensorflow/tensorboard/components/tf-graph/tf-graph.html @@ -62,10 +62,8 @@ Polymer({ observer: '_graphChanged' }, basicGraph: Object, - stats: { - type: Object, - observer: '_statsChanged' - }, + stats: Object, + devicesForStats: Object, hierarchyParams: Object, progress: { type: Object, @@ -102,15 +100,15 @@ Polymer({ }, }, observers: [ + '_statsChanged(stats, devicesForStats)', '_buildRenderHierarchy(graphHierarchy)' ], - _statsChanged: function(stats) { + _statsChanged: function(stats, devicesForStats) { if (this.graphHierarchy) { - if (stats != null) { - tf.graph.joinStatsInfoWithGraph(this.basicGraph, stats); + if (stats && devicesForStats) { + tf.graph.joinStatsInfoWithGraph(this.basicGraph, stats, devicesForStats); tf.graph.hierarchy.joinAndAggregateStats(this.graphHierarchy, stats); } - // Recompute the rendering information. this._buildRenderHierarchy(this.graphHierarchy); } |