diff --git a/assign_hw/Makefile b/assign_hw/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..ab5471e7bf4129544d145b70bbdfe9d4e96a7baf --- /dev/null +++ b/assign_hw/Makefile @@ -0,0 +1,26 @@ +CXX=/usr/gnu/bin/g++ +CC=/usr/gnu/bin/gcc +LEDA=/n/paria/z/dga/LEDA-4.0 +OBJS=score.o parse_top.o parse_ptop.o +LIBS+=-L${LEDA} -lD3 -lW -lP -lG -lL -L/usr/X11R6/lib -lX11 -lm -L. +LDFLAGS+= -O3 -fomit-frame-pointer -m486 +CXXFLAGS = -I${LEDA}/incl +# Pick one of the two below. +#CXXFLAGS += -Wall -O3 -fomit-frame-pointer -m486 +CXXFLAGS += -O0 -g -Wall +DEPLIBS=$(OBJS) + +all: assign + +assign: ${DEPLIBS} ${OBJS} assign.o + ${CXX} assign.o -o assign ${LIBS} $(OBJS) ${LDFLAGS} + +assign_p: assign.po $(DEPLIBS) $(POBJS) + ${CXX} assign.po -pg -g -o assign_p ${LIBS} $(POBJS) + +assign.po: assign.cc + ${CXX} -c -pg -g -o assign.po assign.cc ${CXXFLAGS} + +clean: + /bin/rm -f *.o assign + diff --git a/assign_hw/assign.cc b/assign_hw/assign.cc new file mode 100644 index 0000000000000000000000000000000000000000..b76cab857ca65a5f610facbc59463e26824689aa --- /dev/null +++ b/assign_hw/assign.cc @@ -0,0 +1,503 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "physical.h" +#include "virtual.h" +#include "score.h" + +tb_pgraph PG(1,1); +tb_vgraph G(1,1); + +/* How can we chop things up? */ +#define PARTITION_BY_ANNEALING 0 + +#define MAX_DELAYS 64 + +int nparts = 0; /* DEFAULTS */ +int accepts = 0; +int nnodes = 0; +int partition_mechanism; +int on_line = 0; +int cycles_to_best = 0; +int batch_mode = 0; + +float sensitivity = .1; + +static const int initial_temperature = 100; +static const int temp_prob = 130; + +int refreshed = 0; + +node_array bestnodes, absnodes; +float bestscore, absbest; + +extern node pnodes[MAX_PNODES]; +extern node_array switch_index; +void parse_top(tb_vgraph &G, istream& i); +void parse_ptop(tb_pgraph &G, istream& i); + +/* + * Basic simulated annealing parameters: + * + * Make changes proportional to T + * Accept worse solution with p = e^(change/Temperature*sensitivity) + * + */ + +inline int accept(float change, float temperature) +{ + float p; + int r; + + if (change == 0) { + p = 1000 * temperature / temp_prob; + } else { + p = expf(change/(temperature*sensitivity)) * 1000; + } + r = random() % 1000; + if (r < p) { + accepts++; + return 1; + } + return 0; +} + +/* + * The workhorse of our program. + * + * Assign performs an assignment of the virtual nodes (vnodes) to + * nodes in the physical topology. + * + * The input virtual topology is the graph G (global) + * the input physical topology is the topology topo (global). + * + * The simulated annealing logic is contained herein, + * except for the "accept a bad change" computation, + * which is performed in accept(). + */ + +int assign() +{ + float newscore, bestscore, absbest; + node n; + int iters = 0; + + float timestart = used_time(); + float timeend; + float scorediff; + + nnodes = G.number_of_nodes(); + + float cycles = 120.0*(float)(nnodes + G.number_of_edges()); + + int mintrans = (int)cycles; + int trans; + int naccepts = 40*nnodes; + int accepts = 0; + int oldpos; + + float temp = initial_temperature; + + /* Set up the initial counts */ + init_score(); + + bestscore = get_score(); + absbest = bestscore; + node n3; + forall_nodes(n3, G) { + absnodes[n3] = G[n3].posistion; + } + + if (bestscore < 0.11f) { +#ifdef VERBOSE + cout << "Problem started optimal\n"; +#endif + goto DONE; + } + + while (temp >= 2) { +#ifdef VERBOSE + cout << "Temperature: " << temp << endl; +#endif + trans = 0; + accepts = 0; + + while (trans < mintrans && accepts < naccepts) { + int newpos; + trans++; + iters++; + n = G.choose_node(); + oldpos = G[n].posistion; + + newpos = oldpos; + /* XXX: Room for improvement. :-) */ + while (newpos == oldpos) + newpos = random() % nparts; + if (oldpos != 0) { + remove_node(n); + } + add_node(n,newpos); + newscore = get_score(); + if (newscore < 0.11f) { + timeend = used_time(timestart); + cout << "OPTIMAL (0.0) in " + << iters << " iters, " + << timeend << " seconds" << endl; + goto DONE; + } + /* So it's negative if bad */ + scorediff = bestscore - newscore; + + if (newscore < bestscore || accept(scorediff, temp)) { + bestnodes[n] = G[n].posistion; + bestscore = newscore; + accepts++; + if (newscore < absbest) { + node n2; + forall_nodes(n2, G) { + absnodes[n2] = G[n2].posistion; + } + absbest = newscore; + cycles_to_best = iters; + } + } else { /* Reject this change */ + remove_node(n); + add_node(n,oldpos); + } + } + + temp *= .9; + } + + DONE: + forall_nodes(n, G) { + bestnodes[n] = absnodes[n]; + } + bestscore = absbest; + + forall_nodes(n, G) { + if (G[n].posistion != 0) + remove_node(n); + add_node(n,absnodes[n]); + } + + timeend = used_time(timestart); + cout << " BEST SCORE: " << get_score() << " in " + << iters << " iters and " << timeend << " seconds" << endl; + cout << "With " << accepts << " accepts of increases\n"; + cout << "Iters to find best score: " << cycles_to_best << endl; + + return 0; +} + +/* + * A legacy function from a less general version of the program. + * + * Now simply resets the node assignment, performs a new assignment, + * and prints out the results. + * + */ +void loopassign() +{ + node_array nodestorage; + int optimal = 0; + float timestart = used_time(); + float totaltime; + + nodestorage.init(G, 0); + bestnodes.init(G, 0); + absnodes.init(G, 0); + + nnodes = G.number_of_nodes(); + optimal = assign(); + totaltime = used_time(timestart); + + if (violated > 0) { + cout << "violations: " << violated << endl; + } + cout << "Total time to find solution " + << totaltime << " seconds" << endl; +} + +/* + * If we have more ways of partitioning the graph other than just + * simulated annealing, throw them in here. + */ + +void chopgraph() { + switch(partition_mechanism) { + case PARTITION_BY_ANNEALING: + loopassign(); + break; + default: + cerr << "Unknown partition mechanism. eeeek." << endl; + exit(-1); + } +} + +/* + * Something in the graph has changed! Better redisplay. + * + * Performs the color assignment for whichever switch the + * node belongs to and shows the inter-switch links as + * dashed lines. + */ + +void display_scc(GraphWin& gw) +{ + edge e; + node n; + + if (!refreshed) { + if (on_line) + chopgraph(); + } + + refreshed = 0; + + /* Now color them according to their partition */ + // XXX - Need to color them according to switch now! + // XXX - Need to add labels based on physical node matching + forall_nodes(n, G) { + int switchi; + if (G[n].posistion) { + gw.set_label(n,PG[pnodes[G[n].posistion]].name); + switchi = switch_index[PG[pnodes[G[n].posistion]].the_switch]; + switch (switchi) { + case 0: + gw.set_color(n, black); + break; + case 1: + gw.set_color(n, blue); + break; + case 2: + gw.set_color(n, green); + break; + case 3: + gw.set_color(n, red); + break; + case 4: + gw.set_color(n, yellow); + break; + case 5: + gw.set_color(n, violet); + break; + case 6: + gw.set_color(n, cyan); + break; + case 7: + gw.set_color(n, brown); + break; + case 8: + gw.set_color(n, pink); + break; + case 9: + gw.set_color(n, orange); + break; + case 10: + gw.set_color(n, grey1); + break; + case 11: + gw.set_color(n, grey3); + break; + } + } + } + + forall_edges(e, G) { + node v = G.source(e); + node w = G.target(e); + node sa,sb; + if (G[v].posistion && G[w].posistion) { + sa = PG[pnodes[G[v].posistion]].the_switch; + sb = PG[pnodes[G[w].posistion]].the_switch; + if (sa == sb) { + gw.set_style(e, solid_edge); + } else { + gw.set_style(e, dashed_edge); + } + } + } + gw.redraw(); +} + +/* + * Someone clicked on the "reassign" button. + * Reset and redisplay. + */ + +void reassign(GraphWin& gw) +{ + bestnodes.init(G, 0); + absnodes.init(G, 0); + chopgraph(); + refreshed = 1; + display_scc(gw); +} + +// XXX : another code by copy +void batch() +{ + bestnodes.init(G, 0); + absnodes.init(G, 0); + chopgraph(); +} + +void new_edge_handler(GraphWin& gw, edge) { display_scc(gw); } +void del_edge_handler(GraphWin& gw) { display_scc(gw); } +void new_node_handler(GraphWin& gw, node) { display_scc(gw); } +void del_node_handler(GraphWin& gw) { display_scc(gw); } + +void usage() { + fprintf(stderr, + "usage: assign [-h] [-bao] [-s ] [-n nodes/switch] [-c cap] [file]\n" + " -h ...... brief help listing\n" + // " -s # ... number of switches in cluster\n" + // " -n # ... number of nodes per switch\n" + " -a ...... Use simulated annealing (default)\n" + " -o ...... Update on-line (vs batch, default)\n" + " -t Input topology desc. from \n" + " -b ...... batch mode (no gui)\n" + ); +} + +void print_solution() +{ + node n; + cout << "Best solution: " << absbest << endl; + forall_nodes(n,G) { + if (!G[n].posistion) { + cout << "unassigned: " << G[n].name << endl; + } else { + node pnode = pnodes[G[n].posistion]; + tb_pnode &pnoder = PG[pnode]; + cout << G[n].name << " " << PG[pnoder.the_switch].name << " " + << pnoder.name << endl; + } + } + cout << "End solution" << endl; +} + +int main(int argc, char **argv) +{ + int h_menu; + extern char *optarg; + extern int optind; + char *topofile = NULL; + + int ch; + + partition_mechanism = PARTITION_BY_ANNEALING; + + while ((ch = getopt(argc, argv, "boas:n:t:h")) != -1) + switch(ch) { + case 'h': usage(); exit(0); + // case 's': nparts = atoi(optarg); break; + case 'a': partition_mechanism = PARTITION_BY_ANNEALING; break; + case 'o': on_line = 1; break; + case 't': topofile = optarg; break; + case 'b': batch_mode = 1; break; + default: usage(); exit(-1); + } + + argc -= optind; + argv += optind; + + srandom(time(NULL) + getpid()); + + /* + * Set up the LEDA graph window environment. Whenever + * the user does anything to the graph, call the + * proper handler. + */ + // XXX: alas, we need this because all the GW stuff is not in + // the same spot. + GraphWin gw(G, "Flux Testbed: Simulated Annealing"); + if (! batch_mode) { + gw.set_init_graph_handler(del_edge_handler); + gw.set_new_edge_handler(new_edge_handler); + gw.set_del_edge_handler(del_edge_handler); + gw.set_new_node_handler(new_node_handler); + gw.set_del_node_handler(del_node_handler); + + gw.set_node_width(24); + gw.set_node_height(24); + } + /* + * Allow the user to specify a topology in ".top" format. + */ + + if (argc == 1) { + ifstream infile; + infile.open(argv[0]); + if (!infile || !infile.good()) { + cerr << "Error opening file: " << argv[0] << endl; + exit(-11); + } + parse_top(G, infile); + if (! batch_mode) { + gw.update_graph(); + node n; + forall_nodes(n, G) { + if (G[n].name == NULL) { + G[n].name = ""; + } + gw.set_label(n, G[n].name); + gw.set_position(n, + point(random() % 200, random() % 200)); + } + } + } + + /* + * Allow the user to specify a physical topology + * in .phys format. Fills in the "topo" global variable. + * Make no mistake: This is actually mandatory now. + */ + if (topofile != NULL) { + cout << "Parsing ptop\n"; + ifstream ptopfile; + ptopfile.open(topofile); + if (!ptopfile || !ptopfile.good()) { + cerr << "Error opening file: " << topofile << endl; + exit(-1); + } + parse_ptop(PG,ptopfile); + nparts = PG.number_of_nodes(); + cout << "Nparts: " << nparts << endl; + } + + if (! batch_mode) { + gw.display(); + + gw.set_directed(false); + + gw.set_node_shape(circle_node); + gw.set_node_label_type(user_label); + + h_menu = gw.get_menu("Layout"); + gw_add_simple_call(gw, reassign, "Reassign", h_menu); + + /* Run until the user quits. Everything is handled by callbacks + * from LEDA's event loop from here on. */ + + gw.edit(); + } else { + batch(); + } + + print_solution(); + + return 0; +} diff --git a/assign_hw/common.h b/assign_hw/common.h index bf72ab0b79489bae404aae1214da27b65223ccb6..5f374d1d437d3c589f43b4d917daba825b69ee65 100644 --- a/assign_hw/common.h +++ b/assign_hw/common.h @@ -1,3 +1,6 @@ +#ifndef __COMON_H +#define __COMON_H + class tb_type { public: char *name; @@ -7,7 +10,8 @@ const int MAX_PNODES = 1024; // maximum # of physical nodes typedef enum {TYPE_UNKNOWN, TYPE_PC, TYPE_DNARD, TYPE_DELAY, TYPE_DELAY_PC, TYPE_SWITCH} nodeType; enum {MAX_TYPES = 5}; - + +#endif diff --git a/assign_hw/parse_ptop.cc b/assign_hw/parse_ptop.cc index 62ae057043e96d755196ff73fa0136f675d61e36..20b09edcfa12cd486efcff5f35760a961d2a769b 100644 --- a/assign_hw/parse_ptop.cc +++ b/assign_hw/parse_ptop.cc @@ -22,9 +22,11 @@ extern node pnodes[MAX_PNODES]; // int -> node map extern tb_pgraph PG; // physical graph +node_array switch_index; void parse_ptop(tb_pgraph &G, istream& i) { + int switchi=0; dictionary nmap; node no1; edge ed1; @@ -87,6 +89,7 @@ void parse_ptop(tb_pgraph &G, istream& i) PG[no1].types[TYPE_SWITCH].name = "switch"; PG[no1].types[TYPE_SWITCH].max = 1; PG[no1].the_switch = no1; + switch_index[no1] = switchi++; } } if (! isswitch) diff --git a/assign_hw/physical.h b/assign_hw/physical.h index 2c5ec2b169f282917dc795b3c3e157735f10562e..916321b71dd5e4042660ac7692a103cdd64362d0 100644 --- a/assign_hw/physical.h +++ b/assign_hw/physical.h @@ -1,5 +1,9 @@ +#ifndef __PHYSICAL_H +#define __PHYSICAL_H + class tb_pnode { public: + tb_pnode() {;} friend ostream &operator<<(ostream &o, const tb_pnode& node) { o << "TBNode: " << node.name << endl; @@ -10,13 +14,11 @@ public: { return i; } - tb_pnode(); tb_type types[MAX_TYPES]; // array of types, with counts of max nodeType current_type; // the current type of the node int max_load; // maxmium load for current type int current_load; // how many vnodes are assigned to this pnode - // int index; // index for switches char *name; node the_switch; // the switch the node is attached to int pnodes_used; // for switch nodes @@ -24,6 +26,7 @@ public: class tb_plink { public: + tb_plink() {;} friend ostream &operator<<(ostream &o, const tb_plink& edge) { o << "unimplemeted ostream << for tb_plinke " << endl; @@ -46,3 +49,5 @@ private: }; typedef GRAPH tb_pgraph; + +#endif diff --git a/assign_hw/score.cc b/assign_hw/score.cc index 6d1ecba6ac21a4c2300a7b04105aaa132423d40d..291c73780736b945d4238875ebd57ddf2a49a9d9 100644 --- a/assign_hw/score.cc +++ b/assign_hw/score.cc @@ -37,7 +37,13 @@ extern tb_pgraph PG; // physical grpaph int find_interswitch_path(node src,node dst,int bandwidth,edge *f,edge *s); edge *direct_link(node a,node b); - + +/* + * score() + * Returns the score. + */ +float get_score() {return score;} + /* * init_score() * This initialized the scoring system. It also clears all @@ -55,7 +61,7 @@ void init_score() vn.posistion=0; vn.no_connections=0; score += SCORE_UNASSIGNED; - violations++; + violated++; } forall_edges(e,G) { tb_vlink &ve=G[e]; diff --git a/assign_hw/score.h b/assign_hw/score.h index a7cf7e7117efea3b84d270e05da853d4e57cd134..865df798280080fddf8e0f87291379609d8123c2 100644 --- a/assign_hw/score.h +++ b/assign_hw/score.h @@ -29,10 +29,11 @@ const float SCORE_UNASSIGNED = 1; extern float score; -extern int violations; +extern int violated; void init_score(); void remove_node(node n); -void add_node(node n); +int add_node(node n,int loc); +float get_score(); #endif diff --git a/assign_hw/virtual.h b/assign_hw/virtual.h index c9dbfc92c9dc159b00ac4052b8980aeee62e960f..a6831a5a003fd90a616ee5ecbfc159bbf00b12b2 100644 --- a/assign_hw/virtual.h +++ b/assign_hw/virtual.h @@ -1,17 +1,42 @@ +#ifndef __VIRTUAL_H +#define __VIRTUAL_H + class tb_vnode { public: + tb_vnode() {;} + friend ostream &operator<<(ostream &o, const tb_vnode& node) + { + o << "TBVNode: " << node.name << endl; + return o; + } + + friend istream &operator>>(istream &i, const tb_vnode& node) + { + return i; + } + int posistion; // index into pnode array int no_connections; // how many unfulfilled connections from this node nodeType type; // type of the node char *name; // string name of the node }; -enum {LINK_DIRECT,LINK_INTRASWITCH,LINK_INTERSWITCH} tb_link_type; +typedef enum {LINK_DIRECT,LINK_INTRASWITCH,LINK_INTERSWITCH} tb_link_type; class tb_plink; class tb_vlink { public: + tb_vlink() {;} + friend ostream &operator<<(ostream &o, const tb_vlink& edge) + { + o << "unimplemeted ostream << for tb_vlink " << endl; + return o; + } + friend istream & operator>>(istream &i, const tb_vlink& edge) + { + return i; + } typedef enum {LINK_UNKNOWN, LINK_DIRECT, LINK_INTRASWITCH, LINK_INTERSWITCH} linkType; @@ -22,3 +47,5 @@ public: }; typedef GRAPH tb_vgraph; + +#endif