Skip to content

Commit d738d13

Browse files
committed
Initial commit for throughput upper bound computation!
0 parents  commit d738d13

File tree

6 files changed

+219
-0
lines changed

6 files changed

+219
-0
lines changed

main_tub.py

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from __future__ import absolute_import
2+
from __future__ import division
3+
from __future__ import print_function
4+
5+
import argparse
6+
7+
from utils import utilities
8+
9+
if __name__ == "__main__":
10+
parser = argparse.ArgumentParser(description='compute throughput upper bound (TUB)')
11+
parser.add_argument('file_name', metavar='file_name', type=str, help='path to the topology file')
12+
args = parser.parse_args()
13+
print("=" * 10 + " Computing TUB " + "=" * 10)
14+
topology = utilities.get_model(args.file_name)
15+
tub = topology.get_tub()
16+
print("=" * 10 + " Result " + "=" * 10)
17+
print("TUB = ", tub)

metric/tub.py

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from __future__ import absolute_import
2+
from __future__ import division
3+
from __future__ import print_function
4+
5+
import networkx as nx
6+
from utils import near_wc_tm
7+
from typing import List, Dict
8+
9+
10+
def get_throughput_upper_bound(topo: nx.Graph, tor_list: List, demand_dict: Dict) -> float:
11+
print("** Computing maximal permutation traffic matrix...")
12+
_, sum_weight_matching = near_wc_tm.get_longest_matching_traffic_matrix(topo, tor_list, demand_dict)
13+
print("** Computing TUB...")
14+
tub = len(topo.edges()) * 2.0 / sum_weight_matching
15+
return tub

requirements.txt

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
networkx
2+
python-igraph
3+
numpy
4+
scipy
5+
matplotlib

utils/near_wc_tm.py

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
from __future__ import absolute_import
2+
from __future__ import division
3+
from __future__ import print_function
4+
5+
import gc
6+
import igraph
7+
import numpy as np
8+
9+
from networkx import nx
10+
from utils import shortest_path
11+
from typing import List, Dict, Tuple
12+
13+
14+
def get_longest_matching_traffic_matrix(topology: nx.Graph, tor_list: List, demand_dict: Dict) -> Tuple[Dict, int]:
15+
""" Generates maximal permutation traffic matrix that results in near worst-case throughput
16+
17+
This is a generalization to: Measuring and Understanding Throughput of Network Topologies
18+
(Sangeetha Abdu Jyothi, Ankit Singla, P. Brighten Godfrey, Alexandra Kolla), 2016
19+
20+
Args:
21+
topology: a networkx graph representing switch connections
22+
tor_list: contains list of switches with directly connected servers
23+
demand_dict: a mapping from each ToR to its number of directly connected servers
24+
Returns:
25+
traffic_matrix: a dictionary (src, dst) --> amount of traffic
26+
sum_weight_matching: sum shortest_path_length(src, dst) * traffic_matrix(src, dst) over all pairs
27+
traffic_matrix
28+
"""
29+
30+
initial_tor_list = list(tor_list)
31+
num_nodes = len(tor_list)
32+
tor_list = list()
33+
for node in topology.nodes():
34+
if node in initial_tor_list:
35+
tor_list.append(node)
36+
del initial_tor_list
37+
38+
print("**** Computing all-pair shortest path lengths...")
39+
np_shortest_path = shortest_path.all_pair_shortest_path_length_adjacency_matrix(topology, tor_list)
40+
41+
print("**** Generating bipartite graph...")
42+
bi_graph = igraph.Graph.Full_Bipartite(num_nodes, num_nodes)
43+
s = np.array(list(demand_dict.values()), order='F', dtype=np.uint16) * \
44+
(np.ones((num_nodes, num_nodes), order='F', dtype=np.uint16) - np.eye(num_nodes, order='F', dtype=np.uint16))
45+
minimum = np.min(np.array(list(demand_dict.values())))
46+
coefficient = np.minimum(s, np.transpose(s))/minimum
47+
weights = np.reshape(np.multiply(np_shortest_path, coefficient, dtype=np.float32, order='F'),
48+
(1, num_nodes * num_nodes), order='F').tolist()[0]
49+
del s
50+
del coefficient
51+
del minimum
52+
gc.collect()
53+
# bi_graph.es["weights"] = weights
54+
55+
print("**** Computing maximum weighted matching...")
56+
maximal_matching = bi_graph.maximum_bipartite_matching(weights=weights, eps=0.001)
57+
traffic_matrix = dict()
58+
59+
sum_weight_matching = 0
60+
for i in range(num_nodes):
61+
62+
j = maximal_matching.matching[i]
63+
if j >= num_nodes > i:
64+
j -= num_nodes
65+
s0 = tor_list[i]
66+
s1 = tor_list[j]
67+
elif i >= num_nodes > j:
68+
raise ValueError
69+
else:
70+
raise ValueError
71+
72+
traffic_matrix[s0, s1] = min(demand_dict[s0], demand_dict[s1])
73+
sum_weight_matching += np_shortest_path[i][j] * traffic_matrix[s0, s1]
74+
75+
return traffic_matrix, sum_weight_matching

utils/shortest_path.py

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
from __future__ import absolute_import
2+
from __future__ import division
3+
from __future__ import print_function
4+
5+
import numpy as np
6+
import networkx as nx
7+
import scipy
8+
9+
from typing import List
10+
11+
12+
def sum_all_pair_shortest_path_length_adjacency_matrix(g: nx.Graph) -> int:
13+
""" Computes the sum of shortest path length over all the pairs of nodes in g
14+
15+
Args:
16+
g: a networkx graph representing the connection between switches.
17+
18+
Returns:
19+
total_sp: sum of shortest path length over all the pairs of nodes in g
20+
"""
21+
22+
num_node = len(g.nodes())
23+
A = nx.to_numpy_matrix(g, order='F', dtype=np.float32)
24+
B = np.eye(num_node, order='F', dtype=np.float32)
25+
total_sp = num_node * (num_node - 1) - num_node
26+
C = np.zeros(np.shape(A), order='F', dtype=np.float32)
27+
for k in range(num_node - 1):
28+
B = scipy.linalg.blas.sgemm(1, B, A)
29+
# B = np.matmul(B, A)
30+
C = np.add(C, B)
31+
num = np.count_nonzero(C == 0)
32+
if num == 0:
33+
break
34+
total_sp += num
35+
36+
return total_sp
37+
38+
39+
def all_pair_shortest_path_length_adjacency_matrix(g: nx.Graph, tor_list: List = None) -> np.array:
40+
""" Returns the length of the shortest path between all pairs of ToRs
41+
42+
Args:
43+
g: a networkx graph representing the connection between switches.
44+
tor_list: a list of tors such that the output represents the shortest path length among pairs
45+
with both ends in tor_list. In case tor_list = None, this function returns the shortest
46+
path length between all the pairs.
47+
Returns:
48+
shortest_path_np_array:
49+
"""
50+
51+
num_node = len(g.nodes())
52+
A = nx.to_numpy_matrix(g, order='F', dtype=np.float32)
53+
B = np.eye(num_node, order='F', dtype=np.float32)
54+
55+
C = np.eye(num_node, order='F', dtype=np.float32)
56+
shortest_path_np_array = np.ones(np.shape(A), order='F', dtype=np.float32)
57+
58+
for k in range(num_node - 1):
59+
B = scipy.linalg.blas.sgemm(1, B, A)
60+
C = np.add(C, B)
61+
if np.count_nonzero(C == 0) == 0:
62+
break
63+
64+
add_np_array = np.subtract(np.ones(np.shape(C), order='F', dtype=np.float32), C)
65+
add_np_array = np.where(add_np_array < 0, 0, add_np_array)
66+
shortest_path_np_array = np.add(shortest_path_np_array, add_np_array)
67+
del add_np_array
68+
69+
shortest_path_np_array = np.subtract(shortest_path_np_array, np.eye(num_node, order='F', dtype=np.float32))
70+
delete_index_list = list()
71+
if tor_list:
72+
for index, node in enumerate(g.nodes()):
73+
if node not in tor_list:
74+
delete_index_list.append(index)
75+
shortest_path_np_array = np.delete(shortest_path_np_array, delete_index_list, axis=1)
76+
shortest_path_np_array = np.delete(shortest_path_np_array, delete_index_list, axis=0)
77+
78+
del A
79+
del B
80+
del C
81+
return shortest_path_np_array

utils/utilities.py

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from __future__ import absolute_import
2+
from __future__ import division
3+
from __future__ import print_function
4+
5+
import os
6+
import pickle
7+
8+
from topo_repo import topology
9+
10+
11+
def get_model(filename: str) -> topology.Topology:
12+
print("** Loading Topology...")
13+
with open(filename, "rb") as file:
14+
model = pickle.load(file, encoding="latin1")
15+
return model
16+
17+
18+
def delete_file(file_path: str) -> None:
19+
if os.path.exists(file_path):
20+
os.remove(file_path)
21+
22+
23+
def store_model(model, file_path: str) -> None:
24+
delete_file(file_path)
25+
with open(file_path, "wb") as f:
26+
pickle.dump(model, f)

0 commit comments

Comments
 (0)