Yuta HIGUCHI
Committed by Gerrit Code Review

Lazily compute SCC, etc. on demand

- TODO computeCost does not correspond to actual computation cost now

Change-Id: Iffe3093c81bbb51d5feb46117fae8be092cf9288
......@@ -15,12 +15,17 @@
*/
package org.onlab.onos.store.topology.impl;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import org.onlab.graph.DijkstraGraphSearch;
import org.onlab.graph.GraphPathSearch;
import org.onlab.graph.GraphPathSearch.Result;
import org.onlab.graph.TarjanGraphSearch;
import org.onlab.graph.TarjanGraphSearch.SCCResult;
import org.onlab.onos.net.AbstractModel;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.DefaultPath;
......@@ -46,8 +51,6 @@ import java.util.Set;
import static com.google.common.base.MoreObjects.toStringHelper;
import static com.google.common.collect.ImmutableSetMultimap.Builder;
import static org.onlab.graph.GraphPathSearch.Result;
import static org.onlab.graph.TarjanGraphSearch.SCCResult;
import static org.onlab.onos.core.CoreService.CORE_PROVIDER_ID;
import static org.onlab.onos.net.Link.State.ACTIVE;
import static org.onlab.onos.net.Link.State.INACTIVE;
......@@ -68,18 +71,15 @@ public class DefaultTopology extends AbstractModel implements Topology {
private final long computeCost;
private final TopologyGraph graph;
private final SCCResult<TopologyVertex, TopologyEdge> clusterResults;
private final ImmutableMap<DeviceId, Result<TopologyVertex, TopologyEdge>> results;
private final ImmutableSetMultimap<PathKey, Path> paths;
private final ImmutableMap<ClusterId, TopologyCluster> clusters;
private final ImmutableSet<ConnectPoint> infrastructurePoints;
private final ImmutableSetMultimap<ClusterId, ConnectPoint> broadcastSets;
private final Supplier<SCCResult<TopologyVertex, TopologyEdge>> clusterResults;
private final Supplier<ImmutableMap<DeviceId, Result<TopologyVertex, TopologyEdge>>> results;
private final Supplier<ImmutableSetMultimap<PathKey, Path>> paths;
private ImmutableMap<DeviceId, TopologyCluster> clustersByDevice;
private ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster;
private ImmutableSetMultimap<TopologyCluster, Link> linksByCluster;
private final Supplier<ImmutableMap<ClusterId, TopologyCluster>> clusters;
private final Supplier<ImmutableSet<ConnectPoint>> infrastructurePoints;
private final Supplier<ImmutableSetMultimap<ClusterId, ConnectPoint>> broadcastSets;
private final Supplier<ClusterIndexes> clusterIndexes;
/**
* Creates a topology descriptor attributed to the specified provider.
......@@ -95,16 +95,17 @@ public class DefaultTopology extends AbstractModel implements Topology {
this.graph = new DefaultTopologyGraph(description.vertexes(),
description.edges());
this.results = searchForShortestPaths();
this.paths = buildPaths();
this.clusterResults = searchForClusters();
this.clusters = buildTopologyClusters();
this.results = Suppliers.memoize(() -> searchForShortestPaths());
this.paths = Suppliers.memoize(() -> buildPaths());
buildIndexes();
this.clusterResults = Suppliers.memoize(() -> searchForClusters());
this.clusters = Suppliers.memoize(() -> buildTopologyClusters());
this.broadcastSets = buildBroadcastSets();
this.infrastructurePoints = findInfrastructurePoints();
this.clusterIndexes = Suppliers.memoize(() -> buildIndexes());
this.broadcastSets = Suppliers.memoize(() -> buildBroadcastSets());
this.infrastructurePoints = Suppliers.memoize(() -> findInfrastructurePoints());
this.computeCost = Math.max(0, System.nanoTime() - time);
}
......@@ -120,7 +121,7 @@ public class DefaultTopology extends AbstractModel implements Topology {
@Override
public int clusterCount() {
return clusters.size();
return clusters.get().size();
}
@Override
......@@ -135,7 +136,19 @@ public class DefaultTopology extends AbstractModel implements Topology {
@Override
public int pathCount() {
return paths.size();
return paths.get().size();
}
private ImmutableMap<DeviceId, TopologyCluster> clustersByDevice() {
return clusterIndexes.get().clustersByDevice;
}
private ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster() {
return clusterIndexes.get().devicesByCluster;
}
private ImmutableSetMultimap<TopologyCluster, Link> linksByCluster() {
return clusterIndexes.get().linksByCluster;
}
/**
......@@ -153,7 +166,7 @@ public class DefaultTopology extends AbstractModel implements Topology {
* @return set of clusters
*/
Set<TopologyCluster> getClusters() {
return ImmutableSet.copyOf(clusters.values());
return ImmutableSet.copyOf(clusters.get().values());
}
/**
......@@ -163,7 +176,7 @@ public class DefaultTopology extends AbstractModel implements Topology {
* @return topology cluster
*/
TopologyCluster getCluster(ClusterId clusterId) {
return clusters.get(clusterId);
return clusters.get().get(clusterId);
}
/**
......@@ -173,7 +186,7 @@ public class DefaultTopology extends AbstractModel implements Topology {
* @return topology cluster
*/
TopologyCluster getCluster(DeviceId deviceId) {
return clustersByDevice.get(deviceId);
return clustersByDevice().get(deviceId);
}
/**
......@@ -183,7 +196,7 @@ public class DefaultTopology extends AbstractModel implements Topology {
* @return cluster devices
*/
Set<DeviceId> getClusterDevices(TopologyCluster cluster) {
return devicesByCluster.get(cluster);
return devicesByCluster().get(cluster);
}
/**
......@@ -193,7 +206,7 @@ public class DefaultTopology extends AbstractModel implements Topology {
* @return cluster links
*/
Set<Link> getClusterLinks(TopologyCluster cluster) {
return linksByCluster.get(cluster);
return linksByCluster().get(cluster);
}
/**
......@@ -203,7 +216,7 @@ public class DefaultTopology extends AbstractModel implements Topology {
* @return true if infrastructure
*/
boolean isInfrastructure(ConnectPoint connectPoint) {
return infrastructurePoints.contains(connectPoint);
return infrastructurePoints.get().contains(connectPoint);
}
/**
......@@ -219,14 +232,14 @@ public class DefaultTopology extends AbstractModel implements Topology {
}
// Find the cluster to which the device belongs.
TopologyCluster cluster = clustersByDevice.get(connectPoint.deviceId());
TopologyCluster cluster = clustersByDevice().get(connectPoint.deviceId());
if (cluster == null) {
throw new IllegalArgumentException("No cluster found for device " + connectPoint.deviceId());
}
// If the broadcast set is null or empty, or if the point explicitly
// belongs to it, return true;
Set<ConnectPoint> points = broadcastSets.get(cluster.id());
Set<ConnectPoint> points = broadcastSets.get().get(cluster.id());
return points == null || points.isEmpty() || points.contains(connectPoint);
}
......@@ -237,7 +250,7 @@ public class DefaultTopology extends AbstractModel implements Topology {
* @return size of the cluster broadcast set
*/
int broadcastSetSize(ClusterId clusterId) {
return broadcastSets.get(clusterId).size();
return broadcastSets.get().get(clusterId).size();
}
/**
......@@ -249,7 +262,7 @@ public class DefaultTopology extends AbstractModel implements Topology {
* @return set of shortest paths
*/
Set<Path> getPaths(DeviceId src, DeviceId dst) {
return paths.get(new PathKey(src, dst));
return paths.get().get(new PathKey(src, dst));
}
/**
......@@ -295,8 +308,8 @@ public class DefaultTopology extends AbstractModel implements Topology {
// Builds network paths from the graph path search results
private ImmutableSetMultimap<PathKey, Path> buildPaths() {
Builder<PathKey, Path> builder = ImmutableSetMultimap.builder();
for (DeviceId deviceId : results.keySet()) {
Result<TopologyVertex, TopologyEdge> result = results.get(deviceId);
for (DeviceId deviceId : results.get().keySet()) {
Result<TopologyVertex, TopologyEdge> result = results.get().get(deviceId);
for (org.onlab.graph.Path<TopologyVertex, TopologyEdge> path : result.paths()) {
builder.put(new PathKey(path.src().deviceId(), path.dst().deviceId()),
networkPath(path));
......@@ -363,7 +376,7 @@ public class DefaultTopology extends AbstractModel implements Topology {
// Processes a map of broadcast sets for each cluster.
private ImmutableSetMultimap<ClusterId, ConnectPoint> buildBroadcastSets() {
Builder<ClusterId, ConnectPoint> builder = ImmutableSetMultimap.builder();
for (TopologyCluster cluster : clusters.values()) {
for (TopologyCluster cluster : clusters.get().values()) {
addClusterBroadcastSet(cluster, builder);
}
return builder.build();
......@@ -375,12 +388,12 @@ public class DefaultTopology extends AbstractModel implements Topology {
private void addClusterBroadcastSet(TopologyCluster cluster,
Builder<ClusterId, ConnectPoint> builder) {
// Use the graph root search results to build the broadcast set.
Result<TopologyVertex, TopologyEdge> result = results.get(cluster.root());
Result<TopologyVertex, TopologyEdge> result = results.get().get(cluster.root());
for (Map.Entry<TopologyVertex, Set<TopologyEdge>> entry : result.parents().entrySet()) {
TopologyVertex vertex = entry.getKey();
// Ignore any parents that lead outside the cluster.
if (clustersByDevice.get(vertex.deviceId()) != cluster) {
if (clustersByDevice().get(vertex.deviceId()) != cluster) {
continue;
}
......@@ -409,32 +422,32 @@ public class DefaultTopology extends AbstractModel implements Topology {
}
// Builds cluster-devices, cluster-links and device-cluster indexes.
private void buildIndexes() {
private ClusterIndexes buildIndexes() {
// Prepare the index builders
ImmutableMap.Builder<DeviceId, TopologyCluster> clusterBuilder = ImmutableMap.builder();
ImmutableSetMultimap.Builder<TopologyCluster, DeviceId> devicesBuilder = ImmutableSetMultimap.builder();
ImmutableSetMultimap.Builder<TopologyCluster, Link> linksBuilder = ImmutableSetMultimap.builder();
// Now scan through all the clusters
for (TopologyCluster cluster : clusters.values()) {
for (TopologyCluster cluster : clusters.get().values()) {
int i = cluster.id().index();
// Scan through all the cluster vertexes.
for (TopologyVertex vertex : clusterResults.clusterVertexes().get(i)) {
for (TopologyVertex vertex : clusterResults.get().clusterVertexes().get(i)) {
devicesBuilder.put(cluster, vertex.deviceId());
clusterBuilder.put(vertex.deviceId(), cluster);
}
// Scan through all the cluster edges.
for (TopologyEdge edge : clusterResults.clusterEdges().get(i)) {
for (TopologyEdge edge : clusterResults.get().clusterEdges().get(i)) {
linksBuilder.put(cluster, edge.link());
}
}
// Finalize all indexes.
clustersByDevice = clusterBuilder.build();
devicesByCluster = devicesBuilder.build();
linksByCluster = linksBuilder.build();
return new ClusterIndexes(clusterBuilder.build(),
devicesBuilder.build(),
linksBuilder.build());
}
// Link weight for measuring link cost as hop count with indirect links
......@@ -463,6 +476,20 @@ public class DefaultTopology extends AbstractModel implements Topology {
}
}
static final class ClusterIndexes {
final ImmutableMap<DeviceId, TopologyCluster> clustersByDevice;
final ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster;
final ImmutableSetMultimap<TopologyCluster, Link> linksByCluster;
public ClusterIndexes(ImmutableMap<DeviceId, TopologyCluster> clustersByDevice,
ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster,
ImmutableSetMultimap<TopologyCluster, Link> linksByCluster) {
this.clustersByDevice = clustersByDevice;
this.devicesByCluster = devicesByCluster;
this.linksByCluster = linksByCluster;
}
}
@Override
public String toString() {
return toStringHelper(this)
......