Topological Ordering public MaybeListNode topological Order ListNode ordered
Topological Ordering
public Maybe<List<Node>> topological. Order(){ List<Node> ordered. Nodes = new Array. List<Node>(); while(true){ List<Node> sources = source. Nodes(); if(sources. size()==0) break; Node n = sources. get(0); ordered. Nodes. add(n); ==> nodes. remove(n); <== } if(nodes. size() > 0 ){ return new None<List<Node>>(); } return new Some<List<Node>>(ordered. Nodes); }
public List<Node> source. Nodes(){ List<Node> sources = new Array. List<Node>(); for (Node node : nodes) { if (pred. Count. Of(node) == 0){ sources. add(node); } } return sources; } 3
public int pred. Count. Of(Node node){ int pred. Count = 0; for (Node n : nodes) { if(n. get. Successors(). contains(node)){ pred. Count++; } } return pred. Count; } 4
Optimization Opportunity (1) The method source. Nodes() is invoked several times, in the control flow of an invocation of topological. Order(), with the same implicit this argument destructively updated by the statement nodes. remove(n) between consecutive invocations of source. Nodes().
Dynamizing source. Nodes() (1) It is sound to memoize source. Nodes() as long as we provide a maintenance advice that restores cache consistency after the execution of nodes. remove(n). One approach to restore cache consistency is to use an incrementalized/dynamized version of source. Nodes() under Graph. nodes. remove(Node).
Dynamizing source. Nodes() (2) public List<Node> source. Nodes(){ List<Node> sources = new Array. List<Node>(); for (Node node : nodes) { if (pred. Count. Of(node) == 0){ sources. add(node); } } return sources; } How should we change what this method returns if a node was removed from nodes? 7
Dynamizing source. Nodes() (3) • If a node is removed from the graph, it can no longer be returned by source. Nodes(). • If a node is removed from the graph, all of it’s successors are going to lose a predecessor. 8
public Maybe<List<Node>> topological. Order. Src. Nodes. Opt(){ List<Node> ordered. Nodes = new Array. List<Node>(); List<Node> sources = source. Nodes(); while(sources. size()>0){ Node n = sources. get(0); ordered. Nodes. add(n); nodes. remove(n); /*Src. Node. Opt*/ sources. remove(n); /*Src. Node. Opt*/ for(Node succ: n. get. Successors()){ /*Src. Node. Opt*/ if(!sources. contains(succ)){ /*Src. Node. Opt*/ if(pred. Count. Of(succ) == 0){ /*Src. Node. Opt*/ sources. add(succ); /*Src. Node. Opt*/ } } if(nodes. size() > 0 ) return new None<List<Node>>(); return new Some<List<Node>>(ordered. Nodes); } 9
Optimization Opportunity (2) The method pred. Count. Of(Node) is invoked several times, in the control flow of an invocation of topological. Order(), with the same implicit this argument destructively updated by the statement nodes. remove(n) between consecutive invocations of pred. Count. Of(Node). However, not all invocations have the same Node argument. Would memoization alone be enough to gain the best performance?
Reordering the Computation • Instead of lazily computing Graph. pred. Count. Of(Node) on demand, we can eagerly compute the pred. Count of all nodes in the graph. • What does that save? 11
Reordering the Computation(2) public int pred. Count. Of(Node node){ int pred. Count = 0; for (Node n : nodes) { if(n. get. Successors(). contains(node)){ pred. Count++; } } return pred. Count; } Can we compute the pred. Count of all nodes together more efficiently than computing them one by one? 12
Reordering the Computation(3) • Computing the pred. Count of one node requires a complete graph traversal. • Computing the pred. Count of any number of nodes can be done by a single graph traversal as well. • Amortizing the cost of graph traversal. 13
Dynamizing pred. Count. Of() (1) It is sound to memoize pred. Count. Of() as long as we provide a maintenance advice that restores cache consistency after the execution of nodes. remove(n). One approach to restore cache consistency is to use an incrementalized/dynamized version of pred. Count. Of() under Graph. nodes. remove(Node).
Dynamizing pred. Count. Of() (2) public int pred. Count. Of(Node node){ int pred. Count = 0; for (Node n : nodes) { if(n. get. Successors(). contains(node)){ pred. Count++; } } return pred. Count; } How should we change what this method returns if a node was removed from nodes? 15
Dynamizing pred. Count. Of() (2) • If a node is removed from the graph, it can no longer have pred. Count in that graph. • If a node is removed from the graph, all of it’s successors are no longer going to have the removed node as a predecessor. 16
public Maybe<List<Node>> topological. Order. Predecessors. Opt(){ /*Predecessors. Opt*/ for(Node n: this. nodes){ /*Predecessors. Opt*/ n. pred. Count = 0; /*Predecessors. Opt*/ } /*Predecessors. Opt*/ for(Node n : this. nodes){ /*Predecessors. Opt*/ for(Node succ: n. successors){ /*Predecessors. Opt*/ succ. pred. Count++; /*Predecessors. Opt*/ } List<Node> ordered. Nodes = new Array. List<Node>(); while(true){ List<Node> sources = source. Nodes(); if(sources. size() == 0 ) break; Node n = sources. get(0); ordered. Nodes. add(n); nodes. remove(n); /*Predecessors. Opt*/ for(Node succ: n. get. Successors()){ /*Predecessors. Opt*/ succ. pred. Count--; /*Predecessors. Opt*/ } /*Predecessors. Opt*/ n. pred. Count = 0; } if(nodes. size() > 0 ) return new None<List<Node>>(); return new Some<List<Node>>(ordered. Nodes); } 17
Combining Both Optimizations • Both optimizations add maintenance code right after nodes. remove(n). • When combining them which one should come first? Why? 18
public Maybe<List<Node>> topological. Order. Src. Nodes. Opt. And. Predecessors. Opt(){ /*Predecessors. Opt*/ for(Node n: this. nodes){ /*Predecessors. Opt*/ n. pred. Count = 0; /*Predecessors. Opt*/ } /*Predecessors. Opt*/ for(Node n : this. nodes){ /*Predecessors. Opt*/ for(Node succ: n. successors){ /*Predecessors. Opt*/ succ. pred. Count++; /*Predecessors. Opt*/ } List<Node> ordered. Nodes = new Array. List<Node>(); List<Node> sources = source. Nodes(); while(sources. size()>0){ Node n = sources. get(0); ordered. Nodes. add(n); nodes. remove(n); /*Predecessors. Opt*/ for(Node succ: n. get. Successors()){ /*Predecessors. Opt*/ succ. pred. Count--; /*Predecessors. Opt*/ } /*Predecessors. Opt*/ n. pred. Count = 0; /*Src. Node. Opt*/ sources. remove(n); /*Src. Node. Opt*/ for(Node succ: n. get. Successors()){ /*Src. Node. Opt*/ if(!sources. contains(succ)){ /*Src. Node. Opt*/ if(/*Predecessors. Opt*/succ. pred. Count == 0){ /*Src. Node. Opt*/ sources. add(succ); /*Src. Node. Opt*/ } } if(nodes. size() > 0 ) return new None<List<Node>>(); return new Some<List<Node>>(ordered. Nodes); } 19
- Slides: 19