| Index: tools/android/loading/resource_sack.py
|
| diff --git a/tools/android/loading/resource_sack.py b/tools/android/loading/resource_sack.py
|
| index 90ced3801c92a6844d34d98731be4f80cea4228e..563bb772e683773064c66881bf772790ee33221b 100644
|
| --- a/tools/android/loading/resource_sack.py
|
| +++ b/tools/android/loading/resource_sack.py
|
| @@ -17,16 +17,16 @@ import urlparse
|
| from collections import defaultdict
|
|
|
| import content_classification_lens
|
| -import dag
|
| +import graph
|
| import user_satisfied_lens
|
|
|
| class GraphSack(object):
|
| - """Aggreate of ResourceGraphs.
|
| + """Aggreate of RequestDependencyGraphs.
|
|
|
| - Collects ResourceGraph nodes into bags, where each bag contains the nodes with
|
| - common urls. Dependency edges are tracked between bags (so that each bag may
|
| - be considered as a node of a graph). This graph of bags is referred to as a
|
| - sack.
|
| + Collects RequestDependencyGraph nodes into bags, where each bag contains the
|
| + nodes with common urls. Dependency edges are tracked between bags (so that
|
| + each bag may be considered as a node of a graph). This graph of bags is
|
| + referred to as a sack.
|
|
|
| Each bag is associated with a dag.Node, even though the bag graph may not be a
|
| DAG. The edges are annotated with list of graphs and nodes that generated
|
| @@ -37,55 +37,56 @@ class GraphSack(object):
|
|
|
| _GraphInfo = collections.namedtuple('_GraphInfo', (
|
| 'cost', # The graph cost (aka critical path length).
|
| - 'total_costs', # A vector by node index of total cost of each node.
|
| ))
|
|
|
| def __init__(self):
|
| - # A bag is a node in our combined graph.
|
| - self._bags = []
|
| - # Each bag in our sack corresponds to a url, as expressed by this map.
|
| - self._url_to_bag = {}
|
| + # Each bag in our sack is named as indicated by this map.
|
| + self._name_to_bag = {}
|
| + # List our edges by bag pairs: (from_bag, to_bag) -> graph.Edge.
|
| + self._edges = {}
|
| # Maps graph -> _GraphInfo structures for each graph we've consumed.
|
| self._graph_info = {}
|
|
|
| - def ConsumeGraph(self, graph):
|
| + # Our graph, updated after each ConsumeGraph.
|
| + self._graph = None
|
| +
|
| + def ConsumeGraph(self, request_graph):
|
| """Add a graph and process.
|
|
|
| Args:
|
| - graph: (ResourceGraph) the graph to add. The graph is processed sorted
|
| - according to its current filter.
|
| + graph: (RequestDependencyGraph) the graph to add.
|
| """
|
| assert graph not in self._graph_info
|
| - critical_path = []
|
| - total_costs = []
|
| - cost = graph.Cost(path_list=critical_path,
|
| - costs_out=total_costs)
|
| - self._graph_info[graph] = self._GraphInfo(
|
| - cost=cost, total_costs=total_costs)
|
| - for n in graph.Nodes(sort=True):
|
| - assert graph._node_filter(n.Node())
|
| - self.AddNode(graph, n)
|
| - for node in critical_path:
|
| - self._url_to_bag[node.Url()].MarkCritical()
|
| -
|
| - def AddNode(self, graph, node):
|
| + cost = request_graph.Cost()
|
| + self._graph_info[request_graph] = self._GraphInfo(cost=cost)
|
| + for n in request_graph.graph.Nodes():
|
| + self.AddNode(request_graph, n)
|
| +
|
| + # TODO(mattcary): this is inefficient but our current API doesn't require an
|
| + # explicit graph creation from the client.
|
| + self._graph = graph.DirectedGraph(self.bags, self._edges.itervalues())
|
| +
|
| + def AddNode(self, request_graph, node):
|
| """Add a node to our collection.
|
|
|
| Args:
|
| - graph: (ResourceGraph) the graph in which the node lives.
|
| - node: (NodeInfo) the node to add.
|
| + graph: (RequestDependencyGraph) the graph in which the node lives.
|
| + node: (RequestDependencyGraph node) the node to add.
|
|
|
| Returns:
|
| The Bag containing the node.
|
| """
|
| - if not graph._node_filter(node):
|
| - return
|
| - if node.Url() not in self._url_to_bag:
|
| - new_index = len(self._bags)
|
| - self._bags.append(Bag(self, new_index, node.Url()))
|
| - self._url_to_bag[node.Url()] = self._bags[-1]
|
| - self._url_to_bag[node.Url()].AddNode(graph, node)
|
| - return self._url_to_bag[node.Url()]
|
| + sack_name = self._GetSackName(node)
|
| + if sack_name not in self._name_to_bag:
|
| + self._name_to_bag[sack_name] = Bag(self, sack_name)
|
| + bag = self._name_to_bag[sack_name]
|
| + bag.AddNode(request_graph, node)
|
| + return bag
|
| +
|
| + def AddEdge(self, from_bag, to_bag):
|
| + """Add an edge between two bags."""
|
| + if (from_bag, to_bag) not in self._edges:
|
| + self._edges[(from_bag, to_bag)] = graph.Edge(from_bag, to_bag)
|
|
|
| def CoreSet(self, *graph_sets):
|
| """Compute the core set of this sack.
|
| @@ -141,7 +142,7 @@ class GraphSack(object):
|
|
|
| @property
|
| def bags(self):
|
| - return self._bags
|
| + return self._name_to_bag.values()
|
|
|
| def _SingleCore(self, graph_set):
|
| core = set()
|
| @@ -153,31 +154,31 @@ class GraphSack(object):
|
| core.add(b.label)
|
| return core
|
|
|
| + def _GetSackName(self, node):
|
| + return self._MakeShortname(node.request.url)
|
| +
|
| + @classmethod
|
| + def _MakeShortname(cls, url):
|
| + # TODO(lizeb): Move this method to a convenient common location.
|
| + parsed = urlparse.urlparse(url)
|
| + if parsed.scheme == 'data':
|
| + if ';' in parsed.path:
|
| + kind, _ = parsed.path.split(';', 1)
|
| + else:
|
| + kind, _ = parsed.path.split(',', 1)
|
| + return 'data:' + kind
|
| + path = parsed.path[:10]
|
| + hostname = parsed.hostname if parsed.hostname else '?.?.?'
|
| + return hostname + '/' + path
|
| +
|
|
|
| -class Bag(dag.Node):
|
| - def __init__(self, sack, index, url):
|
| - super(Bag, self).__init__(index)
|
| +class Bag(graph.Node):
|
| + def __init__(self, sack, label):
|
| + super(Bag, self).__init__()
|
| self._sack = sack
|
| - self._url = url
|
| - self._label = self._MakeShortname(url)
|
| + self._label = label
|
| # Maps a ResourceGraph to its Nodes contained in this Bag.
|
| self._graphs = defaultdict(set)
|
| - # Maps each successor bag to the set of (graph, node, graph-successor)
|
| - # tuples that generated it.
|
| - self._successor_sources = defaultdict(set)
|
| - # Maps each successor bag to a set of edge costs. This is just used to
|
| - # track min and max; if we want more statistics we'd have to count the
|
| - # costs with multiplicity.
|
| - self._successor_edge_costs = defaultdict(set)
|
| -
|
| - # Miscellaneous counts and costs used in display.
|
| - self._total_costs = []
|
| - self._relative_costs = []
|
| - self._num_critical = 0
|
| -
|
| - @property
|
| - def url(self):
|
| - return self._url
|
|
|
| @property
|
| def label(self):
|
| @@ -185,62 +186,16 @@ class Bag(dag.Node):
|
|
|
| @property
|
| def graphs(self):
|
| - return self._graphs
|
| -
|
| - @property
|
| - def successor_sources(self):
|
| - return self._successor_sources
|
| -
|
| - @property
|
| - def successor_edge_costs(self):
|
| - return self._successor_edge_costs
|
| -
|
| - @property
|
| - def total_costs(self):
|
| - return self._total_costs
|
| -
|
| - @property
|
| - def relative_costs(self):
|
| - return self._relative_costs
|
| -
|
| - @property
|
| - def num_critical(self):
|
| - return self._num_critical
|
| + return self._graphs.iterkeys()
|
|
|
| @property
|
| def num_nodes(self):
|
| - return len(self._total_costs)
|
| -
|
| - def MarkCritical(self):
|
| - self._num_critical += 1
|
| + return sum(len(g) for g in self._graphs.itervalues())
|
|
|
| - def AddNode(self, graph, node):
|
| - if node in self._graphs[graph]:
|
| + def AddNode(self, request_graph, node):
|
| + if node in self._graphs[request_graph]:
|
| return # Already added.
|
| - graph_info = self._sack.graph_info[graph]
|
| - self._graphs[graph].add(node)
|
| - node_total_cost = graph_info.total_costs[node.Index()]
|
| - self._total_costs.append(node_total_cost)
|
| - self._relative_costs.append(
|
| - float(node_total_cost) / graph_info.cost)
|
| - for s in node.Node().Successors():
|
| - if not graph._node_filter(s):
|
| - continue
|
| - node_info = graph.NodeInfo(s)
|
| - successor_bag = self._sack.AddNode(graph, node_info)
|
| - self.AddSuccessor(successor_bag)
|
| - self._successor_sources[successor_bag].add((graph, node, s))
|
| - self._successor_edge_costs[successor_bag].add(graph.EdgeCost(node, s))
|
| -
|
| - @classmethod
|
| - def _MakeShortname(cls, url):
|
| - parsed = urlparse.urlparse(url)
|
| - if parsed.scheme == 'data':
|
| - if ';' in parsed.path:
|
| - kind, _ = parsed.path.split(';', 1)
|
| - else:
|
| - kind, _ = parsed.path.split(',', 1)
|
| - return 'data:' + kind
|
| - path = parsed.path[:10]
|
| - hostname = parsed.hostname if parsed.hostname else '?.?.?'
|
| - return hostname + '/' + path
|
| + self._graphs[request_graph].add(node)
|
| + for edge in request_graph.graph.OutEdges(node):
|
| + out_bag = self._sack.AddNode(request_graph, edge.to_node)
|
| + self._sack.AddEdge(self, out_bag)
|
|
|