From 83cfa8ec9c26a858f8d0ec0ac277a28e905a1bde Mon Sep 17 00:00:00 2001 From: Robert Ricci Date: Thu, 26 Jun 2003 19:13:15 +0000 Subject: [PATCH] Major changes to the way assign handles LAN nodes. LAN nodes are no longer treated specially. Instead, I've introduced the idea of 'static' types (old-style types retroactively become 'dynamic' types). While a pnode can only satisfy one dynamic type at a time, it can always satisfy its static types (assuming it has enough capacity left.) Static types are flagged by prepending them with a '*' in the ptop file. So, for example, you may give switches the '*lan:10000' type so that they can satisfy virtual LAN nodes. Of course, other pnodes can have this type too, so that we can get 'trivial LANs'. Actually, removing special treatment for LANs cleans up a lot of code. However, it may have some negative impacts on solutions, since we're not as smart about where to place LAN nodes as we used to be (they get annealed along with everything else, and not migrated.) I haven't seen any evidence of this yet, however. This leaves us with a single type of special pnode, a switch. Also added a new bit of syntax in ptop files - when '*' is given as a the maxiumum load for a type, the node is allowed to take on an infinite (well, actually, just a really big number of) vnodes of that type. ptopgen was modified to always report switches as being capable of hosting LANs, and assign_wrapper now understands direct links to LANs, which is what we get when the LAN is hosted directly on a switch. Fixed a bug in scoring direct links, in which the penatly was being added once when a direct link was mapped, but subtracted only once when it was freed. Added a '-T' option for doing simple self-testing. When adding a node to the solution, assign records the score, adds the node, removes it again, and checks to make sure that the resulting score is the same as the original score. The usefulness of this feature in debugging scoring problems cannot be understated... --- assign/anneal.cc | 331 ++++++++++++++++-------------------- assign/anneal.h | 4 +- assign/assign.cc | 25 ++- assign/assign_internals.txt | 28 +-- assign/parse_ptop.cc | 22 ++- assign/pclass.cc | 15 +- assign/physical.h | 53 +++++- assign/score.cc | 210 ++++++++++++++--------- assign/solution.cc | 4 +- tbsetup/assign_wrapper.in | 5 +- tbsetup/ptopgen.in | 4 +- 11 files changed, 375 insertions(+), 326 deletions(-) diff --git a/assign/anneal.cc b/assign/anneal.cc index aea837f9b..76a55b254 100644 --- a/assign/anneal.cc +++ b/assign/anneal.cc @@ -48,6 +48,18 @@ inline int accept(double change, double temperature) return 0; } +// finds a random pnode, of any type at all +tb_pnode *find_random_pnode() { + int choice = std::random() % num_vertices(PG); + pvertex_iterator vit, vendit; + tie(vit,vendit) = vertices(PG); + cout << "Chose pnode " << choice << " of " << num_vertices(PG) << endl; + for (int i = 0; i < choice;++vit, ++i) { + } + tb_pnode *curP = get(pvertex_pmap,*vit); + return curP; +} + tb_pnode *find_pnode(tb_vnode *vn) { #ifdef PER_VNODE_TT @@ -67,7 +79,6 @@ tb_pnode *find_pnode(tb_vnode *vn) #ifdef PCLASS_SIZE_BALANCE int acceptchance = 1000 * (*acceptable_types)[i]->size * 1.0 / npnodes; - //cout << "Chance was " << acceptchance << endl; if ((std::rand() % 1000) < acceptchance) { continue; } @@ -119,7 +130,8 @@ REDO_SEARCH: } #else if ((*it)->typed && ((*it)->current_type.compare(vn->type) || - ((*it)->current_load >= (*it)->max_load))) { + ((*it)->current_type_record->current_load >= + (*it)->current_type_record->max_load))) { it++; } else { break; @@ -152,7 +164,7 @@ REDO_SEARCH: RDEBUG(cout << " to " << newpnode->name << endl;) return newpnode; } - + #ifndef PCLASS_SIZE_BALANCE if (i == first) { // couldn't find one @@ -164,7 +176,7 @@ REDO_SEARCH: /* When this is finished the state will reflect the best solution found. */ -void anneal() +void anneal(bool scoring_selftest) { cout << "Annealing." << endl; @@ -213,6 +225,10 @@ void anneal() /* Set up the initial counts */ init_score(); + /* We'll check against this later to make sure that whe we've unmapped + * everything, the score is the same */ + double initial_score = get_score(); + /* Set up fixed nodes */ for (name_name_map::iterator fixed_it=fixed_nodes.begin(); fixed_it!=fixed_nodes.end();++fixed_it) { @@ -383,12 +399,7 @@ void anneal() } else { int start = std::random()%nnodes; int choice = start; -#if defined(FIX_LAN_NODES) || defined(AUTO_MIGRATE) - while (get(vvertex_pmap,virtual_nodes[choice])->fixed || - !get(vvertex_pmap,virtual_nodes[choice])->type.compare("lan")) { -#else while (get(vvertex_pmap,virtual_nodes[choice])->fixed) { -#endif choice = (choice +1) % nnodes; if (choice == start) { choice = -1; @@ -423,192 +434,156 @@ void anneal() vn->vclass->dominant << endl; #endif } - if (vn->type.compare("lan") == 0) { - // LAN node - pvertex lanv = make_lan_node(vv); -#ifndef FREE_IMMEDIATELY - if (oldassigned) { - RDEBUG(cout << "removing: lan,oldassigned" << endl;) - remove_node(vv); - } -#endif - if (add_node(vv,lanv,false) != 0) { - delete_lan_node(lanv); - unassigned_nodes.push(vvertex_int_pair(vv,std::random())); - continue; - } - } else { - tb_pnode *newpnode = find_pnode(vn); + tb_pnode *newpnode = find_pnode(vn); #ifndef FREE_IMMEDIATELY - if (oldassigned) { - RDEBUG(cout << "removing: !lan, oldassigned" << endl;) + if (oldassigned) { + RDEBUG(cout << "removing: !lan, oldassigned" << endl;) remove_node(vv); - } + } #endif - if (newpnode == NULL) { - // We're not going to be re-assigning this one + if (newpnode == NULL) { + // We're not going to be re-assigning this one #ifndef SMART_UNMAP - unassigned_nodes.push(vvertex_int_pair(vv,std::random())); + unassigned_nodes.push(vvertex_int_pair(vv,std::random())); #endif - // need to free up nodes + // need to free up nodes #ifdef SMART_UNMAP - // XXX: Should probably randomize this - // XXX: Add support for not using PER_VNODE_TT - // XXX: Not very robust - - freednode = true; - - tt_entry tt = vnode_type_table[vn->name]; - int size = tt.first; - pclass_vector *acceptable_types = tt.second; - // Find a node to kick out - bool foundnode = false; - int offi = std::rand(); - int index; - for (int i = 0; i < size; i++) { - index = (i + offi) % size; - if ((*acceptable_types)[index]->used_members.find(vn->type) == - (*acceptable_types)[index]->used_members.end()) { - continue; - } - if ((*acceptable_types)[index]->used_members[vn->type]->size() == 0) { - continue; - } - foundnode = true; - break; + // XXX: Should probably randomize this + // XXX: Add support for not using PER_VNODE_TT + // XXX: Not very robust + + freednode = true; + + tt_entry tt = vnode_type_table[vn->name]; + int size = tt.first; + pclass_vector *acceptable_types = tt.second; + // Find a node to kick out + bool foundnode = false; + int offi = std::rand(); + int index; + for (int i = 0; i < size; i++) { + index = (i + offi) % size; + if ((*acceptable_types)[index]->used_members.find(vn->type) == + (*acceptable_types)[index]->used_members.end()) { + continue; + } + if ((*acceptable_types)[index]->used_members[vn->type]->size() == 0) { + continue; } + foundnode = true; + break; + } - if (foundnode) { - assert((*acceptable_types)[index]->used_members[vn->type]->size()); - tb_pclass::tb_pnodeset::iterator it = (*acceptable_types)[index]->used_members[vn->type]->begin(); - int j = std::rand() % (*acceptable_types)[index]->used_members[vn->type]->size(); - while (j > 0) { - it++; - j--; - } - tb_vnode_set::iterator it2 = (*it)->assigned_nodes.begin(); - int k = std::rand() % (*it)->assigned_nodes.size(); - while (k > 0) { - it2++; - k--; - } - tb_vnode *kickout = *it2; - assert(kickout->assigned); - vvertex toremove = vname2vertex[kickout->name]; - newpnode = *it; - remove_node(toremove); - unassigned_nodes.push(vvertex_int_pair(toremove, - std::random())); - } else { - cerr << "Failed to find a replacement!" << endl; + if (foundnode) { + assert((*acceptable_types)[index]->used_members[vn->type]->size()); + tb_pclass::tb_pnodeset::iterator it = (*acceptable_types)[index]->used_members[vn->type]->begin(); + int j = std::rand() % (*acceptable_types)[index]->used_members[vn->type]->size(); + while (j > 0) { + it++; + j--; + } + tb_vnode_set::iterator it2 = (*it)->assigned_nodes.begin(); + int k = std::rand() % (*it)->assigned_nodes.size(); + while (k > 0) { + it2++; + k--; } + tb_vnode *kickout = *it2; + assert(kickout->assigned); + vvertex toremove = vname2vertex[kickout->name]; + newpnode = *it; + remove_node(toremove); + unassigned_nodes.push(vvertex_int_pair(toremove, + std::random())); + } else { + cerr << "Failed to find a replacement!" << endl; + } #else - int start = std::random()%nnodes; - int toremove = start; -#if defined(FIX_LAN_NODES) || defined(AUTO_MIGRATE) - while (get(vvertex_pmap,virtual_nodes[toremove])->fixed || - (!get(vvertex_pmap,virtual_nodes[toremove])->assigned) || - (get(vvertex_pmap,virtual_nodes[toremove])->type.compare("lan"))) { -#else + int start = std::random()%nnodes; + int toremove = start; #ifdef SMART_UNMAP #ifdef PER_VNODE_TT - tt_entry tt = vnode_type_table[vn->name]; + tt_entry tt = vnode_type_table[vn->name]; #else tt_entry tt = type_table[vn->type]; #endif pclass_vector *acceptable_types = tt.second; while (1) { - bool keepgoing = false; - if (get(vvertex_pmap,virtual_nodes[toremove])->fixed) { - keepgoing = true; - } else if (! get(vvertex_pmap,virtual_nodes[toremove])->assigned) { - keepgoing = true; - } else { - pvertex pv = get(vvertex_pmap,virtual_nodes[toremove])->assignment; - tb_pnode *pn = get(pvertex_pmap,pv); - int j; - for (j = 0; j < acceptable_types->size(); j++) { - if ((*acceptable_types)[j] == pn->my_class) { - break; - } - } - if (j == acceptable_types->size()) { - keepgoing = true; - } - } - - if (!keepgoing) { + bool keepgoing = false; + if (get(vvertex_pmap,virtual_nodes[toremove])->fixed) { + keepgoing = true; + } else if (! get(vvertex_pmap,virtual_nodes[toremove])->assigned) { + keepgoing = true; + } else { + pvertex pv = get(vvertex_pmap,virtual_nodes[toremove])->assignment; + tb_pnode *pn = get(pvertex_pmap,pv); + int j; + for (j = 0; j < acceptable_types->size(); j++) { + if ((*acceptable_types)[j] == pn->my_class) { break; + } + } + if (j == acceptable_types->size()) { + keepgoing = true; } - + } + + if (!keepgoing) { + break; + } + #else - while (get(vvertex_pmap,virtual_nodes[toremove])->fixed || - (! get(vvertex_pmap,virtual_nodes[toremove])->assigned)) { -#endif + while (get(vvertex_pmap,virtual_nodes[toremove])->fixed || + (! get(vvertex_pmap,virtual_nodes[toremove])->assigned)) { #endif toremove = (toremove +1) % nnodes; if (toremove == start) { - toremove = -1; - break; + toremove = -1; + break; } - } - if (toremove >= 0) { + } + if (toremove >= 0) { RDEBUG(cout << "removing: freeing up nodes" << endl;) - remove_node(virtual_nodes[toremove]); + remove_node(virtual_nodes[toremove]); unassigned_nodes.push(vvertex_int_pair(virtual_nodes[toremove], - std::random())); - } - continue; + std::random())); + } + continue; #endif /* SMART_UNMAP */ #ifndef SMART_UNMAP - } else { + } else { #else - } + } #endif if (newpnode != NULL) { - newpos = pnode2vertex[newpnode]; - if (add_node(vv,newpos,false) != 0) { - unassigned_nodes.push(vvertex_int_pair(vv,std::random())); - continue; + newpos = pnode2vertex[newpnode]; + if (scoring_selftest) { + // Run a little test here - see if the score we get by adding + // this node, then removing it, is the same one we would have + // gotten otherwise + double oldscore = get_score(); + if (!add_node(vv,newpos,false)) { + remove_node(vv); } + assert(oldscore == get_score()); + } + if (add_node(vv,newpos,false) != 0) { + unassigned_nodes.push(vvertex_int_pair(vv,std::random())); + continue; + } } else { #ifdef SMART_UNMAP - unassigned_nodes.push(vvertex_int_pair(vv,std::random())); + unassigned_nodes.push(vvertex_int_pair(vv,std::random())); #endif - if (freednode) { - continue; - } + if (freednode) { + continue; + } } #ifndef SMART_UNMAP } -#endif - } - -#ifdef FIX_LAN_NODES - // OK, we're going to do something silly here: Migrate LAN nodes! - vvertex_iterator lanvertex_it,end_lanvertex_it; - vvertex_list migrate_lan_nodes; - tie(lanvertex_it,end_lanvertex_it) = vertices(VG); - for (;lanvertex_it!=end_lanvertex_it;++lanvertex_it) { - tb_vnode *vnode = get(vvertex_pmap,*lanvertex_it); - if (vnode->assigned) { - if (vnode->type.compare("lan") == 0) { - migrate_lan_nodes.push_front(*lanvertex_it); - } - } - } - while (migrate_lan_nodes.size() > 0) { - vvertex lanv = migrate_lan_nodes.front(); - migrate_lan_nodes.pop_front(); - RDEBUG(cout << "removing: migration" << endl;) - remove_node(lanv); - pvertex lanpv = make_lan_node(lanv); - add_node(lanv,lanpv,true); - } - #endif newscore = get_score(); @@ -717,9 +692,6 @@ void anneal() RDEBUG(cout << "removing: rejected change" << endl;) remove_node(vv); if (oldassigned) { - if (vn->type.compare("lan") == 0) { - oldpos = make_lan_node(vv); - } add_node(vv,oldpos,false); } } @@ -904,18 +876,7 @@ NOTQUITEDONE: // Only revert if the best configuration has better violations vvertex_list lan_nodes; vvertex_iterator vvertex_it,end_vvertex_it; - if (!revert) { - // Just find LAN nodes, for migration - tie(vvertex_it,end_vvertex_it) = vertices(VG); - for (;vvertex_it!=end_vvertex_it;++vvertex_it) { - tb_vnode *vnode = get(vvertex_pmap,*vvertex_it); - if (vnode->assigned) { - if (vnode->type.compare("lan") == 0) { - lan_nodes.push_front(*vvertex_it); - } - } - } - } else { + if (revert) { cout << "Reverting to best solution\n"; // Do a full revert tie(vvertex_it,end_vvertex_it) = vertices(VG); @@ -929,36 +890,28 @@ NOTQUITEDONE: RDEBUG(cout << "not removing: revert " << vnode->name << endl;) } } + + // Check to make sure that our 'clean' solution scores the same as + // the initial score - if not, that indicates a bug + if (get_score() != initial_score) { + cerr << "*** WARNING: 'Clean' score does not match initial score" << + endl << " This indicates a bug - contact the operators" << + endl << " (initial score: " << initial_score << + ", current score: " << get_score() << ")" << endl; + } tie(vvertex_it,end_vvertex_it) = vertices(VG); for (;vvertex_it!=end_vvertex_it;++vvertex_it) { tb_vnode *vnode = get(vvertex_pmap,*vvertex_it); if (vnode->fixed) continue; if (absassigned[*vvertex_it]) { - if (vnode->type.compare("lan") == 0) { - lan_nodes.push_front(*vvertex_it); - } else { - if (vnode->vclass != NULL) { - vnode->type = abstypes[*vvertex_it]; - } - assert(!add_node(*vvertex_it,absassignment[*vvertex_it],true)); + if (vnode->vclass != NULL) { + vnode->type = abstypes[*vvertex_it]; } + assert(!add_node(*vvertex_it,absassignment[*vvertex_it],true)); } } } - // Do LAN migration - RDEBUG(cout << "Doing LAN migration" << endl;) - while (lan_nodes.size() > 0) { - vvertex lanv = lan_nodes.front(); - lan_nodes.pop_front(); - if (!revert) { // If reverting, we've already done this - RDEBUG(cout << "removing: migration" << endl;) - remove_node(lanv); - } - pvertex lanpv = make_lan_node(lanv); - add_node(lanv,lanpv,true); - } - tsteps++; if (finished) { diff --git a/assign/anneal.h b/assign/anneal.h index 047839611..5cbd264a6 100644 --- a/assign/anneal.h +++ b/assign/anneal.h @@ -80,8 +80,6 @@ inline int accept(double change, double temperature); tb_pnode *find_pnode(tb_vnode *vn); /* The big guy! */ -void anneal(); - - +void anneal(bool scoring_selftest); #endif diff --git a/assign/assign.cc b/assign/assign.cc index e428ec7be..93a3d7422 100644 --- a/assign/assign.cc +++ b/assign/assign.cc @@ -298,6 +298,7 @@ void print_help() #ifdef PER_VNODE_TT cerr << " -P - Prune unusable pclasses." << endl; #endif + cerr << " -T - Doing some scoring self-testing." << endl; exit(2); } @@ -312,11 +313,6 @@ int type_precheck() { for (name_count_map::iterator vtype_it=vtypes.begin(); vtype_it != vtypes.end();++vtype_it) { - // Ignore LAN vnodes - if (vtype_it->first == crope("lan")) { - continue; - } - // Check to see if there were any pnodes of the type at all name_count_map::iterator ptype_it = ptypes.find(vtype_it->first); if (ptype_it == ptypes.end()) { @@ -366,11 +362,6 @@ int mapping_precheck() { for (;vit != vendit;vit++) { tb_vnode *v = get(vvertex_pmap,*vit); - // - // No reason to do this work for LAN nodes! - if (!v->type.compare("lan")) { - continue; - } pclass_vector *vec = new pclass_vector(); vnode_type_table[v->name] = tt_entry(0,vec); @@ -405,8 +396,12 @@ int mapping_precheck() { // Grab the first node of the pclass as a representative sample tb_pnode *pnode = *((*it)->members[this_type]->L.begin()); - // Check the number of interfaces - if (pnode->total_interfaces >= v->num_links) { + // Check the number of interfaces - if the pnode is a switch, + // for now, we don't check this, since it can end up with more + // 'interfaces' due to the fact that it can have interswitch + // links + if ((pnode->total_interfaces >= v->num_links) || + pnode->current_type.compare("switch")) { matched_links++; } else { potential_match = false; @@ -529,6 +524,7 @@ int main(int argc,char **argv) { int seed = 0; crope viz_prefix; + bool scoring_selftest = false; // Handle command line char ch; @@ -566,6 +562,8 @@ int main(int argc,char **argv) case 'P': prune_pclasses = true; break; #endif + case 'T': + scoring_selftest = true; break; default: print_help(); } @@ -653,8 +651,9 @@ int main(int argc,char **argv) } timestart = used_time(); - anneal(); + anneal(scoring_selftest); timeend = used_time(); + #ifdef GNUPLOT_OUTPUT fclose(scoresout); fclose(tempout); diff --git a/assign/assign_internals.txt b/assign/assign_internals.txt index 1d721218a..bb5e8ec7a 100644 --- a/assign/assign_internals.txt +++ b/assign/assign_internals.txt @@ -63,20 +63,24 @@ Each vnode has a type and a valid solution must match each vnode to a pnode that can support that type. A vnode has a single type but a pnode may be able to support several types and even have multiple vnodes in a single vnode. For example, a standard testbed PC with -four interfaces can either be a test node or up to two delay nodes. A -pnode that has not been matched to a vnode is called virgin and can -take on any of its types. Once it has been mapped to a vnode it -becomes typed and is locked into a certain type (that of the vnode). -When add_node is called the first thing checked is whether the pnode -has a suitable type for the vnode. If it is virgin then all its types -are checked, otherwise only its current type is checked. If the types -can not be matched then add_node returns an invalid condition, -otherwise it continues on. +four interfaces can either be a test node or up to two delay nodes. +The type system is composed of two classes of types: 'dynamic' types, +and 'static' types. A pnode can always satisfy any of its static +types, but has only one of its dynamic types 'active' at any time. A +pnode that has not been matched to a vnode using one of its dynamic +types is called virgin and can take on any of its dynamic types. Once +it has been mapped to a vnode it becomes typed and is locked into a +certain type (that of the vnode). When add_node is called the first +thing checked is whether the pnode has a suitable type for the vnode. +If the vnode uses a static type, the pnode must simply have available +capacity for that type. Otherwise, dynamic types are checked. If it is +virgin then all its types are checked, otherwise only its current type +is checked. If the types can not be matched then add_node returns an +invalid condition, otherwise it continues on. Each type of a pnode also has a limit. This is the number of vnodes -of that type that can fit in the pnode. A vnode can not be placed in a pnode unless there is room left. - -Under no conditions is a vnode mapped to a switch. +of that type that can fit in the pnode. A vnode can not be placed in +a pnode unless there is room left. Links ----- diff --git a/assign/parse_ptop.cc b/assign/parse_ptop.cc index 370e08c1b..1a31b7932 100644 --- a/assign/parse_ptop.cc +++ b/assign/parse_ptop.cc @@ -67,8 +67,18 @@ int parse_ptop(tb_pgraph &PG, tb_sgraph &SG, istream& i) if (split_two(parsed_line[i],':',type,load,"1") != 0) { ptop_error("Bad node line, no load for type: " << type << "."); } + + // Check to see if this is a static type + bool is_static = false; + if (type[0] == '*') { + is_static = true; + type.pop_front(); + } int iload; - if (sscanf(load.c_str(),"%d",&iload) != 1) { + if (!load.compare("*")) { // Allow * to mean unlimited + // (okay, a really big number) + iload = 10000000; + } else if (sscanf(load.c_str(),"%d",&iload) != 1) { ptop_error("Bad node line, bad load: " << load << "."); iload = 1; } @@ -79,14 +89,14 @@ int parse_ptop(tb_pgraph &PG, tb_sgraph &SG, istream& i) } if (type.compare("switch") == 0) { isswitch = true; - p->types["switch"] = 1; + p->types["switch"] = new tb_pnode::type_record(1,false); svertex sv = add_vertex(SG); tb_switch *s = new tb_switch(); put(svertex_pmap,sv,s); s->mate = pv; p->sgraph_switch = sv; } else { - p->types[type] = iload; + p->types[type] = new tb_pnode::type_record(iload,is_static); } } for (i=i+1;(ifeatures[feature] = gcost; } /* - * Parse any other node optios or flags + * Parse any other node options or flags */ for (i=i+1; i < parsed_line.size(); ++i) { crope flag,value; @@ -215,10 +225,11 @@ int parse_ptop(tb_pgraph &PG, tb_sgraph &SG, istream& i) pl->type = tb_plink::PLINK_INTERSWITCH; } } + srcnode->total_interfaces++; + dstnode->total_interfaces++; if (ISSWITCH(srcnode) && ! ISSWITCH(dstnode)) { dstnode->switches.insert(srcv); - dstnode->total_interfaces++; #ifdef PER_VNODE_TT dstnode->total_bandwidth += ibw; #endif @@ -226,7 +237,6 @@ int parse_ptop(tb_pgraph &PG, tb_sgraph &SG, istream& i) else if (ISSWITCH(dstnode) && ! ISSWITCH(srcnode)) { srcnode->switches.insert(dstv); - srcnode->total_interfaces++; #ifdef PER_VNODE_TT srcnode->total_bandwidth += ibw; #endif diff --git a/assign/pclass.cc b/assign/pclass.cc index 04b386e9c..4e211b7a7 100644 --- a/assign/pclass.cc +++ b/assign/pclass.cc @@ -66,10 +66,10 @@ int pclass_equiv(tb_pgraph &PG, tb_pnode *a,tb_pnode *b) for (tb_pnode::types_map::iterator it=a->types.begin(); it!=a->types.end();++it) { const crope &a_type = (*it).first; - const int a_max_nodes = (*it).second; + tb_pnode::type_record *a_type_record = (*it).second; tb_pnode::types_map::iterator bit = b->types.find(a_type); - if ((bit == b->types.end()) ||((*bit).second != a_max_nodes)) + if ((bit == b->types.end()) || ! ( *(*bit).second == *a_type_record) ) return 0; } @@ -222,7 +222,8 @@ int pclass_set(tb_vnode *v,tb_pnode *p) for (dit=c->members.begin();dit!=c->members.end();dit++) { if ((*dit).first == p->current_type) { // same class - only remove if node is full - if (p->current_load == p->max_load) { + if (p->types[p->current_type]->current_load == + p->types[p->current_type]->max_load) { (*dit).second->remove(p); //#ifdef SMART_UNMAP // c->used_members[(*dit).first]->push_back(p); @@ -246,7 +247,7 @@ int pclass_set(tb_vnode *v,tb_pnode *p) } - c->used += 1.0/(p->max_load); + c->used += 1.0/(p->current_type_record->max_load); return 0; } @@ -266,12 +267,12 @@ int pclass_unset(tb_pnode *p) // empty and the front if it's not. Since unset is called before // remove_node empty means only one user. if (! (*dit).second->exists(p)) { - assert(p->current_load > 0); + assert(p->types[p->current_type]->current_load > 0); #ifdef PNODE_ALWAYS_FRONT (*dit).second->push_front(p); #else #ifdef PNODE_SWITCH_LOAD - if (p->current_load == 0) { + if (p->types[p->current_type]->current_load == 0) { #else if (p->current_load == 1) { #endif @@ -295,7 +296,7 @@ int pclass_unset(tb_pnode *p) //cout << endl; - c->used -= 1.0/(p->max_load); + c->used -= 1.0/(p->current_type_record->max_load); return 0; } diff --git a/assign/physical.h b/assign/physical.h index d46f55da3..531edccd7 100644 --- a/assign/physical.h +++ b/assign/physical.h @@ -71,13 +71,34 @@ class tb_pnode { public: tb_pnode() { tb_pnode("(unnamed)"); } tb_pnode(crope _name) : types(), features(), name(_name), typed(false), - max_load(0), current_load(0), switches(), - sgraph_switch(), switch_used_links(0), + current_type_record(NULL), total_load(0), + switches(), sgraph_switch(), switch_used_links(0), total_interfaces(0), used_interfaces(0), total_bandwidth(0), my_class(NULL), assigned_nodes(), trivial_bw(0), trivial_bw_used(0) {;} - typedef hash_map types_map; + class type_record { + public: + type_record(int _max_load, bool _is_static) : + max_load(_max_load), current_load(0), + is_static(_is_static) { ; } + int max_load; // maximum load for this type + int current_load; // how many vnodes are assigned of this type + bool is_static; // whether this type is static or dynamic + + bool operator==(const type_record &b) { + return ((max_load == b.max_load) && (is_static == b.is_static)); + } + + friend ostream &operator<<(ostream &o, const type_record& node) + { + o << "max_load = " << node.max_load << + " current_load = " << node.current_load << + " is_static = " << node.is_static; + } + }; + + typedef hash_map types_map; typedef hash_map features_map; // contains max nodes for each type @@ -89,8 +110,10 @@ public: crope name; // name of the node bool typed; // has it been typed crope current_type; // type the node is currently being used as - int max_load; // maxmium load for current type - int current_load; // how many vnodes are assigned to this pnode + type_record* current_type_record; + int total_load; // total load for all types + //int max_load; // maxmium load for current type + //int current_load; // how many vnodes are assigned to this pnode pvertex_set switches; // what switches the node is attached to svertex sgraph_switch; // only for switches, the corresponding @@ -111,6 +134,22 @@ public: int trivial_bw_used; // the amount of bandwidth currently used by // trivial links + bool set_current_type(crope type) { + if (types.find(type) == types.end()) { + //cout << "Failed to find type " << type << endl; + return false; + } + current_type = type; + typed = true; + current_type_record = types[type]; + return true; + } + + void remove_current_type() { + typed = false; + current_type_record = NULL; + } + // Output operator for debugging friend ostream &operator<<(ostream &o, const tb_pnode& node) { @@ -123,8 +162,8 @@ public: for (features_map::const_iterator it = node.features.begin(); it!=node.features.end();it++) cout << " " << (*it).first << " -> " << (*it).second << endl; - o << " Current Type: " << node.current_type << - " (" << node.current_load << "/" << node.max_load << ")" << endl; + o << " Current Type: " << node.current_type << endl; /* << + " (" << node.current_load << "/" << node.max_load << ")" << endl; */ o << " switches="; for (pvertex_set::const_iterator it = node.switches.begin(); it != node.switches.end();++it) { diff --git a/assign/score.cc b/assign/score.cc index 842969ec3..8641afa75 100644 --- a/assign/score.cc +++ b/assign/score.cc @@ -144,16 +144,18 @@ void unscore_link_info(vedge ve,tb_pnode *src_pnode,tb_pnode *dst_pnode) if (vlink->link_info.type == tb_link_info::LINK_DIRECT) { // DIRECT LINK SDEBUG(cerr << " direct link" << endl); + SSUB(SCORE_DIRECT_LINK); unscore_link(vlink->link_info.plinks.front(),ve,src_pnode,dst_pnode); vlink->link_info.plinks.clear(); } else if (vlink->link_info.type == tb_link_info::LINK_INTERSWITCH) { // INTERSWITCH LINK SDEBUG(cerr << " interswitch link" << endl); - pedge_path &path = vlink->link_info.plinks; #ifndef INTERSWITCH_LENGTH SSUB(SCORE_INTERSWITCH_LINK); #endif + + pedge_path &path = vlink->link_info.plinks; // XXX: Potentially bogus; int numinterlinks; numinterlinks = -2; @@ -231,11 +233,32 @@ void remove_node(vvertex vv) assert(pnode != NULL); + /* + * Find the type on the pnode that this vnode is associated with + */ + tb_pnode::types_map::iterator mit = pnode->types.find(vnode->type); + tb_pnode::type_record *tr; + if (mit == pnode->types.end()) { + // This is kind of a hack - if we don't find the type, then the vnode + // must have a vtype. So, we assume it must have been assigned to this + // node's dynamic type at some point. + // A consequence of this hack is that vtypes can't map to static types, + // for now. + RDEBUG(cerr << "Failed to find type " << vnode->type << " (for vnode " << + vnode->name << ") on pnode " << pnode->name << endl;) + tr = pnode->current_type_record; + } else { + tr = mit->second; + } + + /* * Clean up the pnode's state */ - if (pnode->my_class) { - pclass_unset(pnode); + if (!tr->is_static) { + if (pnode->my_class) { + pclass_unset(pnode); + } } #ifdef SMART_UNMAP @@ -288,7 +311,13 @@ void remove_node(vvertex vv) // Only unscore the link if the vnode on the other end is assigned - this // way, only the first end to be unmapped causes unscoring - if (! dest_vnode->assigned) continue; + if (! dest_vnode->assigned) { + //cerr << "Skipping link unassignment, because " << dest_vnode->name + //<< " is unassigned " << endl; + continue; + } else { + //cerr << "Unassigning, " << dest_vnode->name << " is assigned " << endl; + } // Find the pnode on the ther end of the link, and unscore it! pvertex dest_pv = dest_vnode->assignment; @@ -305,7 +334,8 @@ void remove_node(vvertex vv) /* * Adjust scores for the pnode */ - pnode->current_load--; + tr->current_load--; + pnode->total_load--; #ifdef LOAD_BALANCE // Use this tricky formula to score based on how 'full' the pnode is, so that // we prefer to equally fill the minimum number of pnodes @@ -313,18 +343,19 @@ void remove_node(vvertex vv) SADD(SCORE_PNODE * (powf(1+ pnode->current_load * 1.0/pnode->max_load,2))); #endif - if (pnode->current_load == 0) { + if (pnode->total_load == 0) { // If the pnode is now free, we need to do some cleanup SDEBUG(cerr << " releasing pnode" << endl); SSUB(SCORE_PNODE); - pnode->typed=false; - } else if (pnode->current_load >= pnode->max_load) { + pnode->remove_current_type(); + } else if (tr->current_load >= tr->max_load) { // If the pnode is still over its max load, reduce the penalty to adjust // for the new load. // XXX - This shouldn't ever happen, right? I don't think we can ever // over-assign to a pnode. - SDEBUG(cerr << " reducing penalty, new load=" << pnode->current_load << - " (>= " << pnode->max_load << ")" << endl); + SDEBUG(cerr << " reducing penalty, new load=" << + pnode->current_type_record->current_load << + " (>= " << pnode->current_type_record->max_load << ")" << endl); SSUB(SCORE_PNODE_PENALTY); vinfo.pnode_load--; violated--; @@ -347,14 +378,6 @@ void remove_node(vvertex vv) violated -= fd_violated; vinfo.desires -= fd_violated; - /* - * If we just unassigned a LAN node, we can delete the pnode entirely - */ - if (vnode->type.compare("lan") == 0) { - SDEBUG(cerr << "Deleting lan node." << endl); - delete_lan_node(pv); - } - SDEBUG(cerr << " new score = " << score << " new violated = " << violated << endl); } @@ -453,53 +476,76 @@ int add_node(vvertex vv,pvertex pv, bool deterministic) SDEBUG(cerr << " vnode type = " << vnode->type << endl); /* - * Set up pnode + * Handle types - first, check to see if the node is capable of taking on the + * vnode's type. If it can with a static type, just do the bookkeeping for + * that static type. Otherwise convert the node to the correct dynamic type, + * failing if we can't for some reason (ie. it already has another type. */ + tb_pnode::type_record *tr; + tb_pnode::types_map::iterator mit = pnode->types.find(vnode->type); - // Figure out the pnode's type - if (!pnode->typed) { - // If this pnode has no type yet, give it one - SDEBUG(cerr << " virgin pnode" << endl); - SDEBUG(cerr << " vtype = " << vnode->type << endl); - - // Remove check assuming at higher level? - // Remove higher level checks? - pnode->max_load=0; - if (pnode->types.find(vnode->type) != pnode->types.end()) { - pnode->max_load = pnode->types[vnode->type]; - } - if (pnode->max_load == 0) { - // didn't find a type - SDEBUG(cerr << " no matching type" << endl); + if (mit == pnode->types.end()) { + // This pnode can't take on the vnode's type - we normally shouldn't get + // here, due to the way we pick pnodes + return 1; + } + + tr = mit->second; + if (tr->is_static) { + // XXX: Scoring??? + if (tr->current_load < tr->max_load) { + } else { return 1; } - - pnode->current_type=vnode->type; - pnode->typed=true; - - SDEBUG(cerr << " matching type found (" <current_type << - ", max = " << pnode->max_load << ")" << endl); } else { - // The pnode already has a type, let's just make sure it's compatible - SDEBUG(cerr << " pnode already has type" << endl); - if (pnode->current_type != vnode->type) { - SDEBUG(cerr << " incompatible types" << endl); - return 1; - } else { - SDEBUG(cerr << " compatible types" << endl); - if (pnode->current_load == pnode->max_load) { - /* XXX - We could ignore this check and let the code - at the end of the routine penalize for going over - load. Failing here seems to work better though. */ - - // XXX is this a bug? do we need to revert the pnode/vnode to - // it's initial state. - SDEBUG(cerr << " node is full" << endl); - cerr << " node is full" << endl; + // Figure out the pnode's type + if (!pnode->typed) { + // If this pnode has no type yet, give it one + SDEBUG(cerr << " virgin pnode" << endl); + SDEBUG(cerr << " vtype = " << vnode->type << endl); + + // Remove check assuming at higher level? + // Remove higher level checks? + if (!pnode->set_current_type(vnode->type)) { + // pnode->max_load=0; + // if (pnode->types.find(vnode->type) != pnode->types.end()) { + // pnode->max_load = pnode->types[vnode->type]; + // } + //if (pnode->max_load == 0) { + // didn't find a type + SDEBUG(cerr << " no matching type" << endl); + //cerr << "Failed due to bad type!" << endl; return 1; } + + // pnode->current_type=vnode->type; + // pnode->typed=true; + + SDEBUG(cerr << " matching type found (" << pnode->current_type << + ", max = " << pnode->current_type_record->max_load << ")" << endl); + } else { + // The pnode already has a type, let's just make sure it's compatible + SDEBUG(cerr << " pnode already has type" << endl); + if (pnode->current_type != vnode->type) { + SDEBUG(cerr << " incompatible types" << endl); + return 1; + } else { + SDEBUG(cerr << " compatible types" << endl); + if (pnode->current_type_record->current_load + == pnode->current_type_record->max_load) { + /* XXX - We could ignore this check and let the code + at the end of the routine penalize for going over + load. Failing here seems to work better though. */ + + // XXX is this a bug? do we need to revert the pnode/vnode to + // it's initial state. + SDEBUG(cerr << " node is full" << endl); + cerr << " node is full" << endl; + return 1; + } + } + } } - } #ifdef PENALIZE_UNUSED_INTERFACES pnode->used_interfaces = 0; @@ -657,9 +703,7 @@ int add_node(vvertex vv,pvertex pv, bool deterministic) // check for no link if (resolution_index == 0) { - //cerr << "No resolutions at all" << endl; - SDEBUG(cerr << " Could not find any resolutions. Trying delay." << - endl); + SDEBUG(cerr << " Could not find any resolutions." << endl); SADD(SCORE_NO_CONNECTION); vlink->no_connection=true; @@ -667,7 +711,6 @@ int add_node(vvertex vv,pvertex pv, bool deterministic) vlink->link_info.type = tb_link_info::LINK_UNKNOWN; violated++; } else { - //cerr << "Some resolutions" << endl; // Check to see if we are fixing a violation if (vlink->no_connection) { SDEBUG(cerr << " Fixing previous violations." << endl); @@ -724,20 +767,12 @@ int add_node(vvertex vv,pvertex pv, bool deterministic) score_link_info(*vedge_it,pnode,dest_pnode); } } -#ifdef AUTO_MIGRATE - if (dest_vnode->type.compare("lan") == 0) { - //cout << "Auto-migrating LAN " << dest_vnode->name << " (because of " - // << vnode->name << ")" << endl; - remove_node(dest_vv); - pvertex lanpv = make_lan_node(dest_vv); - add_node(dest_vv,lanpv,false); - } -#endif } } // finish setting up pnode - pnode->current_load++; + tr->current_load++; + pnode->total_load++; #ifdef PENALIZE_UNUSED_INTERFACES assert(pnode->used_interfaces <= pnode->total_interfaces); @@ -746,16 +781,16 @@ int add_node(vvertex vv,pvertex pv, bool deterministic) vnode->assignment = pv; vnode->assigned = true; - if (pnode->current_load > pnode->max_load) { - SDEBUG(cerr << " load to high - penalty (" << pnode->current_load << - ")" << endl); + if (tr->current_load > tr->max_load) { + SDEBUG(cerr << " load too high - penalty (" << + pnode->current_type_record->current_load << ")" << endl); SADD(SCORE_PNODE_PENALTY); vinfo.pnode_load++; violated++; } else { SDEBUG(cerr << " load is fine" << endl); } - if (pnode->current_load == 1) { + if (pnode->total_load == 1) { SDEBUG(cerr << " new pnode" << endl); SADD(SCORE_PNODE); #ifdef LOAD_BALANCE @@ -802,8 +837,10 @@ int add_node(vvertex vv,pvertex pv, bool deterministic) SDEBUG(cerr << " assignment=" << vnode->assignment << endl); SDEBUG(cerr << " new score=" << score << " new violated=" << violated << endl); - if (pnode->my_class) { - pclass_set(vnode,pnode); + if (!tr->is_static) { + if (pnode->my_class) { + pclass_set(vnode,pnode); + } } return 0; @@ -971,7 +1008,7 @@ void score_link(pedge pe,vedge ve,tb_pnode *src_pnode, tb_pnode *dst_pnode) #endif if (plink->type == tb_plink::PLINK_NORMAL) { - // need too account for three things here, the possiblity of a new plink + // need to account for three things here, the possiblity of a new plink // the user of a new emulated link, and a possible violation. if (vlink->emulated) { plink->emulated++; @@ -994,8 +1031,7 @@ void score_link(pedge pe,vedge ve,tb_pnode *src_pnode, tb_pnode *dst_pnode) } #ifdef FIX_SHARED_INTERFACES if ((! vlink->emulated) && (plink->nonemulated > 1)) { - //cerr << "Penalizing overused link" << endl; - assert(false); + //assert(false); SADD(SCORE_DIRECT_LINK_PENALTY); vinfo.link_users++; violated++; @@ -1250,6 +1286,10 @@ double fd_score(tb_vnode *vnode,tb_pnode *pnode,int &fd_violated) * connect the LAN node to. Specifically, it connects it to the switch * which will maximize the number of intra (rather than inter) links for * assigned adjancent nodes of vv. + * + * NOTE: This function is deprecated, since there are no longer special + * physical LAN nodes. + * */ pvertex make_lan_node(vvertex vv) { @@ -1295,10 +1335,8 @@ pvertex make_lan_node(vvertex vv) pvertex pv = add_vertex(PG); tb_pnode *p = new tb_pnode(vnode->name); put(pvertex_pmap,pv,p); - p->typed = true; - p->current_type = "lan"; - p->max_load = 1; - p->types["lan"] = 1; + p->types["lan"] = new tb_pnode::type_record(1,false); + p->set_current_type("lan"); // If the below is false then we have an orphaned lan node which will // quickly be destroyed when add_node fails. @@ -1333,6 +1371,10 @@ pvertex make_lan_node(vvertex vv) /* delete_lan_node(pvertex pv) * Removes the physical lan node and the physical lan link. Assumes that * nothing is assigned to it. + * + * NOTE: This function is deprecated, since there are no longer special + * physical LAN nodes. + * */ void delete_lan_node(pvertex pv) { diff --git a/assign/solution.cc b/assign/solution.cc index 4b10586f2..851645b6c 100644 --- a/assign/solution.cc +++ b/assign/solution.cc @@ -44,8 +44,10 @@ void print_solution() if (vlink->link_info.type == tb_link_info::LINK_DIRECT) { // Direct link - just need the source and destination tb_plink *p = get(pedge_pmap,vlink->link_info.plinks.front()); + tb_plink *p2 = get(pedge_pmap,vlink->link_info.plinks.back()); cout << " direct " << p->name << " (" << - p->srcmac << "," << p->dstmac << ")"; + p->srcmac << "," << p->dstmac << ") " << + p2->name << " (" << p2->srcmac << "," << p2->dstmac << ")"; } else if (vlink->link_info.type == tb_link_info::LINK_INTRASWITCH) { // Intraswitch link - need to grab the plinks to both nodes tb_plink *p = get(pedge_pmap,vlink->link_info.plinks.front()); diff --git a/tbsetup/assign_wrapper.in b/tbsetup/assign_wrapper.in index 59bda89b4..78e177e9b 100644 --- a/tbsetup/assign_wrapper.in +++ b/tbsetup/assign_wrapper.in @@ -1154,8 +1154,9 @@ while (1) { last SWITCH1; }; /^direct$/ && do { - fatal(65, "*** $0:\n". - " Unsupported link type: direct.\n"); + ($vlink,$rawA,$rawB) = @info[0,3,5]; + #fatal(65, "*** $0:\n". + # " Unsupported link type: direct.\n"); }; /^trivial$/ && do { # we don't have plinks for trivial links diff --git a/tbsetup/ptopgen.in b/tbsetup/ptopgen.in index 4e6cf8f14..3c5674d0c 100644 --- a/tbsetup/ptopgen.in +++ b/tbsetup/ptopgen.in @@ -90,7 +90,7 @@ $nodetypes{"switch"} = 0; # Print switches if (defined($switchtouse)) { - print "node $switchtouse switch:1\n"; + print "node $switchtouse switch:1 *lan:*\n"; $switches{$switchtouse} = 1; } else { @@ -98,7 +98,7 @@ else { DBQueryFatal("select node_id from nodes where role = \"testswitch\""); while (($switch) = $result->fetchrow_array) { - print "node $switch switch:1\n"; + print "node $switch switch:1 *lan:*\n"; $switches{$switch} = 1; } $result->finish; -- GitLab