Commit 3a345f8e authored by Robert Ricci's avatar Robert Ricci

Several changes, mostly targeted at jail and simulation-type

topologies.

First, added a new 'summary' of the solution, when the '-u' option is
given. It's a view of things from the physical side - prints out how
many vnodes were mapped to each pnode, as well as how much bandwidth
(trivial and non-trivial) was used on each node. For 'normal' nodes,
we also print out all links used and how much bandwidth was used on
each of them. For switches, we print only inter-switch links. This is
amazingly helpful in getting an intuitive feel for how well assign is
doing.

Added a SIGINFO handler for the impatient (like me) to see things such
as the current temperature, and current and best scores, while assign
is running.

Fixed a bug in which emulated links could get over-subscribed, as well
as a few other misc. bugfixes.

Changed the way assign goes through the list of a node's pclasses in
random order - there were problems with the old way in which you could
end up with a situation in which some pnodes were chosen with a much
higher probability than others. Now, rather than treating the list as
a ring and starting at a random place, we make a randomly-ordered list
of the pclasseses, and go through it from start to finish.

Did some work on dynamic pclasses so that we adjust the estimate of
the neighborhood size to account for disabled pclasses (ie. pnodes
that have nothing mapped to them yet.)

Changed the way that find_link_to_switch() decides on the best link to
use - the old method was doing very poorly at bin-packing emulated
links into plinks. I now use a simple first-fit algorithm. This made a
pretty big difference. I may try some other fast bin-packing
approximation algorithm, but my main fear is that all of the good ones
(such as the 'sort from largest to smallest, then do first-fit'
algorithm) may require re-mapping other links. This might be slow,
and/or it might make it difficult, if not impossible, to keep
add_node() and remove_node() symmetric.

Combinded direct_link() and find_link_to_switch() into
find_best_link(), since they really do the same thing.

Standardized on std::random() to get random numbers - previosuly, some
calls were using std::rand().

The big one: I added a find_pnode_connected() function that finds a
random pnode that one of the vnode's neighbors in the virtual graph is
assigned to. Then, with a random probability (given with the -c option
on the command line), we try that function to find a pnode first (if
it fails, we still call find_pnode() ). Of course, this is only really
applicable when you have a reasonable degree of vnode-to-pnode
multiplexing. In the test case I'm using, this managed to get 3x as
much bandwidth into trivial links as just using find_pnode().
parent 2f3bbf45
......@@ -111,6 +111,62 @@ inline bool pnode_is_match(tb_vnode *vn, tb_pnode *pn) {
return matched;
}
/*
* Finds a pnode which:
* 1) One of the vnode's neighbors is mapped to
* 2) Satisifies the usual pnode mapping constraints
* 3) The vnode is not already mapped to
*/
tb_pnode *find_pnode_connected(vvertex vv, tb_vnode *vn) {
//cerr << "find_pnode_connected(" << vn->name << ") called" << endl;
// We make a list of all neighboring vnodes so that we can go through
// them in random order
vector<vedge> visit_order(out_degree(vv,VG));
voedge_iterator vedge_it,end_vedge_it;
tie(vedge_it,end_vedge_it) = out_edges(vv,VG);
for (int i = 0; vedge_it != end_vedge_it; vedge_it++, i++) {
visit_order[i] = *vedge_it;
}
for (int i = 0; i < visit_order.size(); i++) {
int i1 = std::random() % visit_order.size();
int i2 = std::random() % visit_order.size();
vedge tmp = visit_order[i1];
visit_order[i1] = visit_order[i2];
visit_order[i2] = tmp;
}
for (int i = 0; i < visit_order.size(); i++) {
vvertex neighbor_vv = target(visit_order[i],VG);
tb_vnode *neighbor_vn = get(vvertex_pmap,neighbor_vv);
//cerr << " trying " << neighbor_vn->name << endl;
// Skip any that aren't assigned
if (!neighbor_vn->assigned) {
//cerr << " not assigned" << endl;
continue;
}
// Skip any that are assigned to the same pnode we are
if (neighbor_vn->assignment == vn->assignment) {
//cerr << " same assignment" << endl;
continue;
}
// Check to make sure that our vn can map to the neibor's assigment
tb_pnode *neighbor_pnode = get(pvertex_pmap,neighbor_vn->assignment);
//cerr << " neighbor on " << neighbor_pnode->name << endl;
if (pnode_is_match(vn,neighbor_pnode)) {
//cerr << " good" << endl;
//cerr << " worked" << endl;
return neighbor_pnode;
}
//cerr << " doesn't match" << endl;
}
//cerr << " failed" << endl;
return NULL;
}
tb_pnode *find_pnode(tb_vnode *vn)
{
#ifdef PER_VNODE_TT
......@@ -133,8 +189,8 @@ tb_pnode *find_pnode(tb_vnode *vn)
traversal_order[i] = i;
}
for (int i = 0; i < num_types; i++) {
int i1 = std::rand() % num_types;
int i2 = std::rand() % num_types;
int i1 = std::random() % num_types;
int i2 = std::random() % num_types;
int tmp = traversal_order[i1];
traversal_order[i1] = traversal_order[i2];
traversal_order[i2] = tmp;
......@@ -193,10 +249,13 @@ tb_pnode *find_pnode(tb_vnode *vn)
return NULL;
}
// We put the temperature outside the function so that external stuff, like
// status_report in assign.cc, can see it.
double temp;
/* When this is finished the state will reflect the best solution found. */
void anneal(bool scoring_selftest, double scale_neighborhood,
double *initial_temperature)
double *initial_temperature, double use_connected_pnode_find)
{
cout << "Annealing." << endl;
......@@ -229,7 +288,7 @@ void anneal(bool scoring_selftest, double scale_neighborhood,
int bestviolated;
int num_fixed=0;
double meltedtemp;
double temp = init_temp;
temp = init_temp;
double deltatemp, deltaavg;
// Priority queue of unassigned virtual nodes. Basically a fancy way
......@@ -443,8 +502,8 @@ void anneal(bool scoring_selftest, double scale_neighborhood,
// Adjust the number of transitions we're going to do based on the number
// of pclasses that are actually 'in play'
int transitions = neighborsize *
(count_enabled_pclasses() *1.0 / pclasses.size());
int transitions = (int)(neighborsize *
(count_enabled_pclasses() *1.0 / pclasses.size()));
assert(transitions <= neighborsize);
if (melting) {
......@@ -511,7 +570,16 @@ void anneal(bool scoring_selftest, double scale_neighborhood,
vn->vclass->dominant << endl;
#endif
}
tb_pnode *newpnode = find_pnode(vn);
// Actually find a pnode
tb_pnode *newpnode = NULL;
if ((use_connected_pnode_find != 0)
&& ((std::random() % 1000) < (use_connected_pnode_find * 1000))) {
newpnode = find_pnode_connected(vv,vn);
}
if (newpnode == NULL) {
newpnode = find_pnode(vn);
}
#ifndef FREE_IMMEDIATELY
if (oldassigned) {
RDEBUG(cout << "removing: !lan, oldassigned" << endl;)
......@@ -536,7 +604,7 @@ void anneal(bool scoring_selftest, double scale_neighborhood,
pclass_vector *acceptable_types = tt.second;
// Find a node to kick out
bool foundnode = false;
int offi = std::rand();
int offi = std::random();
int index;
for (int i = 0; i < size; i++) {
index = (i + offi) % size;
......@@ -553,14 +621,16 @@ void anneal(bool scoring_selftest, double scale_neighborhood,
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();
tb_pclass::tb_pnodeset::iterator it =
(*acceptable_types)[index]->used_members[vn->type]->begin();
int j = std::random() %
(*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();
int k = std::random() % (*it)->assigned_nodes.size();
while (k > 0) {
it2++;
k--;
......
......@@ -74,6 +74,6 @@ tb_pnode *find_pnode(tb_vnode *vn);
/* The big guy! */
void anneal(bool scoring_selftest, double scale_neighborhood,
double *initial_temperature);
double *initial_temperature, double use_connected_pnode_find);
#endif
......@@ -117,6 +117,12 @@ bool greedy_link_assignment = false;
bool no_melting = false;
double initial_temperature = 0.0f;
// Print out a summary of the solution in addition to the solution itself
bool print_summary = false;
// Use the 'connected' find algorithm
double use_connected_pnode_find = 0.0f;
// XXX - shouldn't be in this file
double absbest;
int absbestviolated, iters, iters_to_best;
......@@ -325,6 +331,9 @@ void print_help()
cout << " -o - Allow overloaded pnodes to be considered." << endl;
cout << " -t <float> - Start the temperature at <float> instead of melting."
<< endl;
cout << " -u - Print a summary of the solution." << endl;
cout << " -c <float> - Use the 'connected' pnode finding algorithm " <<
"<float>% of the time." << endl;
exit(EXIT_UNRETRYABLE);
}
......@@ -586,6 +595,14 @@ void exit_unretryable(int signal) {
_exit(EXIT_UNRETRYABLE);
}
// Singal handler - add a way for a user to get some status information
extern double temp;
void status_report(int signal) {
cout << "I: " << iters << " T: " << temp << " S: " << get_score() << " V: "
<< violated << " (Best S: " << absbest << " V:" << absbestviolated << ")"
<< endl;
}
int main(int argc,char **argv)
{
int seed = 0;
......@@ -596,7 +613,7 @@ int main(int argc,char **argv)
char ch;
timelimit = 0.0;
timetarget = 0.0;
while ((ch = getopt(argc,argv,"s:v:l:t:rpPTdH:og")) != -1) {
while ((ch = getopt(argc,argv,"s:v:l:t:rpPTdH:oguc:")) != -1) {
switch (ch) {
case 's':
if (sscanf(optarg,"%d",&seed) != 1) {
......@@ -646,6 +663,13 @@ int main(int argc,char **argv)
print_help();
}
no_melting = true; break;
case 'u':
print_summary = true; break;
case 'c':
if (sscanf(optarg,"%lf",&use_connected_pnode_find) != 1) {
print_help();
}
break;
default:
print_help();
}
......@@ -680,6 +704,13 @@ int main(int argc,char **argv)
sigaction(SIGUSR1,&action,NULL);
sigaction(SIGINT,&action,NULL);
// Set up a signal handler for control+T
struct sigaction action2;
action2.sa_handler = status_report;
sigemptyset(&action2.sa_mask);
action2.sa_flags = 0;
sigaction(SIGINFO,&action2,NULL);
// Convert options to the common.h parameters.
parse_options(argv, options, noptions);
#ifdef SCORE_DEBUG
......@@ -751,7 +782,8 @@ int main(int argc,char **argv)
}
timestart = used_time();
anneal(scoring_selftest, scale_neighborhood, initial_temperature_pointer);
anneal(scoring_selftest, scale_neighborhood, initial_temperature_pointer,
use_connected_pnode_find);
timeend = used_time();
#ifdef GNUPLOT_OUTPUT
......@@ -777,6 +809,10 @@ int main(int argc,char **argv)
print_solution();
if (print_summary) {
print_solution_summary();
}
if (viz_prefix.size() != 0) {
crope aviz = viz_prefix + "_solution.viz";
ofstream afile;
......
......@@ -75,8 +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),
my_own_class(NULL), assigned_nodes(),
total_bandwidth(0), nontrivial_bw_used(0),
my_class(NULL), my_own_class(NULL), assigned_nodes(),
trivial_bw(0), trivial_bw_used(0), subnode_of(NULL),
subnode_of_name(""), has_subnode(false),
unique(false), is_switch(false) {;}
......@@ -128,6 +128,8 @@ public:
int total_interfaces; // total number of links leaving the node
int used_interfaces; // number of links that are currently in use
int total_bandwidth; // total bandwidth of all this nodes' links
int nontrivial_bw_used; // amount of non-trivial bandwidth in use on
// this node - for debugging only
tb_pclass *my_class; // the pclass this node belongs to
......
This diff is collapsed.
......@@ -97,6 +97,74 @@ void print_solution()
cout << "End solution" << endl;
}
/*
* Print out a summary of the solution - mainly, we print out information from
* the physical perspective. For example, now many vnodes are assigned to each
* pnode, and how much total bandwidth each pnode is handling.
*/
void print_solution_summary()
{
// First, print the number of vnodes on each pnode, and the total number of
// pnodes used
cout << "Summary:" << endl;
// Go through all physical nodes
int pnodes_used = 0;
pvertex_iterator pit, pendit;
tie(pit, pendit) = vertices(PG);
for (;pit != pendit; pit++) {
tb_pnode *pnode = get(pvertex_pmap,*pit);
// We're going to treat switches specially
bool isswitch = false;
if (pnode->is_switch) {
isswitch = true;
}
// Only print pnodes we are using, or switches we are going through
if ((pnode->total_load > 0) ||
(isswitch && (pnode->switch_used_links > 0))) {
// What we really want to know is how many PCs, etc. were used, so don't
// count switches
if (!pnode->is_switch) {
pnodes_used++;
}
// Print name, number of vnodes, and some bandwidth numbers
cout << pnode->name << " " << pnode->total_load << " vnodes, " <<
pnode->nontrivial_bw_used << " nontrivial BW, " <<
pnode->trivial_bw_used << " trivial BW" << endl;
// Go through all links on this pnode
poedge_iterator pedge_it,end_pedge_it;
tie(pedge_it,end_pedge_it) = out_edges(*pit,PG);
for (;pedge_it!=end_pedge_it;++pedge_it) {
tb_plink *plink = get(pedge_pmap,*pedge_it);
tb_pnode *link_dst = get(pvertex_pmap,source(*pedge_it,PG));
if (link_dst == pnode) {
link_dst = get(pvertex_pmap,target(*pedge_it,PG));
}
// Ignore links we aren't using
if (plink->bw_used == 0) {
continue;
}
// For switches, only print inter-switch links
if (isswitch && (!link_dst->is_switch)) {
continue;
}
cout << " " << plink->bw_used << " " << plink->name << endl;
}
}
}
cout << "Total physical nodes used: " << pnodes_used << endl;
cout << "End summary" << endl;
}
void pvertex_writer::operator()(ostream &out,const pvertex &p) const {
tb_pnode *pnode = get(pvertex_pmap,p);
out << "[label=\"" << pnode->name << "\"";
......
......@@ -34,10 +34,14 @@ using namespace boost;
/* From assign.cc */
extern node_map absassignment;
extern assigned_map absassigned;
extern tb_pgraph PG;
/* Print a solution */
void print_solution();
/* Print a summary of the solution */
void print_solution_summary();
/* Check to see if two scores are, for all intents and purposes, the same */
bool compare_scores(double score1, double score2);
......
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