Commit fbcd6752 authored by Robert Ricci's avatar Robert Ricci

Give assign the ability to make dynamic pclasses. Here's how it works:

After building the set of pclasses normally, we make another pass
through the vnodes. The goal to create a pclass for each individual
node. We disable the node's 'own' pclass to begin with. Then, the
_first_ time it gets a vnode mapped to it, we remove it from the
'regular' pclass it belongs to, and enable it's own pclass. Then, if
it goes empty, we put it back in its regular pclass and disable it's
own.

The point of this is to replace -p for use with multiplexed nodes.
Instead of disabling pclasses altogheter, which has serious
performance implications, we can instead be smart about which pnodes
remain equivalent (because nothing's been mapped to them), and which
are now different. The result is that on my test topoloy, the time to
get a good mapping has gone from over 3 minutes to about 6 seconds.

This feature is enabled with the -d option, and the -P option is
pretty much mandatory when using it, since it greatly exacerbates the
problem of cruft in the ptop file.

This satisfies #14 from the todo file:
14.  do dynamic pclasses

Also bumped up the minimum neighborhood size from 500 to 1000. In some
tests I was doing, this resulted in better solutions.
parent d691592f
......@@ -48,18 +48,6 @@ 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
......@@ -71,6 +59,19 @@ tb_pnode *find_pnode(tb_vnode *vn)
pclass_vector *acceptable_types = tt.second;
tb_pnode *newpnode;
/*
int enabled_pclasses = 0;
for (int i = 0; i < num_types; i++) {
if ((*acceptable_types)[i]->disabled) {
continue;
}
enabled_pclasses++;
}
cout << "Looking for a pnode for " << vn->name << " - there are " <<
enabled_pclasses << " to choose from (" << num_types << " total)" << endl;
assert(num_types == enabled_pclasses);
*/
int i = std::random()%num_types;
int first = i;
......@@ -97,6 +98,10 @@ REDO_SEARCH:
(*acceptable_types)[i]->members.end()) {
continue;
}
if ((*acceptable_types)[i]->disabled) {
i = std::rand()%num_types;
continue;
}
#endif
list<tb_pnode*>::iterator it = (*acceptable_types)[i]->members[vn->type]->L.begin();
#ifdef LOAD_BALANCE
......@@ -898,6 +903,15 @@ NOTQUITEDONE:
endl << " This indicates a bug - contact the operators" <<
endl << " (initial score: " << initial_score <<
", current score: " << get_score() << ")" << endl;
// One source of this can be pclasses that are still used - check for
// those
pclass_list::iterator pit = pclasses.begin();
for (;pit != pclasses.end();pit++) {
if ((*pit)->used_members != 0) {
cerr << (*pit)->name << " is " << (*pit)->used_members
<< "% used" << endl;
}
}
}
tie(vvertex_it,end_vvertex_it) = vertices(VG);
for (;vvertex_it!=end_vvertex_it;++vvertex_it) {
......
......@@ -30,6 +30,7 @@ using namespace boost;
#include "maps.h"
#include "score.h"
#include "pclass.h"
#include "solution.h"
// Some defaults for #defines
#ifndef NO_REVERT
......@@ -50,15 +51,6 @@ using namespace boost;
#define PHYSICAL(x) 0
#endif
/*
* Information returned by the annealing process - note that this does not
* include the solution, which is still global for now.
*/
struct annealing_results {
int violated; /* Number of violations - XXX make vinfo non-global too */
};
/*
* Globals - XXX made non-global!
*/
......
......@@ -95,6 +95,9 @@ bool disable_pclasses = false;
// Whether or not assign should prune out pclasses that it knows can
// never be used
bool prune_pclasses = false;
// Whether or not we should use the experimental support for dynamic pclasses
bool dynamic_pclasses = false;
// XXX - shouldn't be in this file
double absbest;
......@@ -295,6 +298,7 @@ void print_help()
endl;
cerr << " -r - Don't allow trivial links." << endl;
cerr << " -p - Disable pclasses." << endl;
cerr << " -d - Enable dynamic pclasses." << endl;
#ifdef PER_VNODE_TT
cerr << " -P - Prune unusable pclasses." << endl;
#endif
......@@ -530,7 +534,7 @@ int main(int argc,char **argv)
char ch;
timelimit = 0.0;
timetarget = 0.0;
while ((ch = getopt(argc,argv,"s:v:l:t:rpP")) != -1) {
while ((ch = getopt(argc,argv,"s:v:l:t:rpPTd")) != -1) {
switch (ch) {
case 's':
if (sscanf(optarg,"%d",&seed) != 1) {
......@@ -564,6 +568,8 @@ int main(int argc,char **argv)
#endif
case 'T':
scoring_selftest = true; break;
case 'd':
dynamic_pclasses = true; break;
default:
print_help();
}
......@@ -608,7 +614,7 @@ int main(int argc,char **argv)
calculate_switch_MST();
cout << "Generating physical equivalence classes:";
generate_pclasses(PG,disable_pclasses);
generate_pclasses(PG,disable_pclasses,dynamic_pclasses);
cout << pclasses.size() << endl;
#ifdef PCLASS_DEBUG
......
......@@ -95,13 +95,4 @@ This is the current TODO list for assign (not in order by priority):
up a mapping for an unassignable vnode. Doesn't seem to hurt anything, so is
low priority
14. do dynamic pclasses
pclasses are computed when assign starts. Since a partially-filled
multiplexable pnode is no longer really equivalent to the others in its
pclass, we have to turn off pclasses entirely when using them. If we were to
allow nodes to dynamically leave and enter pclasses, then we wouldn't have
to do this, and runtimes and/or solutions might get better. Probably will be
pretty hard, and/or requires a lot of bookkeeping.
(next free #:15)
......@@ -35,6 +35,8 @@ using namespace boost;
// fill out the pclass structure. Then two routines pclass_set, and
// pclass_unset are used to maintaing the structure during annealing.
// Used to catch cumulative floating point errors
static double ITTY_BITTY = 0.00000001;
extern pnode_pvertex_map pnode2vertex;
......@@ -121,7 +123,8 @@ int pclass_equiv(tb_pgraph &PG, tb_pnode *a,tb_pnode *b)
list of all equivalence classes) and type_table (and table of
physical type to list of classes that can satisfy that type) are
set by this routine. */
int generate_pclasses(tb_pgraph &PG, bool pclass_for_each_pnode) {
int generate_pclasses(tb_pgraph &PG, bool pclass_for_each_pnode,
bool dynamic_pclasses) {
typedef hash_map<tb_pclass*,tb_pnode*,hashptr<tb_pclass*> > pclass_pnode_map;
typedef hash_map<crope,pclass_list*> name_pclass_list_map;
......@@ -162,6 +165,38 @@ int generate_pclasses(tb_pgraph &PG, bool pclass_for_each_pnode) {
}
}
if (dynamic_pclasses) {
// Make a pclass for each node, which starts out disabled. It will get
// enabled later when something is assigned to the pnode
pvertex_iterator vit,vendit;
tie(vit,vendit) = vertices(PG);
for (;vit != vendit;++vit) {
tb_pnode *pnode = get(pvertex_pmap,*vit);
// No point in doing this if the pnode is either: already in a pclass of
// size one, or can only have a single vnode mapped to it anyway
if (pnode->my_class->size == 1) {
continue;
}
bool multiplexed = false;
tb_pnode::types_map::iterator it = pnode->types.begin();
for (; it != pnode->types.end(); it++) {
if ((*it).second->max_load > 1) {
multiplexed = true;
break;
}
}
if (!multiplexed) {
continue;
}
tb_pclass *n = new tb_pclass;
pclasses.push_back(n);
n->name = pnode->name + "-own";
n->add_member(pnode);
n->disabled = true;
pnode->my_own_class = n;
}
}
name_pclass_list_map pre_type_table;
pclass_list::iterator it;
......@@ -212,6 +247,38 @@ int tb_pclass::add_member(tb_pnode *p)
return 0;
}
// Debugging function to check the invariant that a node is either in its
// dynamic pclass, a 'normal' pclass, but not both
void assert_own_class_invariant(tb_pnode *p) {
cerr << "class_invariant: " << p->name << endl;
bool own_class = false;
bool other_class = false;
pclass_list::iterator pit = pclasses.begin();
for (;pit != pclasses.end(); pit++) {
tb_pclass::pclass_members_map::iterator mit = (*pit)->members.begin();
for (;mit != (*pit)->members.end(); mit++) {
if (mit->second->exists(p)) {
if (*pit == p->my_own_class) {
cerr << "In own pclass (" << (*pit)->name << "," << (*pit)->disabled
<< ")" << endl;
if (!(*pit)->disabled) {
own_class = true;
}
} else {
cerr << "In pclass " << (*pit)->name << " type " << mit->first <<
" size " << mit->second->size() << endl;
other_class = true;
}
}
}
}
if (own_class && other_class) {
cerr << "Uh oh, in own and other class!" << endl;
pclass_debug();
abort();
}
}
// should be called after add_node
int pclass_set(tb_vnode *v,tb_pnode *p)
{
......@@ -222,12 +289,13 @@ 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->types[p->current_type]->current_load ==
p->types[p->current_type]->max_load) {
if ((p->current_type_record->current_load ==
p->current_type_record->max_load) ||
p->my_own_class) {
(*dit).second->remove(p);
//#ifdef SMART_UNMAP
// c->used_members[(*dit).first]->push_back(p);
//#endif
if (p->my_own_class) {
p->my_own_class->disabled = false;
}
}
} else {
// If it's not in the list then this fails quietly.
......@@ -246,9 +314,9 @@ int pclass_set(tb_vnode *v,tb_pnode *p)
#endif
}
c->used_members++;
c->used += 1.0/(p->current_type_record->max_load);
//assert_own_class_invariant(p);
return 0;
}
......@@ -257,35 +325,29 @@ int pclass_unset(tb_pnode *p)
// add pnode to all lists in equivalence class.
tb_pclass *c = p->my_class;
//cout << "Unassigning " << p->name << ": ";
tb_pclass::pclass_members_map::iterator dit;
for (dit=c->members.begin();dit!=c->members.end();++dit) {
//cout << " Type " << dit->first << ": ";
if ((*dit).first == p->current_type) {
// If it's not in the list then we need to add it to the back if it's
// 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->types[p->current_type]->current_load > 0);
assert(p->current_type_record->current_load > 0);
#ifdef PNODE_ALWAYS_FRONT
(*dit).second->push_front(p);
#else
#ifdef PNODE_SWITCH_LOAD
if (p->types[p->current_type]->current_load == 0) {
if (p->current_type_record->current_load == 0) {
#else
if (p->current_load == 1) {
#endif
//cout << "Pushing back: " << p->current_load << " ";
(*dit).second->push_back(p);
} else {
//cout << "Pushing front: " << p->current_load << " ";
(*dit).second->push_front(p);
}
#endif
}
} else {
//cout << "Pushing back (2) ";
(*dit).second->push_back(p);
}
......@@ -294,10 +356,13 @@ int pclass_unset(tb_pnode *p)
#endif
}
//cout << endl;
if (p->my_own_class) {
p->my_own_class->disabled = true;
}
c->used -= 1.0/(p->current_type_record->max_load);
c->used_members--;
//assert_own_class_invariant(p);
return 0;
}
......@@ -312,7 +377,8 @@ void pclass_debug()
cout << "\n";
cout << "Type Table:\n";
pclass_types::iterator dit;
for (dit=type_table.begin();dit!=type_table.end();++dit) {
extern pclass_types vnode_type_table; // from assign.cc
for (dit=vnode_type_table.begin();dit!=vnode_type_table.end();++dit) {
cout << (*dit).first << ":";
int n = (*dit).second.first;
pclass_vector &A = *((*dit).second.second);
......
......@@ -52,6 +52,10 @@ public:
return (L.empty() ? NULL : L.front());
};
int size() {
return L.size();
};
friend ostream &operator<<(ostream &o, const tb_pnodelist& l)
{
pnode_list::const_iterator lit;
......@@ -64,7 +68,8 @@ public:
class tb_pclass {
public:
tb_pclass() : name(), size(0), used(0), refcount(0) {;}
tb_pclass() : name(), size(0), used_members(0), refcount(0), disabled(false)
{;}
typedef map<crope,tb_pnodelist*> pclass_members_map;
typedef hash_set<tb_pnode*,hashptr<tb_pnode*> > tb_pnodeset;
......@@ -74,12 +79,14 @@ public:
crope name; // purely for debugging
int size;
double used;
int used_members;
pclass_members_map members;
#ifdef SMART_UNMAP
pclass_members_set used_members;
#endif
bool disabled;
// A count of how many nodes can use this pclass
// For use with PRUNE_PCLASSES
int refcount;
......@@ -87,7 +94,7 @@ public:
friend ostream &operator<<(ostream &o, const tb_pclass& p)
{
o << p.name << "(" << &p << ") size=" << p.size <<
" used=" << p.used << "\n";
" used_members=" << p.used_members << " disabled=" << p.disabled << "\n";
pclass_members_map::const_iterator dit;
for (dit=p.members.begin();dit!=p.members.end();++dit) {
o << " " << (*dit).first << ":\n";
......@@ -114,7 +121,8 @@ typedef hash_map<crope,tt_entry> pclass_types;
// Takes two arguments - a physical graph, and a flag indicating whether or not
// each physical node should get its own pclass (effectively disabling
// pclasses)
int generate_pclasses(tb_pgraph &PG, bool pclass_for_each_pnode);
int generate_pclasses(tb_pgraph &PG, bool pclass_for_each_pnode,
bool dynamic_pclasses);
/* The following two routines sets and remove mappings in pclass
datastructures */
......
......@@ -75,7 +75,8 @@ public:
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(),
total_bandwidth(0), my_class(NULL),
my_own_class(NULL), assigned_nodes(),
trivial_bw(0), trivial_bw_used(0) {;}
class type_record {
......@@ -128,6 +129,9 @@ public:
tb_pclass *my_class; // the pclass this node belongs to
tb_pclass *my_own_class; // if using DYNAMIC_PCLASSES, a pointer to the
// node's own class
tb_vnode_set assigned_nodes; // the set of vnodes currently assigned
int trivial_bw; // the maximum amount of trivial bandwidth
......
......@@ -72,6 +72,10 @@ void score_link_endpoints(pedge pe);
#define SSUB(amount) score -= amount
#endif
// For convenience, so we can easily turn on or off one statement
#define SDEBADD(amount) cerr << "SADD: " << #amount << "=" << amount << " from " << score;score+=amount;cerr << " to " << score << endl
#define SDEBSUB(amount) cerr << "SSUB: " << #amount << "=" << amount << " from " << score;score-=amount;cerr << " to " << score << endl
#ifdef SCORE_DEBUG
#define SDEBUG(a) a
#else
......@@ -266,7 +270,8 @@ void remove_node(vvertex vv)
#endif
// pclass
if ((!disable_pclasses) && pnode->my_class && (pnode->my_class->used == 0)) {
if ((!disable_pclasses) && !(tr->is_static) && pnode->my_class
&& (pnode->my_class->used_members == 0)) {
SDEBUG(cerr << " freeing pclass" << endl);
SSUB(SCORE_PCLASS);
}
......@@ -312,11 +317,7 @@ 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) {
//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!
......@@ -507,20 +508,12 @@ int add_node(vvertex vv,pvertex pv, bool deterministic)
// 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 {
......@@ -793,14 +786,8 @@ int add_node(vvertex vv,pvertex pv, bool deterministic)
if (pnode->total_load == 1) {
SDEBUG(cerr << " new pnode" << endl);
SADD(SCORE_PNODE);
#ifdef LOAD_BALANCE
//SADD(SCORE_PNODE); // Yep, twice
#endif
}
#ifdef LOAD_BALANCE
//SSUB(SCORE_PNODE * (1.0 / pnode->max_load));
//SSUB(SCORE_PNODE * (1 + powf(((pnode->current_load-1) * 1.0)/pnode->max_load,2)));
//SADD(SCORE_PNODE * (1 + powf(((pnode->current_load) * 1.0)/pnode->max_load,2)));
SSUB(SCORE_PNODE * (powf(1 + ((pnode->current_load-1) * 1.0)/pnode->max_load,2)));
SADD(SCORE_PNODE * (powf(1 + ((pnode->current_load) * 1.0)/pnode->max_load,2)));
#endif
......@@ -818,7 +805,8 @@ int add_node(vvertex vv,pvertex pv, bool deterministic)
vinfo.desires += fd_violated;
// pclass
if ((!disable_pclasses) && pnode->my_class && (pnode->my_class->used == 0)) {
if ((!disable_pclasses) && (!tr->is_static) && pnode->my_class &&
(pnode->my_class->used_members == 0)) {
SDEBUG(cerr << " new pclass" << endl);
SADD(SCORE_PCLASS);
}
......@@ -1307,7 +1295,6 @@ pvertex make_lan_node(vvertex vv)
tb_vnode *vnode = get(vvertex_pmap,vv);
SDEBUG(cerr << "make_lan_node(" << vnode->name << ")" << endl);
//cerr << "make_lan_node(" << vnode->name << ")" << endl;
// Choose switch
pvertex largest_switch;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment