diff --git a/assign/GNUmakefile.in b/assign/GNUmakefile.in index 62b993671e11609e59bdd2891744a70676a55a19..9bda319379dec101bdec8b1ec76937f26c430dea 100644 --- a/assign/GNUmakefile.in +++ b/assign/GNUmakefile.in @@ -21,7 +21,7 @@ all: assign include $(TESTBED_SRCDIR)/GNUmakerules OBJS=parse_top.o parse_ptop.o assign.o pclass.o vclass.o config.o score.o \ - parser.o solution.o anneal.o + parser.o solution.o anneal.o featuredesire.o LIBS+= -lm LDFLAGS+= -pipe -O3 CXXFLAGS = -pipe -I/usr/local/include -ftemplate-depth-30 diff --git a/assign/anneal.cc b/assign/anneal.cc index 6e4773a577dadff887a3e0a61ef1e1a2427b1847..813a8a1d8408945b4535daf96274dc888205f005 100644 --- a/assign/anneal.cc +++ b/assign/anneal.cc @@ -123,6 +123,8 @@ inline bool pnode_is_match(tb_vnode *vn, tb_pnode *pn) { } } + // Commented out for now because it's too slow! +#if 0 // Check for 'local' desires - the reason we take the time to do this here is // that they are actually, in many ways, like types with vn->typecount > 1. if (matched && !vn->desires.empty()) { @@ -130,12 +132,7 @@ inline bool pnode_is_match(tb_vnode *vn, tb_pnode *pn) { for (desire_it = vn->desires.begin(); desire_it != vn->desires.end(); desire_it++) { - if (desire_it->first[0] != '?') { - continue; - } - if (desire_it->first[1] != '+') { - continue; - } + if (desire_it->is_l_additive() { tb_pnode::features_map::iterator feature_it = pn->features.find(desire_it->first); if (feature_it == pn->features.end()) { @@ -159,6 +156,7 @@ inline bool pnode_is_match(tb_vnode *vn, tb_pnode *pn) { } } } +#endif return matched; } diff --git a/assign/assign.cc b/assign/assign.cc index cbf5b0d4a0bb20c20c7322e7f84716b1835a78a3..392cb398b7a06aebea768be0358bd0e857f2f533 100644 --- a/assign/assign.cc +++ b/assign/assign.cc @@ -27,6 +27,7 @@ #include <signal.h> #include <sys/signal.h> #include <queue> +#include <algorithm> using namespace boost; @@ -429,7 +430,7 @@ int mapping_precheck() { int matched_bw = 0; // Keep track of desires had how many 'hits', so that we can tell // if any simply were not matched - tb_vnode::desires_count_map matched_desires; + map<crope,int> matched_desires; // Keep track of which link types had how many 'hits', so that we can // tell which type(s) caused this node to fail @@ -493,52 +494,41 @@ int mapping_precheck() { } } - // - // Check features and desires - // - - // Desires first - for (tb_vnode::desires_map::iterator desire_it = v->desires.begin(); - desire_it != v->desires.end(); - desire_it++) { - crope name = (*desire_it).first; - float value = (*desire_it).second; - // Only check for desires that would result in a violation if - // unsatisfied - if (value >= FD_VIOLATION_WEIGHT) { - if (matched_desires.find(name) == matched_desires.end()) { - matched_desires[name] = 0; - } - tb_pnode::features_map::iterator feature_it = - pnode->features.find(name); - if (feature_it != pnode->features.end()) { - matched_desires[name]++; - } else { - potential_match = false; - } - } - } + /* + * Check features and desires + */ - // Next, features - for (tb_pnode::features_map::iterator feature_it = pnode->features.begin(); - feature_it != pnode->features.end(); feature_it++) { - crope name = (*feature_it).first; - float value = (*feature_it).second; + tb_featuredesire_set_iterator + fdit(v->desires.begin(),v->desires.end(), + pnode->features.begin(),pnode->features.end()); + for (;!fdit.done();fdit++) { // Skip 'local' and 'global' features - if (name[0] == '*' || name[0] == '?') { + if (fdit->is_global() || fdit->is_local()) { continue; } - // Only check for feature that would result in a violation if - // undesired - if (value >= FD_VIOLATION_WEIGHT) { - tb_vnode::desires_map::iterator desire_it = - v->desires.find(name); - if (desire_it == v->desires.end()) { + // Only check for FDs that would result in a violation if + // unmatched. + if (fdit.either_violateable()) { + // We look for violateable desires on vnodes so that we + // can report them to the user + if (fdit.membership() == + tb_featuredesire_set_iterator::BOTH && + fdit.membership() == + tb_featuredesire_set_iterator::FIRST_ONLY && + fdit.first_iterator()->is_violateable() && + matched_desires.find(fdit->name()) + == matched_desires.end()) { + matched_desires[fdit->name()] = 0; + } + if (fdit.membership() == + tb_featuredesire_set_iterator::BOTH) { + matched_desires[fdit->name()]++; + } else { potential_match = false; } } } - + // Check link types tb_vnode::link_counts_map::iterator vit; for (vit = v->link_counts.begin(); vit != v->link_counts.end(); @@ -604,7 +594,7 @@ int mapping_precheck() { cout << " Too much bandwidth on emulated links!" << endl; } - for (tb_vnode::desires_count_map::iterator dit = matched_desires.begin(); + for (map<crope,int>::iterator dit = matched_desires.begin(); dit != matched_desires.end(); dit++) { if (dit->second == 0) { diff --git a/assign/common.h b/assign/common.h index 58bc13cd0c2884d0ad06c3e1f1727516dd617614..47d284fcc6d06692e49c2e3d782f9a7e1d6f6336 100644 --- a/assign/common.h +++ b/assign/common.h @@ -21,6 +21,8 @@ using namespace __gnu_cxx; #endif #include "config.h" +#include <utility> +#include <rope> #include <boost/graph/adjacency_list.hpp> @@ -219,4 +221,7 @@ template <class T> struct hashptr { #define HASH_MAP #endif +// For use in functions that want to return a score/violations pair +typedef pair<double,int> score_and_violations; + #endif diff --git a/assign/featuredesire.cc b/assign/featuredesire.cc new file mode 100644 index 0000000000000000000000000000000000000000..fbb026d59e2b21e2cc367e954eb74fd9cdcd5a6f --- /dev/null +++ b/assign/featuredesire.cc @@ -0,0 +1,308 @@ +/* + * EMULAB-COPYRIGHT + * Copyright (c) 2004 University of Utah and the Flux Group. + * All rights reserved. + */ + +/* + * featuredesire.cc - implementation of the objects from featuredesire.h + */ + +#include "featuredesire.h" + +/********************************************************************* + * tb_featuredesire + *********************************************************************/ +tb_featuredesire::name_featuredesire_map + tb_featuredesire::featuredesires_by_name; + + +/* + * Constructor + */ +tb_featuredesire::tb_featuredesire(crope _my_name) : my_name(_my_name), + global(false), local(false), + l_additive(false), g_one_is_okay(false), + g_more_than_one(false), + in_use_globally(0) { + static int highest_id = 0; + + // Pick a unique numeric identifier for this feature/desire + id = highest_id++; + + // Find out which flags should be set for this feature/desire from the name + switch (my_name[0]) { + case '?': + // A local feature - the second character tell us what kind. + // Currently only additive are supported + local = true; + switch(my_name[1]) { + case '+': + l_additive = true; + break; + default: + cerr << "*** Invalid local feature type in feature " << + my_name << endl; + exit(EXIT_FATAL); + } + break; + case '*': + // A global feature - the second character tells us what kind. + global = true; + switch(my_name[1]) { + case '&': + g_one_is_okay = true; + break; + case '!': + g_more_than_one = true; + break; + default: + cerr << "*** Invalid global feature type in feature " << + my_name << endl; + exit(EXIT_FATAL); + } + break; + default: + // Just a regular feature + break; + } + + // Place this into the map for finding featuredesire objects by + // name + assert(featuredesires_by_name.find(my_name) + == featuredesires_by_name.end()); + featuredesires_by_name[my_name] = this; +} + +/* + * Operators + */ +ostream &operator<<(ostream &o, const tb_featuredesire &fd) { + // Perhaps this should print more information like the flags and/or global + // use count + o << fd.my_name; +} + + +/* + * Static functions + */ +tb_featuredesire *tb_featuredesire::get_featuredesire_obj(const crope name) { + name_featuredesire_map::iterator it = + featuredesires_by_name.find(name); + if (it == featuredesires_by_name.end()) { + return new tb_featuredesire(name); + } else { + return it->second; + } +} + +/* + * Functions for maintaining state for global FDs + */ +void tb_featuredesire::add_global_user(int howmany) { + assert(global); + in_use_globally += howmany; +} + +void tb_featuredesire::remove_global_user(int howmany) { + assert(global); + in_use_globally -= howmany; + assert(in_use_globally >= 0); +} + +/********************************************************************* + * tb_node_featuredesire + *********************************************************************/ + +/* + * Constructor + */ +tb_node_featuredesire::tb_node_featuredesire(crope _name, double _weight) : + weight(_weight), violateable(false), used_local_capacity(0.0f) { + // We'll want to change in the in the future to seperate out the notions of + // score and violations + if (weight >= FD_VIOLATION_WEIGHT) { + violateable = true; + } + featuredesire_obj = tb_featuredesire::get_featuredesire_obj(_name); + assert(featuredesire_obj != NULL); +} + +/* + * Add a global user of this feature, and return the score and violations + * induced by this + */ +score_and_violations tb_node_featuredesire::add_global_user() const { + featuredesire_obj->add_global_user(); + + double score = 0.0f; + int violations = 0; + + // Handle the scoring and violations from this change + if (featuredesire_obj->is_g_one() && + (featuredesire_obj->global_use_count() >= 2)) { + // Have to penalize this one, it's not the first + score += weight; + if (violateable) { + violations += 1; + } + } else if (featuredesire_obj->is_g_more() && + (featuredesire_obj->global_use_count() == 1)) { + // Only penalize the first one + score += weight; + if (violateable) { + violations += 1; + } + } + return score_and_violations(score,violations); +} + + +/* + * Remove a global user of this feature, and return the score and violations + * induced by this + */ +score_and_violations tb_node_featuredesire::remove_global_user() const { + featuredesire_obj->remove_global_user(); + + double score = 0.0f; + int violations = 0; + + // Handle the scoring and violations from this change + if (featuredesire_obj->is_g_one() && + (featuredesire_obj->global_use_count() >= 1)) { + // We're not removing the final one, so we count it + score += weight; + if (violateable) { + violations += 1; + } + } else if (featuredesire_obj->is_g_more() && + (featuredesire_obj->global_use_count() == 0)) { + // Only count it if we just removed the final one + score += weight; + if (violateable) { + violations += 1; + } + } + return score_and_violations(score,violations); +} + +/* + * Add a local user of a feature + * XXX - Should we add more violations if we hit multiples of the capacity? + * XXX - These are always assumed to be violatable + */ +score_and_violations tb_node_featuredesire::add_local(double amount) { + double oldvalue = used_local_capacity; + used_local_capacity += amount; + if ((oldvalue <= weight) && (used_local_capacity > weight)) { + // This one pushed us over the edge, violation! + return score_and_violations(SCORE_OVERUSED_LOCAL_FEATURE,1); + } else { + // We're good, doesn't cost anything; + return score_and_violations(0.0f,0); + } +} + +/* + * Subtract a local user of a feature + */ +score_and_violations tb_node_featuredesire::subtract_local(double amount) { + double oldvalue = used_local_capacity; + used_local_capacity -= amount; + if ((oldvalue > weight) && (used_local_capacity <= weight)) { + // Back down to below capacity, remove a violation + return score_and_violations(SCORE_OVERUSED_LOCAL_FEATURE,1); + } else { + // We're good, doesn't cost anything; + return score_and_violations(0.0f,0); + } + +} + +/********************************************************************* + * tb_featuredesire_set_iterator + *********************************************************************/ + +tb_featuredesire_set_iterator::tb_featuredesire_set_iterator( + node_fd_set::iterator _begin1, node_fd_set::iterator _end1, + node_fd_set::iterator _begin2, node_fd_set::iterator _end2) : + it1(_begin1), end1(_end1), it2(_begin2), end2(_end2) { + /* + * Figure out what the next element of the set is + */ + // First check to see if we've hit the end of both lists + if ((it1 == end1) && (it2 == end2)) { + current = end1; + } else if ((it1 != end1) && ((it2 == end2) || (*it1 < *it2))) { + // If one has hit the end of the list, go with the other - otherwise, + // go with the smaller of the two. + current_membership = FIRST_ONLY; + current = it1; + } else if ((it1 == end1) || (*it2 < *it1)) { + current_membership = SECOND_ONLY; + current = it2; + } else { + // If neither is smaller, they must be equal + current = it1; + current_membership = BOTH; + } +} + +bool tb_featuredesire_set_iterator::done() const { + return((it1 == end1) && (it2 == end2)); +} + +void tb_featuredesire_set_iterator::operator++(int) { + /* + * Advance the iterator(s) + */ + // Make sure they don't try to go off the end of the list + assert(!done()); + // If one iterator has gone off the end of its list, advance the other one + // - otherwise, go with the smaller one. Or, if they are equal, increment + // both. + if ((it1 != end1) && ((it2 == end2) || (*it1 < *it2))) { + it1++; + } else if ((it1 == end1) || (*it2 < *it1)) { + it2++; + } else { + // If neither was smaller, they must be equal - advance both + it1++; + it2++; + } + + /* + * Figure out what the next element of the set is + */ + // First check to see if we've hit the end of both lists + if ((it1 == end1) && (it2 == end2)) { + current = end1; + } else if ((it2 == end2) || ((it1 != end1) && (*it1 < *it2))) { + // If one has hit the end of the list, go with the other - otherwise, + // go with the smaller of the two. + current_membership = FIRST_ONLY; + current = it1; + } else if ((it1 == end1) || (*it2 < *it1)) { + current_membership = SECOND_ONLY; + current = it2; + } else { + // If neither is smaller, they must be equal + current = it1; + current_membership = BOTH; + } +} + +bool tb_featuredesire_set_iterator::both_equiv() const { + assert(current_membership == BOTH); + return (it1->equivalent(*it2)); +} + +bool tb_featuredesire_set_iterator::either_violateable() const { + if (current_membership == BOTH) { + return (it1->is_violateable() || it2->is_violateable()); + } else { + return current->is_violateable(); + } +} diff --git a/assign/featuredesire.h b/assign/featuredesire.h new file mode 100644 index 0000000000000000000000000000000000000000..e6006a5c0cddf3a9b09b72f8c92f85fd31a21fd3 --- /dev/null +++ b/assign/featuredesire.h @@ -0,0 +1,227 @@ +/* + * EMULAB-COPYRIGHT + * Copyright (c) 2004 University of Utah and the Flux Group. + * All rights reserved. + */ + +#ifndef __FEATUREDESIRE_H +#define __FEATUREDESIRE_H + +#include "common.h" + +#include <rope> +#include <map> +#include <set> +#include <slist> + +/* + * Base class for features and desires - not intended to be used directly, only + * to be subclassed by tb_feature and tb_desire + */ +class tb_featuredesire { + public: + + /* + * Note: Constructor is below - use get_featuredesire_obj instead + */ + + ~tb_featuredesire() { ; } + + /* + * Get the object for a particular feature/desire - if one does not + * exist, creates a new one. Otherwise, returns the existing object + */ + static tb_featuredesire *get_featuredesire_obj(const crope name); + + /* + * Silly accessor functions + */ + inline bool is_global() const { return global; } + inline bool is_local() const { return local; } + inline bool is_l_additive() const { return l_additive; } + inline bool is_g_one() const { return g_one_is_okay; } + inline bool is_g_more() const { return g_more_than_one; } + inline int global_use_count() const { return in_use_globally; } + inline crope name() const { return my_name; } + + /* + * Operators, primarily for use with the STL + */ + inline bool operator==(const tb_featuredesire &o) const { + return (id == o.id); + } + + inline bool operator<(const tb_featuredesire &o) const { + return (id < o.id); + } + + friend ostream &operator<<(ostream &o, const tb_featuredesire &fd); + + /* + * Functions for maintaining state for global FDs + */ + void add_global_user(int howmany = 1); + void remove_global_user(int howmany = 1); + + private: + /* + * This is private, so that we can force callers to go through the + * static get_featuredesire_obj function which uses the existing desire + * if there is one + */ + explicit tb_featuredesire(crope _my_name); + + // Globally unique identifier + int id; + + // String name of this FD, used for debugging purposes only + crope my_name; + + // Flags + bool global; // Whether this FD has global scope + bool g_one_is_okay; // If global, we can have one without penalty + bool g_more_than_one; // If global, more than one doesn't incur + // additional penalty + bool local; // Whether this FD has local scope + bool l_additive; // If a local FD, is additive + + // Counts how many instances of this feature are in use across all + // nodes - for use with global nodes + int in_use_globally; + + typedef map<crope,tb_featuredesire*> name_featuredesire_map; + static name_featuredesire_map featuredesires_by_name; +}; + +/* + * This class stores information about a particular feature or desire for a + * particular node, rather than global information about the feature or desire. + */ +class tb_node_featuredesire { + public: + tb_node_featuredesire(crope _name, double _weight); + + ~tb_node_featuredesire() { ; } + + /* + * Operators, mostly for use with the STL + */ + const bool operator==(const tb_node_featuredesire &o) const { + // Note: Compares that two FDs are have the same name/ID, but NOT + // that they have the same weight - see equivalent() below for that + return(*featuredesire_obj == *(o.featuredesire_obj)); + } + + const bool operator<(const tb_node_featuredesire &o) const { + return(*featuredesire_obj < *(o.featuredesire_obj)); + } + + // Since we have to use == to compare names, for the STL's sake, this + // function checks to make sure that the node-specific parts are + // equivalent too + const bool equivalent(const tb_node_featuredesire &o) const { + return ((*this == o) && (weight == o.weight) && + (violateable == o.violateable)); + } + + /* + * Silly accesors + */ + inline const bool is_violateable() const { return violateable; } + inline const double cost() const { return weight; } + inline const double used() const { return used_local_capacity; } + + /* + * Proxy functions for the stuff in tb_featuredesire + */ + const crope name() const { return featuredesire_obj->name(); } + const bool is_local() const { return featuredesire_obj->is_local(); } + const bool is_global() const { return featuredesire_obj->is_global(); } + const bool is_l_additive() const { + return featuredesire_obj->is_l_additive(); + } + + score_and_violations add_global_user() const; + score_and_violations remove_global_user() const; + + /* + * Functions for tracking local features/desires + */ + score_and_violations add_local(double amount); + score_and_violations subtract_local(double amount); + + protected: + + double weight; + bool violateable; + tb_featuredesire *featuredesire_obj; + double used_local_capacity; +}; + +/* + * Types to hold virtual nodes' sets of desires and physical nodes' sets of + * features + */ +typedef slist<tb_node_featuredesire> node_feature_set; +typedef slist<tb_node_featuredesire> node_desire_set; +typedef slist<tb_node_featuredesire> node_fd_set; + +/* + * Kind of like an iterator, but not quite - used for going through a virtual + * node's desires and a physical node's features, and deterimining which are + * only in one set, and which are in both + */ +class tb_featuredesire_set_iterator { + public: + /* + * Constructors + */ + tb_featuredesire_set_iterator(node_fd_set::iterator _begin1, + node_fd_set::iterator _end1, + node_fd_set::iterator _begin2, + node_fd_set::iterator _end2); + + // Enum for indicating which set(s) an element belongs to + typedef enum { FIRST_ONLY, SECOND_ONLY, BOTH } set_membership; + + // Return whether or not we've iterated to the end of both sets + bool done() const; + + // If we have a membership() of BOTH, do they pass the equivalence + // test? + bool both_equiv() const; + + // Is either of the two elements violateable? + bool either_violateable() const; + + // Iterate to the next element of the set + void operator++(int); + + // Return the member of the set we're currently iterating to + const tb_node_featuredesire &operator*() const { + return *current; + } + + // Return the member of the set we're currently iterating to + const tb_node_featuredesire *operator->() const { + return &*current; + } + + // Return the set membership of the current element + const set_membership membership() const { + return current_membership; + } + + // Give out the iterators to the two elements, so that they can be + // operated upon + node_fd_set::iterator &first_iterator() { return it1; } + node_fd_set::iterator &second_iterator() { return it2; } + + private: + node_fd_set::iterator it1, end1; + node_fd_set::iterator it2, end2; + node_fd_set::iterator current; + set_membership current_membership; +}; + +#endif diff --git a/assign/parse_ptop.cc b/assign/parse_ptop.cc index fc163baa991c323470e172e327bae7e2e8ef9d50..2508d6557d9892e8184a8a701de2d169b6453c3f 100644 --- a/assign/parse_ptop.cc +++ b/assign/parse_ptop.cc @@ -54,12 +54,12 @@ int parse_ptop(tb_pgraph &PG, tb_sgraph &SG, istream& i) { int num_nodes = 0; int line=0,errors=0; - char inbuf[1024]; + char inbuf[4096]; string_vector parsed_line; while (!i.eof()) { line++; - i.getline(inbuf,1024); + i.getline(inbuf,4096); parsed_line = split_line(inbuf,' '); if (parsed_line.size() == 0) {continue;} @@ -128,7 +128,9 @@ int parse_ptop(tb_pgraph &PG, tb_sgraph &SG, istream& i) ptop_error("Bad node line, bad cost: " << gcost << "."); gcost = 0; } - p->features[feature] = gcost; + + p->features.push_front(tb_node_featuredesire(feature,gcost)); + } /* * Parse any other node options or flags @@ -163,6 +165,7 @@ int parse_ptop(tb_pgraph &PG, tb_sgraph &SG, istream& i) ptop_error("Bad flag given: " << flag << "."); } } + p->features.sort(); pname2vertex[name] = pv; } } else if (command.compare("link") == 0) { diff --git a/assign/parse_top.cc b/assign/parse_top.cc index f5200dbd2f0981230b5bb750da1109045f816517..12f230912042d2a0e7087b07cc2fbe08a1bb17c8 100644 --- a/assign/parse_top.cc +++ b/assign/parse_top.cc @@ -148,10 +148,12 @@ int parse_top(tb_vgraph &VG, istream& i) top_error("Bad desire, bad weight."); gweight = 0; } - v->desires[desirename] = gweight; + v->desires.push_front( + tb_node_featuredesire(desirename,gweight)); } } } + v->desires.sort(); } } else if (command.compare("link") == 0) { if (parsed_line.size() < 8) { diff --git a/assign/pclass.cc b/assign/pclass.cc index 2cdc9467f5aaa31f4c4b9dfb9f0fb5bd69e4fb1c..143900b04043d25709e0b660462ff772d4add37c 100644 --- a/assign/pclass.cc +++ b/assign/pclass.cc @@ -102,29 +102,22 @@ int pclass_equiv(tb_pgraph &PG, tb_pnode *a,tb_pnode *b) } // check features - for (tb_pnode::features_map::iterator it=a->features.begin(); - it != a->features.end();++it) { - const crope &a_feature = (*it).first; - const double a_weight = (*it).second; - - tb_pnode::features_map::iterator bit; - bit = b->features.find(a_feature); - if ((bit == b->features.end()) || ((*bit).second != a_weight)) - return 0; - } + tb_featuredesire_set_iterator fdit(a->features.begin(),a->features.end(), + b->features.begin(),b->features.end()); + + while (!fdit.done()) { + if (fdit.membership() == tb_featuredesire_set_iterator::BOTH) { + // Great, we've got a feature that's in both, just make sure that + // the two are equivalent (score, etc.) + if (!fdit.both_equiv()) { + return 0; + } + } else { + // Got a feature that's in one but not the other + return 0; + } - // have to go both ways in case the second node has a feature the first - // doesn't - for (tb_pnode::features_map::iterator it=b->features.begin(); - it != b->features.end();++it) { - const crope &b_feature = (*it).first; - const double b_weight = (*it).second; - - tb_pnode::features_map::iterator ait; - ait = a->features.find(b_feature); - if (ait == a->features.end()) { - return 0; - } + fdit++; } // Check links diff --git a/assign/physical.h b/assign/physical.h index f8b8b8f0c8f793b5a8970b045291232aa33390fa..e00ccc9591849905be57e74bb7f19f3043d514ad 100644 --- a/assign/physical.h +++ b/assign/physical.h @@ -33,6 +33,8 @@ using namespace __gnu_cxx; #include <hash_map> #endif +#include "featuredesire.h" + // Icky, but I can't include virtual.h here class tb_vnode; typedef hash_set<tb_vnode*,hashptr<tb_vnode*> > tb_vnode_set; @@ -127,14 +129,12 @@ public: } }; - typedef hash_map<crope,type_record*> types_map; - typedef hash_map<crope,double> features_map; - // contains max nodes for each type + typedef hash_map<crope,type_record*> types_map; types_map types; // contains cost of each feature - features_map features; + node_feature_set features; crope name; // name of the node bool typed; // has it been typed @@ -211,9 +211,9 @@ public: it!=node.types.end();it++) o << " " << (*it).first << " -> " << (*it).second << endl; o << " Features:" << endl; - for (features_map::const_iterator it = node.features.begin(); - it!=node.features.end();it++) - cout << " " << (*it).first << " -> " << (*it).second << endl; + for (node_feature_set::const_iterator it = node.features.begin(); + it != node.features.end(); it++) + cout << " " << it->name() << " -> " << it->cost() << endl; o << " Current Type: " << node.current_type << endl; /* << " (" << node.current_load << "/" << node.max_load << ")" << endl; */ o << " switches="; diff --git a/assign/score.cc b/assign/score.cc index 39d30fc1ed92f3d55988922d5cf1a3caefdc4c27..a14a07fd8864cd6d25f97bfdd32757d4c0b6ef9a 100644 --- a/assign/score.cc +++ b/assign/score.cc @@ -38,6 +38,7 @@ using namespace boost; #include "virtual.h" #include "pclass.h" #include "score.h" +#include "featuredesire.h" #include "math.h" @@ -62,14 +63,8 @@ bool find_best_link(pvertex pv,pvertex switch_pv,tb_vlink *vlink, int find_interswitch_path(pvertex src_pv,pvertex dest_pv, int bandwidth,pedge_path &out_path, pvertex_list &out_switches); -inline double fd_score(tb_vnode *vnode,tb_pnode *pnode,int &out_fd_violated, - bool include_violations); -inline void add_global_fds(tb_vnode *vnode,tb_pnode *pnode); -inline void remove_global_fds(tb_vnode *vnode,tb_pnode *pnode); -inline double add_stateful_fds(tb_vnode *vnode, tb_pnode *pnode, - int &out_fd_violated); -inline double remove_stateful_fds(tb_vnode *vnode, tb_pnode *pnode, - int &out_fd_violated); +score_and_violations score_fds(tb_vnode *vnode, tb_pnode *pnode, bool add); + void score_link_info(vedge ve, tb_pnode *src_pnode, tb_pnode *dst_pnode, tb_vnode *src_vnode, tb_vnode *dst_vnode); void unscore_link_info(vedge ve, tb_pnode *src_pnode, tb_pnode *dst_pnode, @@ -100,12 +95,6 @@ void score_link_endpoints(pedge pe); #define MIN(a,b) (((a) < (b))? (a) : (b)) #define MAX(a,b) (((a) > (b))? (a) : (b)) -/* - * For features and desires that have a some sort of global impact - */ -typedef hash_map<crope,unsigned int> fd_count_map; -fd_count_map global_fd_set; - /* * score() * Returns the score. @@ -529,14 +518,10 @@ void remove_node(vvertex vv) /* * Scoring for features and desires */ - int fd_violated; - double fds=fd_score(vnode,pnode,fd_violated,false); - SSUB(fds); - remove_global_fds(vnode,pnode); - double sfds=remove_stateful_fds(vnode,pnode,fd_violated); - SSUB(sfds); - violated -= fd_violated; - vinfo.desires -= fd_violated; + score_and_violations sv = score_fds(vnode,pnode,false); + SSUB(sv.first); + violated -= sv.second; + vinfo.desires -= sv.second; SDEBUG(cerr << " new score = " << score << " new violated = " << violated << endl); } @@ -1152,14 +1137,12 @@ int add_node(vvertex vv,pvertex pv, bool deterministic, bool is_fixed) violated--; // features/desires - add_global_fds(vnode,pnode); - int fd_violated; - double fds = fd_score(vnode,pnode,fd_violated,is_fixed); - SADD(fds); - double sfds = add_stateful_fds(vnode,pnode,fd_violated); - SADD(sfds); - violated += fd_violated; - vinfo.desires += fd_violated; + score_and_violations sv = score_fds(vnode,pnode,true); + SADD(sv.first); + if (!is_fixed) { + violated += sv.second; + vinfo.desires += sv.second; + } // pclass if ((!disable_pclasses) && (!tr->is_static) && pnode->my_class && @@ -1579,249 +1562,96 @@ UNSCORE_TRIVIAL: vlink->link_info.type_used = tb_link_info::LINK_UNMAPPED; } -double fd_score(tb_vnode *vnode,tb_pnode *pnode,int &fd_violated, - bool ignore_violations) -{ - double fd_score=0; - fd_violated=0; - - double value; - tb_vnode::desires_map::iterator desire_it; - tb_pnode::features_map::iterator feature_it; - - // Optimize the case where the vnode has no desires - if (!vnode->desires.empty()) { - for (desire_it = vnode->desires.begin(); - desire_it != vnode->desires.end(); - desire_it++) { - // We ignore local desires, which are handled by add_stateful_fds() and - // remove_stateful_fds() - if (desire_it->first[0] == '?') { - continue; - } - feature_it = pnode->features.find((*desire_it).first); - SDEBUG(cerr << " desire = " << (*desire_it).first << " " << - (*desire_it).second << endl); - - if (feature_it == pnode->features.end()) { - // Unmatched desire. Add cost. - SDEBUG(cerr << " unmatched" << endl); - value = (*desire_it).second; - fd_score += SCORE_DESIRE*value; - if ((value >= FD_VIOLATION_WEIGHT) && (!ignore_violations)) { - fd_violated++; - } - } else { - // Features/desires with a '+' at the front are additive - rather than - // 'cancelling out' if both have it, they add together, possibly - // resulting in a violation - if (((*desire_it).first)[0] == '+') { - value = (*desire_it).second + (*feature_it).second; - SDEBUG(cerr << " additive - total " << value << endl); - fd_score += SCORE_DESIRE*value; - if ((value >= FD_VIOLATION_WEIGHT) && (!ignore_violations)) { - fd_violated++; - } - } - } - } - } - - // Optimize the case where the pnode has no features - if (!pnode->features.empty()) { - for (feature_it = pnode->features.begin(); - feature_it != pnode->features.end();++feature_it) { - // We ignore local features, which are handled by add_stateful_fds() and - // remove_stateful_fds() - if (feature_it->first[0] == '?') { - continue; - } - crope feature_name = (*feature_it).first; - value = (*feature_it).second; - SDEBUG(cerr << " feature = " << feature_name - << " " << (*feature_it).second << endl); - - if (feature_name[0] == '*') { - SDEBUG(cerr << " global" << endl); - // Handle features with global scope - for now, these don't have - // desires to go with them, but we may want to change that at some - // point - switch (feature_name[1]) { - case '&': // A 'one is okay' feature - only score if we have more - // than one pnode with this feature - SDEBUG(cerr << " 'one is okay'" << endl); - if (global_fd_set[feature_name] > 1) { - SDEBUG(cerr << " but more than one" << endl); - fd_score+=SCORE_FEATURE*value; - if ((value >= FD_VIOLATION_WEIGHT) && (!ignore_violations)) { - fd_violated++; +score_and_violations score_fds(tb_vnode *vnode, tb_pnode *pnode, bool add) { + // Iterate through the set of features and desires on these nodes + tb_featuredesire_set_iterator fdit(vnode->desires.begin(), + vnode->desires.end(), pnode->features.begin(), + pnode->features.end()); + + // Keep track of the score and violations changes we rack up as we go along + double score_delta = 0.0f; + int violations_delta = 0; + + for (;!fdit.done();fdit++) { + node_fd_set::iterator fit, dit; + dit = fdit.first_iterator(); + fit = fdit.second_iterator(); + // What we do here depends on whether it's a feature of the vnode, a desire + // of the pnode, or both + switch (fdit.membership()) { + case tb_featuredesire_set_iterator::BOTH: + /* + * On both + */ + // note: Right now, global features cannot be + // desires, so there is no code path for them here + if (fdit->is_local()) { + if (fdit->is_l_additive()) { + score_and_violations delta; + if (add) { + delta = fit->add_local(dit->cost()); + } else { + delta = fit->subtract_local(dit->cost()); + } + score_delta += delta.first; + violations_delta += delta.second; + } + } else { + // Just a normal feature or desire - since both have it, + // no need to do any scoring } - } - break; - case '!': // A 'more than one is okay' feature - if we already have one, - // a second doesn't incur further penalty - SDEBUG(cerr << " ' more than one is okay'" << endl); - if (global_fd_set[feature_name] == 1) { - SDEBUG(cerr << " but only one" << endl); - fd_score+=SCORE_FEATURE*value; - if ((value >= FD_VIOLATION_WEIGHT) && (!ignore_violations)) { - fd_violated++; + break; + case tb_featuredesire_set_iterator::FIRST_ONLY: + /* + * On the vnode, but not the pnode + */ + // May be a violation to not have the matching feature + if (fdit->is_violateable()) { + violations_delta++; } - } - break; - default: - // Global features are required to have some kind of type - cout << "Bad global feature " << (*feature_it).first << endl; - exit(EXIT_FATAL); - } - } else { - desire_it = vnode->desires.find(feature_name); - if (desire_it == vnode->desires.end()) { - // Unused feature. Add weight - SDEBUG(cerr << " unused" << endl); - fd_score+=SCORE_FEATURE*value; - if ((value >= FD_VIOLATION_WEIGHT) && (!ignore_violations)) { - fd_violated++; - } - } - } - } - } - - SDEBUG(cerr << " Total feature score: " << fd_score << endl); - return fd_score; -} - -/* - * Compute the features/desires score resulting from mapping the given vnode to - * the given vnode. We do this specially for local features and desires, - * because they are stateful (ie. we have to explicitly add and subtract them, - * we can't just compute the current value statelessly.) - * - */ -double add_stateful_fds(tb_vnode *vnode, tb_pnode *pnode, - int &out_fd_violated) { - tb_vnode::desires_map::iterator desire_it; - double score = 0.0f; - if (!vnode->desires.empty()) { - for (desire_it = vnode->desires.begin(); - desire_it != pnode->features.end();++desire_it) { - if (desire_it->first[0] == '?') { - // Found a local desire - does the pnode have it? - tb_pnode::features_map::iterator feature_it; - feature_it = pnode->features.find(desire_it->first); - if (feature_it == pnode->features.end()) { - // Didn't find the feature - violation! - score += SCORE_MISSING_LOCAL_FEATURE; - out_fd_violated++; - } else { - // Found the feature, score it - switch (((*desire_it).first)[1]) { - case '+': - /* - * A 'sum' local feature - we add up all of the vnode desires, - * and everything's fine as long as they're <= the pnodes' - * feature weight. - */ - double oldvalue; - oldvalue = feature_it->second; - feature_it->second -= desire_it->second; - if ((oldvalue >= 0) && (feature_it->second < 0)) { - // This one pushed us over the edge, violation! - score += SCORE_OVERUSED_LOCAL_FEATURE; - out_fd_violated++; - } - break; - default: - // Local features are required to have some kind of type - cout << "Bad local desire " << (*desire_it).first << endl; - exit(EXIT_FATAL); - } - } - } - } - } - - return score; -} - -double remove_stateful_fds(tb_vnode *vnode, tb_pnode *pnode, - int &out_fd_violated) { - tb_vnode::desires_map::iterator desire_it; - double score = 0.0f; - if (!vnode->desires.empty()) { - for (desire_it = vnode->desires.begin(); - desire_it != pnode->features.end();++desire_it) { - if (desire_it->first[0] == '?') { - // Found a local desire - does the pnode have it? - tb_pnode::features_map::iterator feature_it; - feature_it = pnode->features.find(desire_it->first); - if (feature_it == pnode->features.end()) { - // Didn't find the feature, so we just removed a violation (note - - // out_fd_violted gets subtracted from the total violation count, so - // adding to it causes a violation to be removed.) - score += SCORE_MISSING_LOCAL_FEATURE; - out_fd_violated++; - } else { - // Found the feature, score it - switch (((*desire_it).first)[1]) { - case '+': - /* - * A 'sum' local feature - we add up all of the vnode desires, - * and everything's fine as long as they're <= the pnodes' - * feature weight. - */ - double oldvalue; - oldvalue = feature_it->second; - feature_it->second += desire_it->second; - if ((oldvalue < 0) && (feature_it->second >= 0)) { - // This one pushed us over the edge, we removed a violation - // (note - out_fd_violted gets subtracted from the total - // violation count, so adding to it causes a violation to be - // removed.) - score += SCORE_OVERUSED_LOCAL_FEATURE; - out_fd_violated++; - } - break; + if (fdit->is_local()) { + // We handle local features specially + score_delta += SCORE_MISSING_LOCAL_FEATURE; + } else { + score_delta += fdit->cost(); + } + break; + case tb_featuredesire_set_iterator::SECOND_ONLY: + /* + * On the pnode, but not the vnode + */ + // What we do here depends on what kind o feature it is + if (fdit->is_local()) { + // Do nothing for local features that are not matched - + // they are free to waste + } else if (fdit->is_global()) { + // Global feature - we have to look at what others are in + // use + score_and_violations delta; + if (add) { + delta = fdit->add_global_user(); + } else { + delta = fdit->remove_global_user(); + } + score_delta += delta.first; + violations_delta += delta.second; + } else { + // Regular feature - score the sucker + score_delta += fdit->cost(); + if (fdit->is_violateable()) { + violations_delta++; + } + } + break; default: - // Local features are required to have some kind of type - cout << "Bad local desire " << (*desire_it).first << endl; - exit(EXIT_FATAL); - } + cerr << "*** Internal error - bad set membership" << endl; + exit(EXIT_FATAL); } - } } - } - return score; + // Okay, return the score and violations + //cerr << "Returning (" << score_delta << "," << violations_delta << ")" << + // endl; + return score_and_violations(score_delta,violations_delta); } -/* - * For now, in these function, which simply keep the global_fd_set - * data structure up to date, we ignore vnodes desires - however, we may - * decide to change this someday if we use global features for some other - * purpose - */ -void add_global_fds(tb_vnode *vnode,tb_pnode *pnode) { - tb_pnode::features_map::iterator feature_it; - if (!pnode->features.empty()) { - for (feature_it = pnode->features.begin(); - feature_it != pnode->features.end();++feature_it) { - if (feature_it->first[0] == '*') { - global_fd_set[feature_it->first]++; - } - } - } -} -void remove_global_fds(tb_vnode *vnode,tb_pnode *pnode) { - tb_pnode::features_map::iterator feature_it; - if (!pnode->features.empty()) { - for (feature_it = pnode->features.begin(); - feature_it != pnode->features.end();++feature_it) { - if (feature_it->first[0] == '*') { - global_fd_set[feature_it->first]--; - assert(global_fd_set[feature_it->first] >= 0); - } - } - } -} diff --git a/assign/solution.cc b/assign/solution.cc index b75d4c1df28cab8c7383155ee5003e3492aeb851..b91008d47f487e6278039fb8141891f299e1fd8f 100644 --- a/assign/solution.cc +++ b/assign/solution.cc @@ -164,12 +164,14 @@ void print_solution_summary() } // Print out used local additive features - tb_pnode::features_map::iterator feature_it; + node_feature_set::iterator feature_it; for (feature_it = pnode->features.begin(); feature_it != pnode->features.end();++feature_it) { - if ((feature_it->first[0] == '?') && (feature_it->first[1] == '+')) { - double remaining = feature_it->second; - cout << " " << feature_it->first << ":" << remaining << endl; + if (feature_it->is_local() && feature_it->is_l_additive()) { + double total = feature_it->cost(); + double used = feature_it->used(); + cout << " " << feature_it->name() << ": used=" << used << + " total=" << total << endl; } } } diff --git a/assign/virtual.h b/assign/virtual.h index b07e4b6b872f208f99ac7e2ef4342c73e69d2232..a9035682f1a7a8b190dbebf3681081234bd03b80 100644 --- a/assign/virtual.h +++ b/assign/virtual.h @@ -21,6 +21,7 @@ #include <list> using namespace std; +#include "featuredesire.h" class tb_plink; class tb_vnode; @@ -89,19 +90,16 @@ public: o << "tb_vnode: " << node.name << " (" << &node << ")" << endl; o << " Type: " << node.type << " Count: " << node.typecount << endl; o << " Desires:" << endl; - for (desires_map::const_iterator it = node.desires.begin(); + for (node_desire_set::const_iterator it = node.desires.begin(); it!=node.desires.end();it++) - o << " " << (*it).first << " -> " << (*it).second << endl; + o << " " << it->name() << " -> " << it->cost() << endl; o << " vclass=" << node.vclass << " fixed=" << node.fixed << endl; return o; } - typedef hash_map<crope,double> desires_map; - typedef hash_map<crope,int> desires_count_map; - // contains weight of each desire - desires_map desires; + node_desire_set desires; crope type; // the current type of the node int typecount; // How many slots of the type this vnode takes up