Commit 83cfa8ec authored by Robert Ricci's avatar Robert Ricci

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...
parent 84ff51eb
......@@ -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) {
......
......@@ -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
......@@ -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);
......
......@@ -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
-----
......
......@@ -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;(i<parsed_line.size()) && (parsed_line[i].compare("-")) ;++i) {
......@@ -103,7 +113,7 @@ int parse_ptop(tb_pgraph &PG, tb_sgraph &SG, istream& i)
p->features[feature] = gcost;
}
/*
* Parse any other node optios or flags
* Parse any other node options or flags
*/