Commit 491db35a authored by Robert Ricci's avatar Robert Ricci

Merge my re-write of features and desires from the assign-fd-rewrite

branch.
parent 5e58bf75
......@@ -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
......
......@@ -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;
}
......
......@@ -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) {
......
......@@ -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
/*
* 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();
}
}
/*
* 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
......@@ -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) {
......
......@@ -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) {
......