Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Graph Visualization: Added hierarchy based on frames, faster function to get the 'pbtxt' and 'dot' formats, and implicit edges are added between commands and subcommands. #2527

Merged
merged 1 commit into from
Jan 14, 2019

Conversation

ecapia
Copy link
Contributor

@ecapia ecapia commented Jan 11, 2019

No description provided.

@ecapia ecapia force-pushed the graph_visualization_4 branch from 6e34820 to 702dd46 Compare January 11, 2019 00:38
currentGraph, err := createGraphFromDependencyGraph(dependencyGraph)
currentGraph.removeNodesWithZeroDegree()
output := ""
currentGraph, err := createGraphFromDependencyGraph(ctx, dependencyGraph)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be more canonical go with:

	currentGraph, err := createGraphFromDependencyGraph(ctx, dependencyGraph)
	if err != nil {
		return []byte{}, err
        }
	currentGraph.joinNodesByFrame()
	currentGraph.joinNodesWithZeroDegree()
	output := []byte{}
	if format == service.GraphFormat_PBTXT {
		output = currentGraph.getGraphInPbtxtFormat()
	} else if format == service.GraphFormat_DOT {
		output = currentGraph.getGraphInDotFormat()
	}

	return output, nil

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, I added the error checking.

for _, currentEdge := range g.edgeIdToEdge {
lines := strconv.Itoa(currentEdge.source.id) + " -> " + strconv.Itoa(currentEdge.sink.id) + ";\n"
output += lines
func (g *graph) dfs(currentNode *node, visited []bool, visitedNodes *[]*node) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This recursive implementation makes me a bit nervous that a long chain of dependencies could cause a stack overflow.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Golang has an infinite stack. So unless we expect this to end up being a memory consumption issue, we should be ok there.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, interesting!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, I was not expecting an infinite stack. However, I implemented an iterative version (using a breadth first search). I think in some point it could end up a unnecessary memory consumption issue for huge graphs (millions of nodes and hundred of millions of edges).

@@ -81,6 +89,11 @@ func createGraphFromDependencyGraph(dependencyGraph dependencygraph2.DependencyG
newNode := getNewNode(int(nodeId), label)
newNode.name = name
newNode.attributes = attributes
newNode.isEndOfFrame = command.CmdFlags(ctx, api.CmdID(cmdNodeId), &api.GlobalState{}).IsEndOfFrame()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using an empty GlobalState actually works?

@AWoloszyn, is this safe in general? And if so, why does CmdFlags have a GlobalState argument?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not safe in general. I expect we would be ok in Vulkan, but it will likely segfault in GLES.

The way you would normally do this is get all of the EndOfFrame events from the trace (using a path.Events resolvable), and use those.
Alternatively you could insert those parameters into the dependency graph when creating it, which will have a valid global state.

gapis/resolve/events.go

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will store the CmdFlags in the dependency graph command nodes, and switch this to get the CmdFlags from the dependency graph in a separate PR.

frameNumber := 1
nodes := g.getSortedNodes()
for _, currentNode := range nodes {
if !visited[currentNode.id] && currentNode.isEndOfFrame {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just an observation: Forward dependencies break this strategy. For example, if an image is created in frame 1 and destroyed in frame 2, the creation has a dependency to the destruction. Following that edge in the DFS would cause some of the frame 2 commands to be incorrectly placed in frame 1.

However, I think the issues from this should be pretty small, since nodes with forward dependencies (like resource destruction) probably don't have a lot of other dependencies. I also don't see an obviously more correct way to handle forward dependencies, since just ignoring them in the DFS will leave lots of orphan nodes not associated with any frame.

@ecapia ecapia force-pushed the graph_visualization_4 branch from 702dd46 to 1428ee9 Compare January 11, 2019 20:04
lines := strconv.Itoa(currentEdge.source.id) + " -> " + strconv.Itoa(currentEdge.sink.id) + ";\n"
output += lines
func (g *graph) bfs(sourceNode *node, visited []bool, visitedNodes *[]*node) {
head, tail := 0, 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need tail? It looks like tail should always be the end of visitedNodes.

Also, it looks like things will behave very strangely if visitedNodes were somehow not empty to start. I know this should not happen, but if you set head := len(*visitedNodes), it would be a non-issue.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I do not . I was using tail for better understanding of the algorithm, but I think it is okay remove this.

I am assuming that always visitedNodes is empty at the beginning, but could be useful for some cases set head := len(*visitedNodes) such as get nodes from two frames, so I changed it. Thank you.

output.WriteString("\"\n")
output.WriteString("op: \"")
output.WriteString(currentNode.label)
output.WriteString("\"\n")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't doubt that individual WriteString calls are faster, but I think a single WriteString or fmt.Fprintf call per output line would be easier to read, and I suspect the performance difference is extremely small.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the performance is almost the same considering small lengths of strings. Based on local becnhmarking and https://gist.github.com/dtjm/c6ebc86abe7515c988ec , using concatenation is a little faster than fmt.Fprintf. So I added concatenation by line.

@ecapia ecapia force-pushed the graph_visualization_4 branch from 1428ee9 to cfce6e9 Compare January 11, 2019 22:27
to get the 'pbtxt' and 'dot' formats, and implicit edges are added
between commands and subcommands.

Hierarchy based on frames: Using a depth first search starting in every
VK_QUEUE_PRESENT command.

Implicit edges added between commands and subcommands to correctly
defined the frames in the depth first search.
@ecapia ecapia force-pushed the graph_visualization_4 branch from cfce6e9 to bed1338 Compare January 11, 2019 23:01
Copy link
Contributor

@bjoeris bjoeris left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@bjoeris bjoeris merged commit 830d930 into google:master Jan 14, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants