diff --git a/tensorflow/tensorboard/components/tf-graph-common/lib/common.ts b/tensorflow/tensorboard/components/tf-graph-common/lib/common.ts index 1ed300b03e3..4d970f1defd 100644 --- a/tensorflow/tensorboard/components/tf-graph-common/lib/common.ts +++ b/tensorflow/tensorboard/components/tf-graph-common/lib/common.ts @@ -13,228 +13,102 @@ See the License for the specific language governing permissions and limitations under the License. ==============================================================================*/ +/** + * @fileoverview Common interfaces for the tensorflow graph visualizer. + */ + module tf { -/** - * Recommended delay (ms) when running an expensive task asynchronously - * that gives enough time for the progress bar to update its UI. - */ -const ASYNC_TASK_DELAY = 20; - -export function time(msg: string, task: () => T) { - let start = Date.now(); - let result = task(); - /* tslint:disable */ - console.log(msg, ':', Date.now() - start, 'ms'); - /* tslint:enable */ - return result; -} - -/** - * Tracks task progress. Each task being passed a progress tracker needs - * to call the below-defined methods to notify the caller about the gradual - * progress of the task. - */ -export interface ProgressTracker { - updateProgress(incrementValue: number): void; - setMessage(msg: string): void; - reportError(msg: string, err: Error): void; -} - -/** - * Creates a tracker that sets the progress property of the - * provided polymer component. The provided component must have - * a property called 'progress' that is not read-only. The progress - * property is an object with a numerical 'value' property and a - * string 'msg' property. - */ -export function getTracker(polymerComponent: any) { - return { - setMessage: function(msg) { - polymerComponent.set( - 'progress', {value: polymerComponent.progress.value, msg: msg}); - }, - updateProgress: function(value) { - polymerComponent.set('progress', { - value: polymerComponent.progress.value + value, - msg: polymerComponent.progress.msg - }); - }, - reportError: function(msg: string, err) { - // Log the stack trace in the console. - console.error(err.stack); - // And send a user-friendly message to the UI. - polymerComponent.set( - 'progress', - {value: polymerComponent.progress.value, msg: msg, error: true}); - }, - }; -} - -/** - * Creates a tracker for a subtask given the parent tracker, the total progress - * of the subtask and the subtask message. The parent task should pass a - * subtracker to its subtasks. The subtask reports its own progress which - * becames relative to the main task. - */ -export function getSubtaskTracker(parentTracker: ProgressTracker, - impactOnTotalProgress: number, subtaskMsg: string): ProgressTracker { - return { - setMessage: function(progressMsg) { - // The parent should show a concatenation of its message along with - // its subtask tracker message. - parentTracker.setMessage(subtaskMsg + ': ' + progressMsg); - }, - updateProgress: function(incrementValue) { - // Update the parent progress relative to the child progress. - // For example, if the sub-task progresses by 30%, and the impact on the - // total progress is 50%, then the task progresses by 30% * 50% = 15%. - parentTracker - .updateProgress(incrementValue * impactOnTotalProgress / 100); - }, - reportError: function(msg: string, err: Error) { - // The parent should show a concatenation of its message along with - // its subtask error message. - parentTracker.reportError(subtaskMsg + ': ' + msg, err); - } - }; -} - -/** - * Runs an expensive task and return the result. - */ -export function runTask(msg: string, incProgressValue: number, - task: () => T, tracker: ProgressTracker): T { - // Update the progress message to say the current running task. - tracker.setMessage(msg); - // Run the expensive task with a delay that gives enough time for the - // UI to update. - try { - var result = tf.time(msg, task); - // Update the progress value. - tracker.updateProgress(incProgressValue); - // Return the result to be used by other tasks. - return result; - } catch (e) { - // Errors that happen inside asynchronous tasks are - // reported to the tracker using a user-friendly message. - tracker.reportError('Failed ' + msg, e); + /** + * Tracks task progress. Each task being passed a progress tracker needs + * to call the below-defined methods to notify the caller about the gradual + * progress of the task. + */ + export interface ProgressTracker { + updateProgress(incrementValue: number): void; + setMessage(msg: string): void; + reportError(msg: string, err: Error): void; } -} -/** - * Runs an expensive task asynchronously and returns a promise of the result. - */ -export function runAsyncTask(msg: string, incProgressValue: number, - task: () => T, tracker: ProgressTracker): Promise { - return new Promise((resolve, reject) => { - // Update the progress message to say the current running task. - tracker.setMessage(msg); - // Run the expensive task with a delay that gives enough time for the - // UI to update. - setTimeout(function() { - try { - var result = tf.time(msg, task); - // Update the progress value. - tracker.updateProgress(incProgressValue); - // Return the result to be used by other tasks. - resolve(result); - } catch (e) { - // Errors that happen inside asynchronous tasks are - // reported to the tracker using a user-friendly message. - tracker.reportError('Failed ' + msg, e); - } - }, ASYNC_TASK_DELAY); - }); -} + /** + * TensorFlow node definition as defined in the graph proto file. + */ + export interface TFNode { + /** Name of the node */ + name: string; + /** List of nodes that are inputs for this node. */ + input: string[]; + /** The name of the device where the computation will run. */ + device: string; + /** The name of the operation associated with this node. */ + op: string; + /** List of attributes that describe/modify the operation. */ + attr: {key: string, value: Object}[]; + } -/** - * Returns a query selector with escaped special characters that are not - * allowed in a query selector. - */ -export function escapeQuerySelector(querySelector: string): string { - return querySelector.replace(/([:.\[\],/\\\(\)])/g, '\\$1'); -} + /** + * TensorFlow stats file definition as defined in the stats proto file. + */ + export interface StepStats { + dev_stats: {device: string, node_stats: NodeStats[]}[]; + } -/** - * TensorFlow node definition as defined in the graph proto file. - */ -export interface TFNode { - /** Name of the node */ - name: string; - /** List of nodes that are inputs for this node. */ - input: string[]; - /** The name of the device where the computation will run. */ - device: string; - /** The name of the operation associated with this node. */ - op: string; - /** List of attributes that describe/modify the operation. */ - attr: {key: string, value: Object}[]; -} - -/** - * TensorFlow stats file definition as defined in the stats proto file. - */ -export interface StepStats { - dev_stats: {device: string, node_stats: NodeStats[]}[]; -} - -/** - * TensorFlow stats for a node as defined in the stats proto file. - */ -export interface NodeStats { - node_name: string; - // The next 4 properties are currently stored as string in json - // and must be parsed. - all_start_micros: number; - op_start_rel_micros: number; - op_end_rel_micros: number; - all_end_rel_micros: number; - memory: { - allocator_name: string; - total_bytes: number; // Stored as string in json and should be parsed. - peak_bytes: number; // Stored as string in json and should be parsed. - }[]; - /** Output sizes recorded for a single execution of a graph node */ - output: TFNodeOutput[]; - timeline_label: string; - scheduled_micros: string; - thread_id: string; -} - -/** - * Description for the output tensor(s) of an operation in the graph. - */ -export interface TFNodeOutput { - slot: number; // Stored as string in json and should be parsed. - tensor_description: { - /** Data type of tensor elements */ - dtype: string; - /** Shape of the tensor */ - shape: { - /** - * Dimensions of the tensor, such as [{name: 'input', size: 30}, - * {name: 'output', size: 40}] for a 30 x 40 2D tensor. The names - * are optional. The order of entries in 'dim' matters: It indicates - * the layout of the values in the tensor in-memory representation. - */ - dim: { - /** Size of the tensor in that dimension */ - size: number, // Stored as string in json and should be parsed. - /** Optional name of the tensor dimension */ - name?: string - }[]; - }; - /** Information about the size and allocator used for the data */ - allocation_description: { - // The next 2 properties are stored as string in json and - // should be parsed. - /** Total number of bytes requested */ - requested_bytes: number; - /** Total number of bytes allocated, if known */ - allocated_bytes?: number; - /** Name of the allocator used */ + /** + * TensorFlow stats for a node as defined in the stats proto file. + */ + export interface NodeStats { + node_name: string; + // The next 4 properties are currently stored as string in json + // and must be parsed. + all_start_micros: number; + op_start_rel_micros: number; + op_end_rel_micros: number; + all_end_rel_micros: number; + memory: { allocator_name: string; + total_bytes: number; // Stored as string in json and should be parsed. + peak_bytes: number; // Stored as string in json and should be parsed. + }[]; + /** Output sizes recorded for a single execution of a graph node */ + output: TFNodeOutput[]; + timeline_label: string; + scheduled_micros: string; + thread_id: string; + } + + /** + * Description for the output tensor(s) of an operation in the graph. + */ + export interface TFNodeOutput { + slot: number; // Stored as string in json and should be parsed. + tensor_description: { + /** Data type of tensor elements */ + dtype: string; + /** Shape of the tensor */ + shape: { + /** + * Dimensions of the tensor, such as [{name: 'input', size: 30}, + * {name: 'output', size: 40}] for a 30 x 40 2D tensor. The names + * are optional. The order of entries in 'dim' matters: It indicates + * the layout of the values in the tensor in-memory representation. + */ + dim: { + /** Size of the tensor in that dimension */ + size: number, // Stored as string in json and should be parsed. + /** Optional name of the tensor dimension */ + name?: string + }[]; + }; + /** Information about the size and allocator used for the data */ + allocation_description: { + // The next 2 properties are stored as string in json and + // should be parsed. + /** Total number of bytes requested */ + requested_bytes: number; + /** Total number of bytes allocated, if known */ + allocated_bytes?: number; + /** Name of the allocator used */ + allocator_name: string; + }; }; - }; -} + } } // close module tf diff --git a/tensorflow/tensorboard/components/tf-graph-common/lib/graph.ts b/tensorflow/tensorboard/components/tf-graph-common/lib/graph.ts index 066522f5001..4f2c703f412 100644 --- a/tensorflow/tensorboard/components/tf-graph-common/lib/graph.ts +++ b/tensorflow/tensorboard/components/tf-graph-common/lib/graph.ts @@ -855,100 +855,115 @@ export function build(rawNodes: tf.TFNode[], params: BuildParams, */ let nodeNames = new Array(rawNodes.length); - return runAsyncTask('Normalizing names', 30, () => { - let opNodes = new Array(rawNodes.length); - let index = 0; - _.each(rawNodes, rawNode => { - let opNode = new OpNodeImpl(rawNode); - if (isInEmbeddedPred(opNode)) { - embeddingNodeNames.push(opNode.name); - inEmbedding[opNode.name] = opNode; - return; - } + return tf.graph.util + .runAsyncTask( + 'Normalizing names', 30, + () => { + let opNodes = new Array(rawNodes.length); + let index = 0; + _.each(rawNodes, rawNode => { + let opNode = new OpNodeImpl(rawNode); + if (isInEmbeddedPred(opNode)) { + embeddingNodeNames.push(opNode.name); + inEmbedding[opNode.name] = opNode; + return; + } - if (isOutEmbeddedPred(opNode)) { - embeddingNodeNames.push(opNode.name); - outEmbedding[opNode.name] = opNode; - _.each(opNode.inputs, input => { - let inputName = input.name; - outEmbeddings[inputName] = outEmbeddings[inputName] || []; - outEmbeddings[inputName].push(opNode); - }); - return; - } - // The node is not an embedding, so add it to the names and nodes - // lists. - opNodes[index] = opNode; - nodeNames[index] = opNode.name; - index++; - }); - opNodes.splice(index); - nodeNames.splice(index); - return opNodes; - }, tracker).then((opNodes) => { - // Create the graph data structure from the graphlib library. - return runAsyncTask('Building the data structure', 70, () => { - let normalizedNameDict = mapStrictHierarchy(nodeNames, - embeddingNodeNames); - let graph = new SlimGraph; + if (isOutEmbeddedPred(opNode)) { + embeddingNodeNames.push(opNode.name); + outEmbedding[opNode.name] = opNode; + _.each(opNode.inputs, input => { + let inputName = input.name; + outEmbeddings[inputName] = outEmbeddings[inputName] || []; + outEmbeddings[inputName].push(opNode); + }); + return; + } + // The node is not an embedding, so add it to the names and nodes + // lists. + opNodes[index] = opNode; + nodeNames[index] = opNode.name; + index++; + }); + opNodes.splice(index); + nodeNames.splice(index); + return opNodes; + }, + tracker) + .then((opNodes) => { + // Create the graph data structure from the graphlib library. + return tf.graph.util.runAsyncTask( + 'Building the data structure', 70, () => { + let normalizedNameDict = + mapStrictHierarchy(nodeNames, embeddingNodeNames); + let graph = new SlimGraph; - // Add the nodes to the graph. - _.each(opNodes, opNode => { - let normalizedName = normalizedNameDict[opNode.name] || opNode.name; - graph.nodes[normalizedName] = opNode; - // Check if the node has out-embeddings. If yes, add them to the - // node. - if (opNode.name in outEmbeddings) { - opNode.outEmbeddings = outEmbeddings[opNode.name]; - // Normalize the names of the out-embeddings. - _.each(opNode.outEmbeddings, node => { - node.name = normalizedNameDict[node.name] || node.name; - }); - } - // Update the name of the node. - opNode.name = normalizedName; + // Add the nodes to the graph. + _.each(opNodes, opNode => { + let normalizedName = + normalizedNameDict[opNode.name] || opNode.name; + graph.nodes[normalizedName] = opNode; + // Check if the node has out-embeddings. If yes, add them to the + // node. + if (opNode.name in outEmbeddings) { + opNode.outEmbeddings = outEmbeddings[opNode.name]; + // Normalize the names of the out-embeddings. + _.each(opNode.outEmbeddings, node => { + node.name = normalizedNameDict[node.name] || node.name; + }); + } + // Update the name of the node. + opNode.name = normalizedName; + }); + + // Visit each node's inputs to add the edges to the graph. If the + // input + // is an in-embedding, then add it to the node's in-embeddings + // instead. + _.each(opNodes, opNode => { + _.each(opNode.inputs, (input, i) => { + let inputName = input.name; + if (inputName in inEmbedding) { + let inEmbedNode = inEmbedding[inputName]; + opNode.inEmbeddings.push(inEmbedNode); + // Move the inputs of the in-embedding node into incoming + // edges of + // the main node. E.g. the control dependency of a constant + // node + // should be moved to the op node where the constant is + // embedded. + for (let embedInput of inEmbedNode.inputs) { + addEdgeToGraph( + graph, normalizedNameDict[embedInput.name] || + embedInput.name, + opNode, embedInput.isControlDependency, params, i); + } + } else if (inputName in outEmbedding) { + // Move the inputs of the out-embedding node into inputs of + // the main node where the out-embedding points to. + let outEmbedNode = outEmbedding[inputName]; + for (let embedInput of outEmbedNode.inputs) { + addEdgeToGraph( + graph, normalizedNameDict[embedInput.name] || + embedInput.name, + opNode, input.isControlDependency, params, i); + } + } else { + addEdgeToGraph( + graph, normalizedNameDict[inputName] || inputName, + opNode, input.isControlDependency, params, i); + } + }); + }); + + // Normalize the names of in-embeddings. + _.each(inEmbedding, (node, name) => { + node.name = normalizedNameDict[node.name] || node.name; + }); + + return graph; + }, tracker); }); - - // Visit each node's inputs to add the edges to the graph. If the input - // is an in-embedding, then add it to the node's in-embeddings instead. - _.each(opNodes, opNode => { - _.each(opNode.inputs, (input, i) => { - let inputName = input.name; - if (inputName in inEmbedding) { - let inEmbedNode = inEmbedding[inputName]; - opNode.inEmbeddings.push(inEmbedNode); - // Move the inputs of the in-embedding node into incoming edges of - // the main node. E.g. the control dependency of a constant node - // should be moved to the op node where the constant is embedded. - for (let embedInput of inEmbedNode.inputs) { - addEdgeToGraph(graph, - normalizedNameDict[embedInput.name] || embedInput.name, - opNode, embedInput.isControlDependency, params, i); - } - } else if (inputName in outEmbedding) { - // Move the inputs of the out-embedding node into inputs of - // the main node where the out-embedding points to. - let outEmbedNode = outEmbedding[inputName]; - for (let embedInput of outEmbedNode.inputs) { - addEdgeToGraph(graph, - normalizedNameDict[embedInput.name] || embedInput.name, - opNode, input.isControlDependency, params, i); - } - } else { - addEdgeToGraph(graph, normalizedNameDict[inputName] || inputName, - opNode, input.isControlDependency, params, i); - } - }); - }); - - // Normalize the names of in-embeddings. - _.each(inEmbedding, (node, name) => { - node.name = normalizedNameDict[node.name] || node.name; - }); - - return graph; - }, tracker); - }); }; /** diff --git a/tensorflow/tensorboard/components/tf-graph-common/lib/hierarchy.ts b/tensorflow/tensorboard/components/tf-graph-common/lib/hierarchy.ts index 5723899596e..687f7af008b 100644 --- a/tensorflow/tensorboard/components/tf-graph-common/lib/hierarchy.ts +++ b/tensorflow/tensorboard/components/tf-graph-common/lib/hierarchy.ts @@ -400,22 +400,23 @@ export function build(graph: tf.graph.SlimGraph, params: HierarchyParams, tracker: ProgressTracker): Promise { let h = new HierarchyImpl(); let seriesNames: { [name: string]: string } = {}; - return runAsyncTask( - 'Adding nodes', 20, - () => { - // Get all the possible device names. - let deviceNames = {}; - _.each(graph.nodes, (node, nodeName) => { - if (node.device != null) { - deviceNames[node.device] = true; - } - }); - h.devices = _.keys(deviceNames); - addNodes(h, graph); - }, - tracker) + return tf.graph.util + .runAsyncTask( + 'Adding nodes', 20, + () => { + // Get all the possible device names. + let deviceNames = {}; + _.each(graph.nodes, (node, nodeName) => { + if (node.device != null) { + deviceNames[node.device] = true; + } + }); + h.devices = _.keys(deviceNames); + addNodes(h, graph); + }, + tracker) .then(() => { - return runAsyncTask('Detect series', 20, () => { + return tf.graph.util.runAsyncTask('Detect series', 20, () => { if (params.seriesNodeMinSize > 0) { groupSeries( h.root, h, seriesNames, params.seriesNodeMinSize, @@ -424,14 +425,15 @@ export function build(graph: tf.graph.SlimGraph, params: HierarchyParams, }, tracker); }) .then(() => { - return runAsyncTask('Adding edges', 30, () => { + return tf.graph.util.runAsyncTask('Adding edges', 30, () => { addEdges(h, graph, seriesNames); }, tracker); }) .then(() => { - return runAsyncTask('Finding similar subgraphs', 30, () => { - h.templates = template.detect(h, params.verifyTemplate); - }, tracker); + return tf.graph.util.runAsyncTask( + 'Finding similar subgraphs', 30, () => { + h.templates = template.detect(h, params.verifyTemplate); + }, tracker); }) .then(() => { return h; }); }; diff --git a/tensorflow/tensorboard/components/tf-graph-common/lib/parser.ts b/tensorflow/tensorboard/components/tf-graph-common/lib/parser.ts index 5611ff0708c..5680417da48 100644 --- a/tensorflow/tensorboard/components/tf-graph-common/lib/parser.ts +++ b/tensorflow/tensorboard/components/tf-graph-common/lib/parser.ts @@ -53,16 +53,21 @@ export function fetchPbTxt(filepath: string): Promise { * Fetches the metadata file, parses it and returns a promise of the result. */ export function fetchAndParseMetadata(path: string, tracker: ProgressTracker) { - return runTask('Reading metadata pbtxt', 40, () => { - if (path == null) { - return Promise.resolve(null); - } - return fetchPbTxt(path).then(text => new Blob([text])); - }, tracker).then((blob: Blob) => { - return runTask('Parsing metadata.pbtxt', 60, () => { - return blob != null ? parseStatsPbTxt(blob) : null; - }, tracker); - }); + return tf.graph.util + .runTask( + 'Reading metadata pbtxt', 40, + () => { + if (path == null) { + return Promise.resolve(null); + } + return fetchPbTxt(path).then(text => new Blob([text])); + }, + tracker) + .then((blob: Blob) => { + return tf.graph.util.runTask('Parsing metadata.pbtxt', 60, () => { + return blob != null ? parseStatsPbTxt(blob) : null; + }, tracker); + }); } /** @@ -70,14 +75,19 @@ export function fetchAndParseMetadata(path: string, tracker: ProgressTracker) { */ export function fetchAndParseGraphData(path: string, pbTxtFile: Blob, tracker: ProgressTracker) { - return runTask('Reading graph pbtxt', 40, () => { - return pbTxtFile ? Promise.resolve(pbTxtFile) : - fetchPbTxt(path).then(text => new Blob([text])); - }, tracker).then(blob => { - return runTask('Parsing graph.pbtxt', 60, () => { - return parseGraphPbTxt(blob); - }, tracker); - }); + return tf.graph.util + .runTask( + 'Reading graph pbtxt', 40, + () => { + return pbTxtFile ? Promise.resolve(pbTxtFile) : + fetchPbTxt(path).then(text => new Blob([text])); + }, + tracker) + .then(blob => { + return tf.graph.util.runTask('Parsing graph.pbtxt', 60, () => { + return parseGraphPbTxt(blob); + }, tracker); + }); } /** diff --git a/tensorflow/tensorboard/components/tf-graph-common/lib/scene/node.ts b/tensorflow/tensorboard/components/tf-graph-common/lib/scene/node.ts index 7dcd60ea3cf..8b59b9fb2b0 100644 --- a/tensorflow/tensorboard/components/tf-graph-common/lib/scene/node.ts +++ b/tensorflow/tensorboard/components/tf-graph-common/lib/scene/node.ts @@ -544,7 +544,7 @@ export function getFillForNode(templateIndex, colorBy, return colorParams.UNKNOWN; } let id = renderInfo.node.name; - let escapedId = tf.escapeQuerySelector(id); + let escapedId = tf.graph.util.escapeQuerySelector(id); let gradientDefs = d3.select('svg#svg defs #linearGradients'); let linearGradient = gradientDefs.select('linearGradient#' + escapedId); // If the linear gradient is not there yet, create it. diff --git a/tensorflow/tensorboard/components/tf-graph-common/lib/util.ts b/tensorflow/tensorboard/components/tf-graph-common/lib/util.ts new file mode 100644 index 00000000000..51ce06b76fa --- /dev/null +++ b/tensorflow/tensorboard/components/tf-graph-common/lib/util.ts @@ -0,0 +1,154 @@ +/* Copyright 2015 Google Inc. 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. +==============================================================================*/ + +/** + * @fileoverview Utility functions for the tensorflow graph visualizer. + */ + +module tf.graph.util { + /** + * Recommended delay (ms) when running an expensive task asynchronously + * that gives enough time for the progress bar to update its UI. + */ + const ASYNC_TASK_DELAY = 20; + + export function time(msg: string, task: () => T) { + let start = Date.now(); + let result = task(); + /* tslint:disable */ + console.log(msg, ':', Date.now() - start, 'ms'); + /* tslint:enable */ + return result; + } + + /** + * Creates a tracker that sets the progress property of the + * provided polymer component. The provided component must have + * a property called 'progress' that is not read-only. The progress + * property is an object with a numerical 'value' property and a + * string 'msg' property. + */ + export function getTracker(polymerComponent: any) { + return { + setMessage: function(msg) { + polymerComponent.set( + 'progress', {value: polymerComponent.progress.value, msg: msg}); + }, + updateProgress: function(value) { + polymerComponent.set('progress', { + value: polymerComponent.progress.value + value, + msg: polymerComponent.progress.msg + }); + }, + reportError: function(msg: string, err) { + // Log the stack trace in the console. + console.error(err.stack); + // And send a user-friendly message to the UI. + polymerComponent.set( + 'progress', + {value: polymerComponent.progress.value, msg: msg, error: true}); + }, + }; + } + + /** + * Creates a tracker for a subtask given the parent tracker, the total + * progress + * of the subtask and the subtask message. The parent task should pass a + * subtracker to its subtasks. The subtask reports its own progress which + * becames relative to the main task. + */ + export function getSubtaskTracker( + parentTracker: ProgressTracker, impactOnTotalProgress: number, + subtaskMsg: string): ProgressTracker { + return { + setMessage: function(progressMsg) { + // The parent should show a concatenation of its message along with + // its subtask tracker message. + parentTracker.setMessage(subtaskMsg + ': ' + progressMsg); + }, + updateProgress: function(incrementValue) { + // Update the parent progress relative to the child progress. + // For example, if the sub-task progresses by 30%, and the impact on the + // total progress is 50%, then the task progresses by 30% * 50% = 15%. + parentTracker.updateProgress( + incrementValue * impactOnTotalProgress / 100); + }, + reportError: function(msg: string, err: Error) { + // The parent should show a concatenation of its message along with + // its subtask error message. + parentTracker.reportError(subtaskMsg + ': ' + msg, err); + } + }; + } + + /** + * Runs an expensive task and return the result. + */ + export function runTask( + msg: string, incProgressValue: number, task: () => T, + tracker: ProgressTracker): T { + // Update the progress message to say the current running task. + tracker.setMessage(msg); + // Run the expensive task with a delay that gives enough time for the + // UI to update. + try { + let result = tf.graph.util.time(msg, task); + // Update the progress value. + tracker.updateProgress(incProgressValue); + // Return the result to be used by other tasks. + return result; + } catch (e) { + // Errors that happen inside asynchronous tasks are + // reported to the tracker using a user-friendly message. + tracker.reportError('Failed ' + msg, e); + } + } + + /** + * Runs an expensive task asynchronously and returns a promise of the result. + */ + export function runAsyncTask( + msg: string, incProgressValue: number, task: () => T, + tracker: ProgressTracker): Promise { + return new Promise((resolve, reject) => { + // Update the progress message to say the current running task. + tracker.setMessage(msg); + // Run the expensive task with a delay that gives enough time for the + // UI to update. + setTimeout(function() { + try { + let result = tf.graph.util.time(msg, task); + // Update the progress value. + tracker.updateProgress(incProgressValue); + // Return the result to be used by other tasks. + resolve(result); + } catch (e) { + // Errors that happen inside asynchronous tasks are + // reported to the tracker using a user-friendly message. + tracker.reportError('Failed ' + msg, e); + } + }, ASYNC_TASK_DELAY); + }); + } + + /** + * Returns a query selector with escaped special characters that are not + * allowed in a query selector. + */ + export function escapeQuerySelector(querySelector: string): string { + return querySelector.replace(/([:.\[\],/\\\(\)])/g, '\\$1'); + } +} diff --git a/tensorflow/tensorboard/components/tf-graph-common/tf-graph-common.html b/tensorflow/tensorboard/components/tf-graph-common/tf-graph-common.html index d3fc4c2ef1d..7136f8adcab 100644 --- a/tensorflow/tensorboard/components/tf-graph-common/tf-graph-common.html +++ b/tensorflow/tensorboard/components/tf-graph-common/tf-graph-common.html @@ -3,17 +3,18 @@ + - + + - - + - - - + + + diff --git a/tensorflow/tensorboard/components/tf-graph-loader/tf-graph-loader.html b/tensorflow/tensorboard/components/tf-graph-loader/tf-graph-loader.html index 5f9dc847be9..cf83d090dda 100644 --- a/tensorflow/tensorboard/components/tf-graph-loader/tf-graph-loader.html +++ b/tensorflow/tensorboard/components/tf-graph-loader/tf-graph-loader.html @@ -66,7 +66,7 @@ Polymer({ value: 0, msg: '' }); - var tracker = tf.getTracker(this); + var tracker = tf.graph.util.getTracker(this); tf.graph.parser.fetchAndParseMetadata(path, tracker) .then(function(stats) { this._setOutStats(stats); @@ -78,7 +78,7 @@ Polymer({ value: 0, msg: '' }); - var tracker = tf.getTracker(this); + var tracker = tf.graph.util.getTracker(this); var hierarchyParams = { verifyTemplate: true, // If a set of numbered op nodes has at least this number of nodes @@ -91,7 +91,7 @@ Polymer({ seriesMap: {}, }; this._setOutHierarchyParams(hierarchyParams); - var dataTracker = tf.getSubtaskTracker(tracker, 30, 'Data'); + var dataTracker = tf.graph.util.getSubtaskTracker(tracker, 30, 'Data'); tf.graph.parser.fetchAndParseGraphData(path, pbTxtFile, dataTracker) .then(function(graph) { // Build the flat graph (consists only of Op nodes). @@ -119,12 +119,12 @@ Polymer({ outEmbeddingTypes: ['^[a-zA-Z]+Summary$'], refEdges: refEdges }; - var graphTracker = tf.getSubtaskTracker(tracker, 20, 'Graph'); + var graphTracker = tf.graph.util.getSubtaskTracker(tracker, 20, 'Graph'); return tf.graph.build(graph, buildParams, graphTracker); }) .then(function(graph) { this._setOutGraph(graph); - var hierarchyTracker = tf.getSubtaskTracker(tracker, 50, + var hierarchyTracker = tf.graph.util.getSubtaskTracker(tracker, 50, 'Namespace hierarchy'); return tf.graph.hierarchy.build(graph, hierarchyParams, hierarchyTracker); }.bind(this)) diff --git a/tensorflow/tensorboard/components/tf-graph/tf-graph-scene.html b/tensorflow/tensorboard/components/tf-graph/tf-graph-scene.html index 2e977d26994..fc034c6f80b 100644 --- a/tensorflow/tensorboard/components/tf-graph/tf-graph-scene.html +++ b/tensorflow/tensorboard/components/tf-graph/tf-graph-scene.html @@ -569,12 +569,12 @@ Polymer({ /** Main method for building the scene */ _build: function(renderHierarchy) { this.templateIndex = renderHierarchy.hierarchy.getTemplateIndex(); - tf.time('tf-graph-scene (layout):', function() { + tf.graph.util.time('tf-graph-scene (layout):', function() { // layout the scene for this meta / series node tf.graph.layout.layoutScene(renderHierarchy.root, this); }.bind(this)); - tf.time('tf-graph-scene (build scene):', function() { + tf.graph.util.time('tf-graph-scene (build scene):', function() { tf.graph.scene.buildGroup(d3.select(this.$.root), renderHierarchy.root, this); tf.graph.scene.addGraphClickListener(this.$.svg, this); }.bind(this)); diff --git a/tensorflow/tensorboard/components/tf-graph/tf-graph.html b/tensorflow/tensorboard/components/tf-graph/tf-graph.html index d0bc9c55fbd..a7ef75b95dc 100644 --- a/tensorflow/tensorboard/components/tf-graph/tf-graph.html +++ b/tensorflow/tensorboard/components/tf-graph/tf-graph.html @@ -113,7 +113,7 @@ Polymer({ } }, _buildRenderHierarchy: function(graphHierarchy) { - tf.time('new tf.graph.render.Hierarchy', function() { + tf.graph.util.time('new tf.graph.render.Hierarchy', function() { if (graphHierarchy.root.type !== tf.graph.NodeType.META) { // root must be metanode but sometimes Polymer's dom-if has not // remove tf-graph element yet in @@ -265,8 +265,8 @@ Polymer({ value: 0, msg: '' }); - var tracker = tf.getTracker(this); - var hierarchyTracker = tf.getSubtaskTracker(tracker, 100, + var tracker = tf.graph.util.getTracker(this); + var hierarchyTracker = tf.graph.util.getSubtaskTracker(tracker, 100, 'Namespace hierarchy'); tf.graph.hierarchy.build(this.basicGraph, this.hierarchyParams, hierarchyTracker) .then(function(graphHierarchy) {