Move tensorflow graph visualizer utility functions to a util.ts file under a tf.graph.util namespace.
Change: 121883015
This commit is contained in:
parent
6e02bf0299
commit
d4f83e1c64
@ -13,152 +13,26 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
==============================================================================*/
|
==============================================================================*/
|
||||||
|
|
||||||
module tf {
|
|
||||||
/**
|
/**
|
||||||
* Recommended delay (ms) when running an expensive task asynchronously
|
* @fileoverview Common interfaces for the tensorflow graph visualizer.
|
||||||
* that gives enough time for the progress bar to update its UI.
|
|
||||||
*/
|
*/
|
||||||
const ASYNC_TASK_DELAY = 20;
|
|
||||||
|
|
||||||
export function time<T>(msg: string, task: () => T) {
|
module tf {
|
||||||
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
|
* Tracks task progress. Each task being passed a progress tracker needs
|
||||||
* to call the below-defined methods to notify the caller about the gradual
|
* to call the below-defined methods to notify the caller about the gradual
|
||||||
* progress of the task.
|
* progress of the task.
|
||||||
*/
|
*/
|
||||||
export interface ProgressTracker {
|
export interface ProgressTracker {
|
||||||
updateProgress(incrementValue: number): void;
|
updateProgress(incrementValue: number): void;
|
||||||
setMessage(msg: string): void;
|
setMessage(msg: string): void;
|
||||||
reportError(msg: string, err: Error): 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<T>(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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs an expensive task asynchronously and returns a promise of the result.
|
|
||||||
*/
|
|
||||||
export function runAsyncTask<T>(msg: string, incProgressValue: number,
|
|
||||||
task: () => T, tracker: ProgressTracker): Promise<T> {
|
|
||||||
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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 node definition as defined in the graph proto file.
|
* TensorFlow node definition as defined in the graph proto file.
|
||||||
*/
|
*/
|
||||||
export interface TFNode {
|
export interface TFNode {
|
||||||
/** Name of the node */
|
/** Name of the node */
|
||||||
name: string;
|
name: string;
|
||||||
/** List of nodes that are inputs for this node. */
|
/** List of nodes that are inputs for this node. */
|
||||||
@ -169,19 +43,19 @@ export interface TFNode {
|
|||||||
op: string;
|
op: string;
|
||||||
/** List of attributes that describe/modify the operation. */
|
/** List of attributes that describe/modify the operation. */
|
||||||
attr: {key: string, value: Object}[];
|
attr: {key: string, value: Object}[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TensorFlow stats file definition as defined in the stats proto file.
|
* TensorFlow stats file definition as defined in the stats proto file.
|
||||||
*/
|
*/
|
||||||
export interface StepStats {
|
export interface StepStats {
|
||||||
dev_stats: {device: string, node_stats: NodeStats[]}[];
|
dev_stats: {device: string, node_stats: NodeStats[]}[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TensorFlow stats for a node as defined in the stats proto file.
|
* TensorFlow stats for a node as defined in the stats proto file.
|
||||||
*/
|
*/
|
||||||
export interface NodeStats {
|
export interface NodeStats {
|
||||||
node_name: string;
|
node_name: string;
|
||||||
// The next 4 properties are currently stored as string in json
|
// The next 4 properties are currently stored as string in json
|
||||||
// and must be parsed.
|
// and must be parsed.
|
||||||
@ -199,12 +73,12 @@ export interface NodeStats {
|
|||||||
timeline_label: string;
|
timeline_label: string;
|
||||||
scheduled_micros: string;
|
scheduled_micros: string;
|
||||||
thread_id: string;
|
thread_id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Description for the output tensor(s) of an operation in the graph.
|
* Description for the output tensor(s) of an operation in the graph.
|
||||||
*/
|
*/
|
||||||
export interface TFNodeOutput {
|
export interface TFNodeOutput {
|
||||||
slot: number; // Stored as string in json and should be parsed.
|
slot: number; // Stored as string in json and should be parsed.
|
||||||
tensor_description: {
|
tensor_description: {
|
||||||
/** Data type of tensor elements */
|
/** Data type of tensor elements */
|
||||||
@ -236,5 +110,5 @@ export interface TFNodeOutput {
|
|||||||
allocator_name: string;
|
allocator_name: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} // close module tf
|
} // close module tf
|
||||||
|
|||||||
@ -855,7 +855,10 @@ export function build(rawNodes: tf.TFNode[], params: BuildParams,
|
|||||||
*/
|
*/
|
||||||
let nodeNames = new Array<string>(rawNodes.length);
|
let nodeNames = new Array<string>(rawNodes.length);
|
||||||
|
|
||||||
return runAsyncTask('Normalizing names', 30, () => {
|
return tf.graph.util
|
||||||
|
.runAsyncTask(
|
||||||
|
'Normalizing names', 30,
|
||||||
|
() => {
|
||||||
let opNodes = new Array<OpNode>(rawNodes.length);
|
let opNodes = new Array<OpNode>(rawNodes.length);
|
||||||
let index = 0;
|
let index = 0;
|
||||||
_.each(rawNodes, rawNode => {
|
_.each(rawNodes, rawNode => {
|
||||||
@ -885,16 +888,20 @@ export function build(rawNodes: tf.TFNode[], params: BuildParams,
|
|||||||
opNodes.splice(index);
|
opNodes.splice(index);
|
||||||
nodeNames.splice(index);
|
nodeNames.splice(index);
|
||||||
return opNodes;
|
return opNodes;
|
||||||
}, tracker).then((opNodes) => {
|
},
|
||||||
|
tracker)
|
||||||
|
.then((opNodes) => {
|
||||||
// Create the graph data structure from the graphlib library.
|
// Create the graph data structure from the graphlib library.
|
||||||
return runAsyncTask('Building the data structure', 70, () => {
|
return tf.graph.util.runAsyncTask(
|
||||||
let normalizedNameDict = mapStrictHierarchy(nodeNames,
|
'Building the data structure', 70, () => {
|
||||||
embeddingNodeNames);
|
let normalizedNameDict =
|
||||||
|
mapStrictHierarchy(nodeNames, embeddingNodeNames);
|
||||||
let graph = new SlimGraph;
|
let graph = new SlimGraph;
|
||||||
|
|
||||||
// Add the nodes to the graph.
|
// Add the nodes to the graph.
|
||||||
_.each(opNodes, opNode => {
|
_.each(opNodes, opNode => {
|
||||||
let normalizedName = normalizedNameDict[opNode.name] || opNode.name;
|
let normalizedName =
|
||||||
|
normalizedNameDict[opNode.name] || opNode.name;
|
||||||
graph.nodes[normalizedName] = opNode;
|
graph.nodes[normalizedName] = opNode;
|
||||||
// Check if the node has out-embeddings. If yes, add them to the
|
// Check if the node has out-embeddings. If yes, add them to the
|
||||||
// node.
|
// node.
|
||||||
@ -909,20 +916,26 @@ export function build(rawNodes: tf.TFNode[], params: BuildParams,
|
|||||||
opNode.name = normalizedName;
|
opNode.name = normalizedName;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Visit each node's inputs to add the edges to the graph. If the input
|
// Visit each node's inputs to add the edges to the graph. If the
|
||||||
// is an in-embedding, then add it to the node's in-embeddings instead.
|
// input
|
||||||
|
// is an in-embedding, then add it to the node's in-embeddings
|
||||||
|
// instead.
|
||||||
_.each(opNodes, opNode => {
|
_.each(opNodes, opNode => {
|
||||||
_.each(opNode.inputs, (input, i) => {
|
_.each(opNode.inputs, (input, i) => {
|
||||||
let inputName = input.name;
|
let inputName = input.name;
|
||||||
if (inputName in inEmbedding) {
|
if (inputName in inEmbedding) {
|
||||||
let inEmbedNode = inEmbedding[inputName];
|
let inEmbedNode = inEmbedding[inputName];
|
||||||
opNode.inEmbeddings.push(inEmbedNode);
|
opNode.inEmbeddings.push(inEmbedNode);
|
||||||
// Move the inputs of the in-embedding node into incoming edges of
|
// Move the inputs of the in-embedding node into incoming
|
||||||
// the main node. E.g. the control dependency of a constant node
|
// edges of
|
||||||
// should be moved to the op node where the constant is embedded.
|
// 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) {
|
for (let embedInput of inEmbedNode.inputs) {
|
||||||
addEdgeToGraph(graph,
|
addEdgeToGraph(
|
||||||
normalizedNameDict[embedInput.name] || embedInput.name,
|
graph, normalizedNameDict[embedInput.name] ||
|
||||||
|
embedInput.name,
|
||||||
opNode, embedInput.isControlDependency, params, i);
|
opNode, embedInput.isControlDependency, params, i);
|
||||||
}
|
}
|
||||||
} else if (inputName in outEmbedding) {
|
} else if (inputName in outEmbedding) {
|
||||||
@ -930,12 +943,14 @@ export function build(rawNodes: tf.TFNode[], params: BuildParams,
|
|||||||
// the main node where the out-embedding points to.
|
// the main node where the out-embedding points to.
|
||||||
let outEmbedNode = outEmbedding[inputName];
|
let outEmbedNode = outEmbedding[inputName];
|
||||||
for (let embedInput of outEmbedNode.inputs) {
|
for (let embedInput of outEmbedNode.inputs) {
|
||||||
addEdgeToGraph(graph,
|
addEdgeToGraph(
|
||||||
normalizedNameDict[embedInput.name] || embedInput.name,
|
graph, normalizedNameDict[embedInput.name] ||
|
||||||
|
embedInput.name,
|
||||||
opNode, input.isControlDependency, params, i);
|
opNode, input.isControlDependency, params, i);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
addEdgeToGraph(graph, normalizedNameDict[inputName] || inputName,
|
addEdgeToGraph(
|
||||||
|
graph, normalizedNameDict[inputName] || inputName,
|
||||||
opNode, input.isControlDependency, params, i);
|
opNode, input.isControlDependency, params, i);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -400,7 +400,8 @@ export function build(graph: tf.graph.SlimGraph, params: HierarchyParams,
|
|||||||
tracker: ProgressTracker): Promise<Hierarchy|void> {
|
tracker: ProgressTracker): Promise<Hierarchy|void> {
|
||||||
let h = new HierarchyImpl();
|
let h = new HierarchyImpl();
|
||||||
let seriesNames: { [name: string]: string } = {};
|
let seriesNames: { [name: string]: string } = {};
|
||||||
return runAsyncTask(
|
return tf.graph.util
|
||||||
|
.runAsyncTask(
|
||||||
'Adding nodes', 20,
|
'Adding nodes', 20,
|
||||||
() => {
|
() => {
|
||||||
// Get all the possible device names.
|
// Get all the possible device names.
|
||||||
@ -415,7 +416,7 @@ export function build(graph: tf.graph.SlimGraph, params: HierarchyParams,
|
|||||||
},
|
},
|
||||||
tracker)
|
tracker)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return runAsyncTask('Detect series', 20, () => {
|
return tf.graph.util.runAsyncTask('Detect series', 20, () => {
|
||||||
if (params.seriesNodeMinSize > 0) {
|
if (params.seriesNodeMinSize > 0) {
|
||||||
groupSeries(
|
groupSeries(
|
||||||
h.root, h, seriesNames, params.seriesNodeMinSize,
|
h.root, h, seriesNames, params.seriesNodeMinSize,
|
||||||
@ -424,12 +425,13 @@ export function build(graph: tf.graph.SlimGraph, params: HierarchyParams,
|
|||||||
}, tracker);
|
}, tracker);
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return runAsyncTask('Adding edges', 30, () => {
|
return tf.graph.util.runAsyncTask('Adding edges', 30, () => {
|
||||||
addEdges(h, graph, seriesNames);
|
addEdges(h, graph, seriesNames);
|
||||||
}, tracker);
|
}, tracker);
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return runAsyncTask('Finding similar subgraphs', 30, () => {
|
return tf.graph.util.runAsyncTask(
|
||||||
|
'Finding similar subgraphs', 30, () => {
|
||||||
h.templates = template.detect(h, params.verifyTemplate);
|
h.templates = template.detect(h, params.verifyTemplate);
|
||||||
}, tracker);
|
}, tracker);
|
||||||
})
|
})
|
||||||
|
|||||||
@ -53,13 +53,18 @@ export function fetchPbTxt(filepath: string): Promise<string> {
|
|||||||
* Fetches the metadata file, parses it and returns a promise of the result.
|
* Fetches the metadata file, parses it and returns a promise of the result.
|
||||||
*/
|
*/
|
||||||
export function fetchAndParseMetadata(path: string, tracker: ProgressTracker) {
|
export function fetchAndParseMetadata(path: string, tracker: ProgressTracker) {
|
||||||
return runTask('Reading metadata pbtxt', 40, () => {
|
return tf.graph.util
|
||||||
|
.runTask(
|
||||||
|
'Reading metadata pbtxt', 40,
|
||||||
|
() => {
|
||||||
if (path == null) {
|
if (path == null) {
|
||||||
return Promise.resolve(null);
|
return Promise.resolve(null);
|
||||||
}
|
}
|
||||||
return fetchPbTxt(path).then(text => new Blob([text]));
|
return fetchPbTxt(path).then(text => new Blob([text]));
|
||||||
}, tracker).then((blob: Blob) => {
|
},
|
||||||
return runTask('Parsing metadata.pbtxt', 60, () => {
|
tracker)
|
||||||
|
.then((blob: Blob) => {
|
||||||
|
return tf.graph.util.runTask('Parsing metadata.pbtxt', 60, () => {
|
||||||
return blob != null ? parseStatsPbTxt(blob) : null;
|
return blob != null ? parseStatsPbTxt(blob) : null;
|
||||||
}, tracker);
|
}, tracker);
|
||||||
});
|
});
|
||||||
@ -70,11 +75,16 @@ export function fetchAndParseMetadata(path: string, tracker: ProgressTracker) {
|
|||||||
*/
|
*/
|
||||||
export function fetchAndParseGraphData(path: string, pbTxtFile: Blob,
|
export function fetchAndParseGraphData(path: string, pbTxtFile: Blob,
|
||||||
tracker: ProgressTracker) {
|
tracker: ProgressTracker) {
|
||||||
return runTask('Reading graph pbtxt', 40, () => {
|
return tf.graph.util
|
||||||
|
.runTask(
|
||||||
|
'Reading graph pbtxt', 40,
|
||||||
|
() => {
|
||||||
return pbTxtFile ? Promise.resolve(pbTxtFile) :
|
return pbTxtFile ? Promise.resolve(pbTxtFile) :
|
||||||
fetchPbTxt(path).then(text => new Blob([text]));
|
fetchPbTxt(path).then(text => new Blob([text]));
|
||||||
}, tracker).then(blob => {
|
},
|
||||||
return runTask('Parsing graph.pbtxt', 60, () => {
|
tracker)
|
||||||
|
.then(blob => {
|
||||||
|
return tf.graph.util.runTask('Parsing graph.pbtxt', 60, () => {
|
||||||
return parseGraphPbTxt(blob);
|
return parseGraphPbTxt(blob);
|
||||||
}, tracker);
|
}, tracker);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -544,7 +544,7 @@ export function getFillForNode(templateIndex, colorBy,
|
|||||||
return colorParams.UNKNOWN;
|
return colorParams.UNKNOWN;
|
||||||
}
|
}
|
||||||
let id = renderInfo.node.name;
|
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 gradientDefs = d3.select('svg#svg defs #linearGradients');
|
||||||
let linearGradient = gradientDefs.select('linearGradient#' + escapedId);
|
let linearGradient = gradientDefs.select('linearGradient#' + escapedId);
|
||||||
// If the linear gradient is not there yet, create it.
|
// If the linear gradient is not there yet, create it.
|
||||||
|
|||||||
154
tensorflow/tensorboard/components/tf-graph-common/lib/util.ts
Normal file
154
tensorflow/tensorboard/components/tf-graph-common/lib/util.ts
Normal file
@ -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<T>(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<T>(
|
||||||
|
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<T>(
|
||||||
|
msg: string, incProgressValue: number, task: () => T,
|
||||||
|
tracker: ProgressTracker): Promise<T> {
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,17 +3,18 @@
|
|||||||
<link rel="import" href="../tf-imports/graphlib.html">
|
<link rel="import" href="../tf-imports/graphlib.html">
|
||||||
<link rel="import" href="../tf-imports/lodash.html">
|
<link rel="import" href="../tf-imports/lodash.html">
|
||||||
|
|
||||||
|
<script src="lib/colors.js"></script>
|
||||||
<script src="lib/common.js"></script>
|
<script src="lib/common.js"></script>
|
||||||
<script src="lib/externs.js"></script>
|
<script src="lib/externs.js"></script>
|
||||||
<script src="lib/graph.js"></script>
|
<script src="lib/graph.js"></script>
|
||||||
<script src="lib/parser.js"></script>
|
|
||||||
<script src="lib/hierarchy.js"></script>
|
<script src="lib/hierarchy.js"></script>
|
||||||
|
<script src="lib/layout.js"></script>
|
||||||
|
<script src="lib/parser.js"></script>
|
||||||
<script src="lib/render.js"></script>
|
<script src="lib/render.js"></script>
|
||||||
<script src="lib/template.js"></script>
|
|
||||||
<script src="lib/scene/scene.js"></script>
|
|
||||||
<script src="lib/scene/annotation.js"></script>
|
<script src="lib/scene/annotation.js"></script>
|
||||||
|
<script src="lib/scene/contextmenu.js"></script>
|
||||||
<script src="lib/scene/edge.js"></script>
|
<script src="lib/scene/edge.js"></script>
|
||||||
<script src="lib/scene/node.js"></script>
|
<script src="lib/scene/node.js"></script>
|
||||||
<script src="lib/scene/contextmenu.js"></script>
|
<script src="lib/scene/scene.js"></script>
|
||||||
<script src="lib/layout.js"></script>
|
<script src="lib/template.js"></script>
|
||||||
<script src="lib/colors.js"></script>
|
<script src="lib/util.js"></script>
|
||||||
|
|||||||
@ -66,7 +66,7 @@ Polymer({
|
|||||||
value: 0,
|
value: 0,
|
||||||
msg: ''
|
msg: ''
|
||||||
});
|
});
|
||||||
var tracker = tf.getTracker(this);
|
var tracker = tf.graph.util.getTracker(this);
|
||||||
tf.graph.parser.fetchAndParseMetadata(path, tracker)
|
tf.graph.parser.fetchAndParseMetadata(path, tracker)
|
||||||
.then(function(stats) {
|
.then(function(stats) {
|
||||||
this._setOutStats(stats);
|
this._setOutStats(stats);
|
||||||
@ -78,7 +78,7 @@ Polymer({
|
|||||||
value: 0,
|
value: 0,
|
||||||
msg: ''
|
msg: ''
|
||||||
});
|
});
|
||||||
var tracker = tf.getTracker(this);
|
var tracker = tf.graph.util.getTracker(this);
|
||||||
var hierarchyParams = {
|
var hierarchyParams = {
|
||||||
verifyTemplate: true,
|
verifyTemplate: true,
|
||||||
// If a set of numbered op nodes has at least this number of nodes
|
// If a set of numbered op nodes has at least this number of nodes
|
||||||
@ -91,7 +91,7 @@ Polymer({
|
|||||||
seriesMap: {},
|
seriesMap: {},
|
||||||
};
|
};
|
||||||
this._setOutHierarchyParams(hierarchyParams);
|
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)
|
tf.graph.parser.fetchAndParseGraphData(path, pbTxtFile, dataTracker)
|
||||||
.then(function(graph) {
|
.then(function(graph) {
|
||||||
// Build the flat graph (consists only of Op nodes).
|
// Build the flat graph (consists only of Op nodes).
|
||||||
@ -119,12 +119,12 @@ Polymer({
|
|||||||
outEmbeddingTypes: ['^[a-zA-Z]+Summary$'],
|
outEmbeddingTypes: ['^[a-zA-Z]+Summary$'],
|
||||||
refEdges: refEdges
|
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);
|
return tf.graph.build(graph, buildParams, graphTracker);
|
||||||
})
|
})
|
||||||
.then(function(graph) {
|
.then(function(graph) {
|
||||||
this._setOutGraph(graph);
|
this._setOutGraph(graph);
|
||||||
var hierarchyTracker = tf.getSubtaskTracker(tracker, 50,
|
var hierarchyTracker = tf.graph.util.getSubtaskTracker(tracker, 50,
|
||||||
'Namespace hierarchy');
|
'Namespace hierarchy');
|
||||||
return tf.graph.hierarchy.build(graph, hierarchyParams, hierarchyTracker);
|
return tf.graph.hierarchy.build(graph, hierarchyParams, hierarchyTracker);
|
||||||
}.bind(this))
|
}.bind(this))
|
||||||
|
|||||||
@ -569,12 +569,12 @@ Polymer({
|
|||||||
/** Main method for building the scene */
|
/** Main method for building the scene */
|
||||||
_build: function(renderHierarchy) {
|
_build: function(renderHierarchy) {
|
||||||
this.templateIndex = renderHierarchy.hierarchy.getTemplateIndex();
|
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
|
// layout the scene for this meta / series node
|
||||||
tf.graph.layout.layoutScene(renderHierarchy.root, this);
|
tf.graph.layout.layoutScene(renderHierarchy.root, this);
|
||||||
}.bind(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.buildGroup(d3.select(this.$.root), renderHierarchy.root, this);
|
||||||
tf.graph.scene.addGraphClickListener(this.$.svg, this);
|
tf.graph.scene.addGraphClickListener(this.$.svg, this);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|||||||
@ -113,7 +113,7 @@ Polymer({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
_buildRenderHierarchy: function(graphHierarchy) {
|
_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) {
|
if (graphHierarchy.root.type !== tf.graph.NodeType.META) {
|
||||||
// root must be metanode but sometimes Polymer's dom-if has not
|
// root must be metanode but sometimes Polymer's dom-if has not
|
||||||
// remove tf-graph element yet in <tf-node-info>
|
// remove tf-graph element yet in <tf-node-info>
|
||||||
@ -265,8 +265,8 @@ Polymer({
|
|||||||
value: 0,
|
value: 0,
|
||||||
msg: ''
|
msg: ''
|
||||||
});
|
});
|
||||||
var tracker = tf.getTracker(this);
|
var tracker = tf.graph.util.getTracker(this);
|
||||||
var hierarchyTracker = tf.getSubtaskTracker(tracker, 100,
|
var hierarchyTracker = tf.graph.util.getSubtaskTracker(tracker, 100,
|
||||||
'Namespace hierarchy');
|
'Namespace hierarchy');
|
||||||
tf.graph.hierarchy.build(this.basicGraph, this.hierarchyParams, hierarchyTracker)
|
tf.graph.hierarchy.build(this.basicGraph, this.hierarchyParams, hierarchyTracker)
|
||||||
.then(function(graphHierarchy) {
|
.then(function(graphHierarchy) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user