Commit 987f90a7 authored by Neil McGlohon's avatar Neil McGlohon

Merge branch 'dfp-topo-output' into 'dfp-online-workloads'

Dfp topo output

See merge request neil/codes!14
parents a762cc3d 595ef04e
# Neil McGlohon - Rensselaer Polytechnic Institute (c) 2018
import sys
from enum import Enum
import re
import struct
import csv
argv = sys.argv
# ----------------------- README -------------------------
# Usage: python3 dfp_get_connectivity_file.py <config_filename> <intra_filename> <inter_filename> <output_filename>
# Note: This assumes that the modelnet_order is that of (terminal, router). This will break otherwise.
# Example usage:
# Have configuration file: "example/dfp-test.conf"
# Have intra-connection file: "example/dfp-test-intra"
# Have inter-connection file: "example/dfp-test-inter"
# Want output to file: "example/output"
#
# python3 dfp_get_connectivity_file.py example/dfp-test.conf example/dfp-test-intra example/dfp-test-inter example/output
# Options:
# --quiet Suppress print output
# --debug Show all print output
# --------------------------------------------------------
def auto_str(cls):
def __str__(self):
return '%s(%s)' % (type(self).__name__,', '.join('%s=%s' % item for item in sorted(vars(self).items())))
cls.__str__ = __str__
return cls
# ENUMS --------------------------------------------------
class Loudness(Enum):
DEBUG = 0 #prints all output
LOUD = 2 #prints a lot of output
STANDARD = 3 #prints what step its working on
QUIET = 4 #print no output
class LP_Type(Enum):
ROUTER = 0
TERMINAL = 1
MODELNET = 3 #LPs that aren't relevant to the model topology
class ConnectionType(Enum):
INTRA = 0
INTER = 1
TERM = 3
# CLASSES -------------------------------------------------
@auto_str
class Params():
def __init__(self):
self.loaded = False
#Modelnet Group Parameters
self.mn_num_rep = 0
self.mn_nw_lp = 0
self.mn_num_term_rep = 0
self.mn_num_router_rep = 0
#derived modelnet params
self.mn_num_lp_per_rep = 0
self.mn_total_lps = 0 #total LPs including those from nw-lp
#Dragonfly Custom Parameters
self.num_groups = 0
self.num_router_spine = 0
self.num_router_leaf = 0
self.num_term_per_router = 0
self.intra_filename = 0
self.inter_filename = 0
#derived dragonfly params
self.num_routers_per_group = 0
self.num_terminals_per_group = 0
self.total_leaf = 0
self.total_spine = 0
self.total_routers = 0
self.total_terminals = 0
self.total_df_lps = 0 #Doesn't care about nw-lp stuff
def load_config(self, filename, intra_filename, inter_filename):
log("Loading Configuraiton...")
with open(filename) as f:
for line in f:
line = line.strip()
line = line.split("=") #there was weird behavior, modelnet_dragonfly_custom_router wasn't being found
val = re.findall('"([^"]*)"', line[-1]) #regex for the value in quotes
if len(val) > 0:
val = val[0] #OOR if the regex finds no parameter - this conditional avoids that error
if "repetitions" in line:
self.mn_num_rep = int(val)
elif "nw-lp" in line:
self.mn_nw_lp = int(val)
elif "modelnet_dragonfly_plus" in line:
self.mn_num_term_rep = int(val)
elif "modelnet_dragonfly_plus_router" in line:
self.mn_num_router_rep = int(val)
elif "num_groups" in line:
self.num_groups = int(val)
elif "num_router_spine" in line:
self.num_router_spine = int(val)
elif "num_router_leaf" in line:
self.num_router_leaf = int(val)
elif "num_cns_per_router" in line:
self.num_term_per_router = int(val)
self.intra_filename = intra_filename
self.inter_filename = inter_filename
self.mn_num_lp_per_rep = self.mn_nw_lp + self.mn_num_term_rep + self.mn_num_router_rep
self.mn_total_lps = self.mn_num_rep * self.mn_num_lp_per_rep
self.num_routers_per_group = self.num_router_spine + self.num_router_leaf
self.num_terminals_per_group = self.num_term_per_router * self.num_router_leaf
self.total_leaf = self.num_router_leaf * self.num_groups
self.total_spine = self.num_router_spine * self.num_groups
self.total_routers = self.num_groups * self.num_routers_per_group
self.total_terminals = self.total_leaf * self.num_term_per_router
self.total_df_lps = self.total_routers + self.total_terminals
self.loaded = True
def toStringPretty(self):
outstr = ""
outstr += "ModelNet Parameters:\n"
outstr += "\tRepetitions: %d\n" % self.mn_num_rep
outstr += "\tnw-lp: %d\n" % self.mn_nw_lp
outstr += "\tTerminals per Rep: %d\n" % self.mn_num_term_rep
outstr += "\tRouters per Rep: %d\n" % self.mn_num_router_rep
outstr += "\tNum LP Per Rep: %d\n" % self.mn_num_lp_per_rep
outstr += "\tTotal LPs (w/ mn): %d\n" % self.mn_total_lps
outstr += "\n"
outstr += "Dragonfly Plus Parameters:\n"
outstr += "\tNum Groups: %d\n" % self.num_groups
outstr += "\tNum Spine per Group: %d\n" % self.num_router_spine
outstr += "\tNum Leaf per Group: %d\n" % self.num_router_leaf
outstr += "\tNum Term Per Router: %d\n" % self.num_term_per_router
outstr += "\tNum Routers per Group: %d\n" % self.num_routers_per_group
outstr += "\tNum Term per Group: %d\n" % self.num_terminals_per_group
outstr += "\tTotal Spine: %d\n" % self.total_spine
outstr += "\tTotal Leaf: %d\n" % self.total_leaf
outstr += "\tTotal Routers: %d\n" % self.total_routers
outstr += "\tTotal Terminals: %d\n" % self.total_terminals
outstr += "\tTotal LPs (DFP only): %d\n" % self.total_df_lps
return outstr
class Network(object):
def __init__(self):
self.all_lps = {} # maps tw_lpid_df to the lp object. Note, the key is not the lp_gid but the lpid for the Dragonfly LP (Ignores nw-lp)
self.all_routers = {}
self.all_terminals = {}
self.lps_created = 0 #running total of dragonfly lps created
self.routers_created = 0 #running total of dragonfly routers created
self.terminals_created = 0 #running total of dragonfly terminals created
log("Creating LPs from Parameters...")
assert(params.loaded)
for repi in range(params.mn_num_rep):
for lpi in range(params.mn_num_lp_per_rep):
lp_gid = repi * params.mn_num_lp_per_rep + lpi
if lpi < params.mn_nw_lp:
continue #we don't care about nw-lp
elif ( (lpi >= params.mn_nw_lp) and (lpi < (params.mn_nw_lp + params.mn_num_term_rep)) ): #terminal
group_id = self.terminals_created // params.num_terminals_per_group
newTerm = Terminal(lp_gid, self.lps_created, group_id, self.terminals_created)
self.all_lps[self.lps_created] = newTerm
self.all_terminals[self.terminals_created] = newTerm
self.terminals_created += 1
self.lps_created += 1
else: #router
group_id = self.routers_created // params.num_routers_per_group
newRouter = Router(lp_gid, self.lps_created, group_id, self.routers_created)
self.all_routers[self.routers_created] = newRouter
self.all_lps[self.lps_created] = newRouter
self.routers_created += 1
self.lps_created += 1
self.parseConnFiles()
self.spawnTerminalConnections()
def getGIDfromDFID(self, theID):
lp = self.all_lps[theID]
return lp.gid
def getGIDfromRelativeID(self, theID, lptype):
# lp = DragonflyLP(0,0,0,0)
if lptype is LP_Type.ROUTER:
lp = self.all_routers[theID]
elif lptype is LP_Type.TERMINAL:
lp = self.all_terminals[theID]
return lp.gid
def getAssignedRouterIDfromTermID(self, term_id):
group_id = term_id // (params.num_router_leaf * params.num_term_per_router)
local_router_id = term_id % params.num_term_per_router
return (group_id * params.num_routers_per_group) + local_router_id
def parseInterLine(self, line_struct):
src_router = self.all_routers[line_struct[0]]
dest_router = self.all_routers[line_struct[1]]
src_router.connectToRouter(dest_router, ConnectionType.INTER)
log("INTER %d (%dr) -> %d (%dr)" % (src_router.lpid_df, src_router.router_id, dest_router.lpid_df, dest_router.router_id), Loudness.DEBUG)
#make connections between each local router in each group
def parseIntraLine(self, line_struct):
for gi in range(params.num_groups):
src_router_id_df = gi * params.num_routers_per_group + line_struct[0] #the DF router ID of the source
dest_router_id_df = gi * params.num_routers_per_group + line_struct[1] #the DF router ID of the dest
src_router = self.all_routers[src_router_id_df]
dest_router = self.all_routers[dest_router_id_df]
src_router.connectToRouter(dest_router, ConnectionType.INTRA)
log("INTRA G%d %di (%dr) -> %di (%dr)" % (src_router.group_id, line_struct[0], src_router.router_id, line_struct[1], dest_router.router_id), Loudness.DEBUG)
def parseConnFile(self, filename, struct_format, isIntra=False):
struct_len = struct.calcsize(struct_format)
struct_unpack = struct.Struct(struct_format).unpack_from
with open(filename, 'rb') as f:
while True:
data = f.read(struct_len)
if not data:
break
line_struct = struct_unpack(data)
if isIntra:
self.parseIntraLine(line_struct)
else:
self.parseInterLine(line_struct)
def parseConnFiles(self):
log("Parsing Interconnection File...")
self.parseConnFile(params.inter_filename, "=2i")
log("Parsing Intraconnection File...")
self.parseConnFile(params.intra_filename, "=2i", isIntra=True) #DFP uses 2 ints, DFC uses 3
# for each terminal, add connections to and from the router that terminal is assigned to
def spawnTerminalConnections(self):
log("Spawning Terminal Connections...")
for t in self.all_terminals.values():
term_id = t.terminal_id
assigned_router_id = self.getAssignedRouterIDfromTermID(term_id)
dest_router = self.all_routers[assigned_router_id]
t.connectToRouter(dest_router)
log("TERM G%d %d (%dt) -> %d (%dr)" % (t.group_id, t.gid, t.terminal_id, dest_router.gid, dest_router.router_id), Loudness.DEBUG)
log("TERM G%d %d (%dr) -> %d (%dt)" % (t.group_id, dest_router.gid, dest_router.router_id, t.gid, t.terminal_id), Loudness.DEBUG)
def printRouterConnSizes(self):
for r in self.all_routers.values():
num_inter = 0
num_intra = 0
num_term = 0
for conn in r.connections:
if conn.conn_type is ConnectionType.INTER:
num_inter += 1
if conn.conn_type is ConnectionType.INTRA:
num_intra += 1
if conn.conn_type is ConnectionType.TERM:
num_term += 1
print("Router %d: Intra: %d Inter: %d Term: %d" % (r.router_id, num_intra, num_inter, num_term))
def writeOutputFile(self, filename):
log("Writing Output File: %s..." %filename)
#writes the output file in CSV format with pairs of tw_lpid_gids
with open(filename, "w") as csvf:
cw = csv.writer(csvf, delimiter=",")
for lp in self.all_lps.values():
for conn in lp.connections:
cw.writerow([str(conn.source_obj.gid), str(conn.dest_obj.gid), str(conn.source_obj.type.value), str(conn.dest_obj.type.value)])
# log("%d, %d, SRCTYPE:%d DESTTYPE:%d" % (conn.source_obj.gid, conn.dest_obj.gid, conn.source_obj.type.value, conn.dest_obj.type.value), Loudness.DEBUG)
class DragonflyLP(object):
def __init__(self, tw_lpid_gid, tw_lpid_df, group_id, lptype):
self.gid = tw_lpid_gid # The actual GID of the LP in the simulation - used for the output to CSV
self.lpid_df = tw_lpid_df # The lpid of the LP relative to the Dragonfly Model. Used as key in network LP dictionary
self.group_id = group_id
self.type = lptype # Is this a terminal or Router?
self.connections = [] # The connections to other LPs
def addConnection(self, conn):
self.connections.append(conn)
class Router(DragonflyLP):
def __init__(self, tw_lpid_gid, tw_lpid_df, group_id, router_id):
self.router_id = router_id
super().__init__(tw_lpid_gid, tw_lpid_df, group_id, LP_Type.ROUTER)
log("New Router: %d -- Router ID: %d -- Group: %d" % (self.gid, self.router_id, self.group_id) ,Loudness.DEBUG)
def connectToRouter(self, other_rtr, conn_type):
#connect one way since each connection is explicitly laid out in the file
conn = Connection(self, other_rtr, conn_type)
self.addConnection(conn)
class Terminal(DragonflyLP):
def __init__(self, tw_lpid_gid, tw_lpid_df, group_id, terminal_id):
self.terminal_id = terminal_id
super().__init__(tw_lpid_gid, tw_lpid_df, group_id, LP_Type.TERMINAL)
log("New Terminal: %d -- Term ID: %d -- Group: %d" % (self.gid, self.terminal_id, self.group_id) ,Loudness.DEBUG)
def connectToRouter(self, rtr):
#makes two connections since each connection is not explicitly laid out in a file
conn = Connection(self, rtr, ConnectionType.TERM)
oconn = Connection(rtr, self, ConnectionType.TERM)
self.addConnection(conn) #connect to
rtr.addConnection(oconn) #connect from
class Connection(object):
def __init__(self,src_obj, dest_obj, conn_type):
self.source_obj = src_obj
self.dest_obj = dest_obj
self.conn_type = conn_type
# GLOBALS ----------------------------------------------------
params = Params()
# UTILITY ----------------------------------------------------
def log(mystr, loudness=Loudness.STANDARD):
global LOUDNESS
if loudness.value >= LOUDNESS.value:
if loudness.value < Loudness.QUIET.value:
print(mystr)
#just prints out each integer in the file
def decodeBinaryFile(filename):
print("File: %s" % filename)
struct_format = "=1i"
struct_len = struct.calcsize(struct_format)
struct_unpack = struct.Struct(struct_format).unpack_from
with open(filename, 'rb') as f:
while True:
data = f.read(struct_len)
if not data:
break
line_struct = struct_unpack(data)
print("%d " % line_struct[0], end="")
def parseOptionArguments():
global LOUDNESS
if "--debug" in argv:
LOUDNESS = Loudness.DEBUG
if "--quiet" in argv:
LOUDNESS = Loudness.QUIET
def main():
global LOUDNESS
LOUDNESS = Loudness.STANDARD
if len(argv) < 5:
print("ERROR: Usage: python3 dfp_get_connectivity_file.py <config_filename> <intra_filename> <inter_filename> <output_filename> [--<option>]")
exit(1)
parseOptionArguments()
param_filename = argv[1]
intra_filename = argv[2]
inter_filename = argv[3]
output_filename = argv[4]
params.load_config(param_filename, intra_filename, inter_filename)
log(params.toStringPretty())
theNet = Network()
theNet.writeOutputFile(output_filename)
# theNet.printRouterConnSizes() #you can check how many connections each router has with this uncommented
if __name__ == '__main__':
main()
LPGROUPS
{
MODELNET_GRP
{
repetitions="5";
# name of this lp changes according to the model
nw-lp="16";
# these lp names will be the same for dragonfly-custom model
modelnet_dragonfly_plus="16";
modelnet_dragonfly_plus_router="8";
}
}
PARAMS
{
# packet size in the network
packet_size="1024";
modelnet_order=( "dragonfly_plus","dragonfly_plus_router" );
# scheduler options
modelnet_scheduler="fcfs";
# chunk size in the network (when chunk size = packet size, packets will not be
# divided into chunks)
chunk_size="1024";
# modelnet_scheduler="round-robin";
# number of routers within each group
# each router row corresponds to a chassis in Cray systems
num_router_spine="4";
# each router column corresponds to a slot in a chassis
num_router_leaf="4";
# number of links connecting between group levels per router
num_level_chans="1";
# number of groups in the network
num_groups="5";
# buffer size in bytes for local virtual channels
local_vc_size="8192";
#buffer size in bytes for global virtual channels
global_vc_size="16384";
#buffer size in bytes for compute node virtual channels
cn_vc_size="8192";
#bandwidth in GiB/s for local channels
local_bandwidth="5.25";
# bandwidth in GiB/s for global channels
global_bandwidth="1.5";
# bandwidth in GiB/s for compute node-router channels
cn_bandwidth="8.0";
# ROSS message size
message_size="608";
# number of compute nodes connected to router, dictated by dragonfly config
# file
num_cns_per_router="4";
# number of global channels per router
num_global_connections="4";
# network config file for intra-group connections
intra-group-connections="../src/network-workloads/conf/dragonfly-plus/dfp-test-intra";
# network config file for inter-group connections
inter-group-connections="../src/network-workloads/conf/dragonfly-plus/dfp-test-inter";
# routing protocol to be used
routing="minimal";
}
4,20,1,0
5,21,1,0
6,22,1,0
7,23,1,0
8,20,1,0
9,21,1,0
10,22,1,0
11,23,1,0
12,20,1,0
13,21,1,0
14,22,1,0
15,23,1,0
16,20,1,0
17,21,1,0
18,22,1,0
19,23,1,0
20,24,0,0
20,25,0,0
20,26,0,0
20,27,0,0
20,4,0,1
20,8,0,1
20,12,0,1
20,16,0,1
21,24,0,0
21,25,0,0
21,26,0,0
21,27,0,0
21,5,0,1
21,9,0,1
21,13,0,1
21,17,0,1
22,24,0,0
22,25,0,0
22,26,0,0
22,27,0,0
22,6,0,1
22,10,0,1
22,14,0,1
22,18,0,1
23,24,0,0
23,25,0,0
23,26,0,0
23,27,0,0
23,7,0,1
23,11,0,1
23,15,0,1
23,19,0,1
24,55,0,0
24,83,0,0
24,111,0,0
24,139,0,0
24,20,0,0
24,21,0,0
24,22,0,0
24,23,0,0
25,54,0,0
25,82,0,0
25,110,0,0
25,138,0,0
25,20,0,0
25,21,0,0
25,22,0,0
25,23,0,0
26,53,0,0
26,81,0,0
26,109,0,0
26,137,0,0
26,20,0,0
26,21,0,0
26,22,0,0
26,23,0,0
27,52,0,0
27,80,0,0
27,108,0,0
27,136,0,0
27,20,0,0
27,21,0,0
27,22,0,0
27,23,0,0
32,48,1,0
33,49,1,0
34,50,1,0
35,51,1,0
36,48,1,0
37,49,1,0
38,50,1,0
39,51,1,0
40,48,1,0
41,49,1,0
42,50,1,0
43,51,1,0
44,48,1,0
45,49,1,0
46,50,1,0
47,51,1,0
48,52,0,0
48,53,0,0
48,54,0,0
48,55,0,0
48,32,0,1
48,36,0,1
48,40,0,1
48,44,0,1
49,52,0,0
49,53,0,0
49,54,0,0
49,55,0,0
49,33,0,1
49,37,0,1
49,41,0,1
49,45,0,1
50,52,0,0
50,53,0,0
50,54,0,0
50,55,0,0
50,34,0,1
50,38,0,1
50,42,0,1
50,46,0,1
51,52,0,0
51,53,0,0
51,54,0,0
51,55,0,0
51,35,0,1
51,39,0,1
51,43,0,1
51,47,0,1
52,83,0,0
52,111,0,0
52,139,0,0
52,27,0,0
52,48,0,0
52,49,0,0
52,50,0,0
52,51,0,0
53,82,0,0
53,110,0,0
53,138,0,0
53,26,0,0
53,48,0,0
53,49,0,0
53,50,0,0
53,51,0,0
54,81,0,0
54,109,0,0
54,137,0,0
54,25,0,0
54,48,0,0
54,49,0,0
54,50,0,0
54,51,0,0
55,80,0,0
55,108,0,0
55,136,0,0
55,24,0,0
55,48,0,0
55,49,0,0
55,50,0,0
55,51,0,0
60,76,1,0
61,77,1,0
62,78,1,0
63,79,1,0
64,76,1,0
65,77,1,0
66,78,1,0
67,79,1,0
68,76,1,0
69,77,1,0
70,78,1,0
71,79,1,0
72,76,1,0
73,77,1,0
74,78,1,0
75,79,1,0
76,80,0,0
76,81,0,0
76,82,0,0
76,83,0,0
76,60,0,1
76,64,0,1
76,68,0,1
76,72,0,1
77,80,0,0
77,81,0,0
77,82,0,0
77,83,0,0
77,61,0,1
77,65,0,1
77,69,0,1
77,73,0,1
78,80,0,0
78,81,0,0
78,82,0,0
78,83,0,0
78,62,0,1
78,66,0,1
78,70,0,1
78,74,0,1
79,80,0,0
79,81,0,0
79,82,0,0
79,83,0,0
79,63,0,1
79,67,0,1
79,71,0,1
79,75,0,1
80,111,0,0
80,139,0,0
80,27,0,0
80,55,0,0
80,76,0,0
80,77,0,0
80,78,0,0
80,79,0,0
81,110,0,0
81,138,0,0
81,26,0,0
81,54,0,0
81,76,0,0
81,77,0,0
81,78,0,0
81,79,0,0
82,109,0,0
82,137,0,0
82,25,0,0
82,53,0,0
82,76,0,0
82,77,0,0
82,78,0,0
82,79,0,0
83,108,0,0
83,136,0,0
83,24,0,0
83,52,0,0
83,76,0,0
83,77,0,0
83,78,0,0
83,79,0,0
88,104,1,0
89,105,1,0
90,106,1,0
91,107,1,0
92,104,1,0
93,105,1,0
94,106,1,0
95,107,1,0
96,104,1,0
97,105,1,0
98,106,1,0