|
From: Jonathan L. <le...@us...> - 2009-01-22 15:14:23
|
Update of /cvsroot/pyxida/Pyxida/src/edu/harvard/syrah/pyxida/nc/lib In directory ddv4jf1.ch3.sourceforge.com:/tmp/cvs-serv473/src/edu/harvard/syrah/pyxida/nc/lib Modified Files: NCClient.java Coordinate.java NCClientIF.java Added Files: AppCoordClient.java Log Message: Factored out app level coordinates to make each node's coordinate a bit more lightweight. This should allow for better simulations. AppCoordClient has a minor templating problem. Index: NCClientIF.java =================================================================== RCS file: /cvsroot/pyxida/Pyxida/src/edu/harvard/syrah/pyxida/nc/lib/NCClientIF.java,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** NCClientIF.java 27 Mar 2008 18:33:01 -0000 1.2 --- NCClientIF.java 22 Jan 2009 15:14:17 -0000 1.3 *************** *** 1,3 **** --- 1,7 ---- + package edu.harvard.syrah.pyxida.nc.lib; + /* + * Pyxida - a network coordinate library + * * Copyright 2008 Jonathan Ledlie and Peter Pietzuch * *************** *** 16,22 **** * limitations under the License. */ - package edu.harvard.syrah.pyxida.nc.lib; ! public interface NCClientIF { ! } --- 20,191 ---- * limitations under the License. */ ! import java.io.DataInputStream; ! import java.io.DataOutputStream; ! import java.io.IOException; ! import java.util.Hashtable; ! import java.util.Set; ! public interface NCClientIF<T> { ! ! // for debugging simulations ! public abstract void setLocalID(T _local_addr); ! ! public abstract String toString(); ! ! public abstract Hashtable<String, Double> getStatistics(); ! ! public abstract void reset(); ! ! /** ! * Returns the dimension of the Euclidian space coordinates are embedded in. ! * ! * @return the coordinate space dimension ! */ ! public abstract int getNumDimensions(); ! ! /** ! * Returns the system-level Vivaldi coordinates. These coordinates change more ! * frequently than the application-level coordinates. ! * ! * @return the system-level coordinates ! */ ! public abstract Coordinate getSystemCoords(); ! ! /** ! * Returns the system-level error, which denotes the accuracy of the ! * system-level coordinates. ! * ! * @return the system-level error ! */ ! public abstract double getSystemError(); ! ! /** ! * Returns the age of our coordinate Note that this does not require ! * clock-synchronization because it is relative to our coordinate ! * ! * @return relative age of our coordinate since we last updated it ! */ ! public abstract long getAge(long curr_time); ! ! /** ! * Returns the list of observers, to which observers for the application-level ! * coordinate can be added, removed, and so forth. ! * ! * @return the list of observers for the application-level coordinate ! */ ! public abstract ObserverList getObserverList(); ! ! /** ! * Notifies this <code>VivaldiClient</code> object that a host that supports ! * Vivaldi has joined the system. State associated with the new host is ! * created. This method succeeds and returns <code>true</code> only if the ! * host is not already registered with this <code>VivaldiClient</code> ! * object. ! * ! * @param addr ! * the address of the joining host ! * @return <code>true</code> if <code>addr</code> is registered and its ! * associated state created, <code>false</code> otherwise ! */ ! public abstract boolean addHost(T addr); ! ! /** ! * Notifies this <code>VivaldiClient</code> object that a host that supports ! * Vivaldi and has the provided coordinates and error has joined the system. ! * State associated with the new host is created. This method succeeds and ! * returns <code>true</code> only if the host is not already registered with ! * this <code>VivaldiClient</code> object. ! * ! * @param addr ! * the address of the joining host ! * @param _r_coord ! * the app-level coordinates of the remote host ! * @param r_error ! * the system-level error of the remote host ! * @param sample_rtt ! * the RTT sample to the remote host ! * @param curr_time ! * the current time, in milliseconds ! * @param can_update ! * <code>true</code> if this method can update a host already ! * present ! * @return <code>true</code> if <code>addr</code> is registered and its ! * associated state created, <code>false</code> otherwise ! */ ! ! public abstract boolean addHost(T addr, Coordinate _r_coord, ! double r_error, long curr_time, boolean can_update); ! ! /** ! * Notifies this <code>VivaldiClient</code> object that a host that supports ! * Vivaldi has left the system. However, the state (i.e. short list of RTT ! * values) is kept because it will be useful if and when the node returns into ! * the system ! * ! * @param addr ! * the address of the departing host ! * @return <code>true</code> if <code>addr</code> was a known node ! */ ! ! public abstract boolean removeHost(T addr); ! ! /** ! * Returns whether the given host has been registered with this ! * <code>VivaldiClient</code> object. ! * ! * @param addr ! * the address to query as registered ! * @return <code>true</code> if registered, <code>false</code> otherwise ! */ ! public abstract boolean containsHost(T addr); ! ! /** ! * Returns all hosts that support Vivaldi and have been registered with this ! * <code>VivaldiClient</code> object. The returned set is backed by the true ! * set of registered hosts, but cannot be modified. ! * ! * @return the set of registered Vivaldi-supporting hosts ! */ ! public abstract Set<T> getHosts(); ! ! /** ! * This method is invoked when a new RTT sample is made to a host that ! * supports Vivaldi. This method succeeds and returns <code>true</code> only ! * if the host is already registered with this <code>VivaldiClient</code> ! * object, and the RTT sample is valid. ! * ! * @param addr ! * the address of the host ! * @param _r_coord ! * the system-level coordinates of the remote host ! * @param r_error ! * the system-level error of the remote host ! * @param sample_rtt ! * the RTT sample to the remote host ! * @param curr_time ! * the current time, in milliseconds ! * @param can_add ! * <code>true</code> if this method can add a host not already ! * present ! * @return <code>true</code> if <code>addr</code> is registered and the ! * sample is processed, <code>false</code> otherwise ! */ ! ! public abstract boolean processSample(T addr, Coordinate _r_coord, ! double r_error, double sample_rtt, long sample_age, long curr_time, ! boolean can_add); ! ! public abstract boolean hasAllNeighbors(); ! ! public abstract boolean stabilized(); ! ! public abstract String printNeighbors(); ! ! public abstract T getNeighborToPing(long curr_time); ! ! public abstract void startUp(DataInputStream is) throws IOException; ! ! public abstract void shutDown(DataOutputStream os) throws IOException; ! ! } \ No newline at end of file Index: Coordinate.java =================================================================== RCS file: /cvsroot/pyxida/Pyxida/src/edu/harvard/syrah/pyxida/nc/lib/Coordinate.java,v retrieving revision 1.9 retrieving revision 1.10 diff -C2 -d -r1.9 -r1.10 *** Coordinate.java 21 Jan 2009 19:22:48 -0000 1.9 --- Coordinate.java 22 Jan 2009 15:14:17 -0000 1.10 *************** *** 197,201 **** if (c == null) { ! assert (!NCClient.SIMULATION); return -1.; } --- 197,201 ---- if (c == null) { ! //assert (!NCClient.SIMULATION); return -1.; } *************** *** 271,275 **** for (int i = 0; i < coords.length; ++i) { if (Double.isNaN(coords[i])) { ! if (NCClient.SIMULATION) System.err.println("coord isNaN i=" + i); return false; --- 271,275 ---- for (int i = 0; i < coords.length; ++i) { if (Double.isNaN(coords[i])) { ! if (NCClient.SIM) System.err.println("coord isNaN i=" + i); return false; *************** *** 277,281 **** if (coords[i] > NCClient.MAX_DIST_FROM_ORIGIN || coords[i] < NEG_MAX_DIST_FROM_ORIGIN) { ! if (NCClient.SIMULATION) System.err.println("coord too far from origin i=" + i + " coord=" + coords[i]); --- 277,281 ---- if (coords[i] > NCClient.MAX_DIST_FROM_ORIGIN || coords[i] < NEG_MAX_DIST_FROM_ORIGIN) { ! if (NCClient.SIM) System.err.println("coord too far from origin i=" + i + " coord=" + coords[i]); --- NEW FILE: AppCoordClient.java --- package edu.harvard.syrah.pyxida.nc.lib; /* * Pyxida - a network coordinate library * * Copyright 2008 Jonathan Ledlie and Peter Pietzuch * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * * See the License for the specific language governing permissions and * limitations under the License. */ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; import java.util.logging.Logger; /** * A class that is responsible for updating the local Vivaldi coordinates, both * at the system and application level, and also maintaining the state of remote * hosts that support Vivaldi. * * @author Michael Parker, Jonathan Ledlie, Peter Pietzuch * * @param <T> * the type of the unique identifier of a host * * A note about time: Try to not use System.currentTimeMillis or * similar references to the system's time in this code. This code * should be able to run in simulation, where arbitrary times are * passed in as parameters. */ public class AppCoordClient<T> extends NCClient<T> { // Do an update if we've moved a third of the way to our nearest known neighbor. // Lowering this value leads to more frequent updates. // Should be 0.5 or less. public static double APP_UPDATE_THRESHOLD = 0.5; public static int WINDOW_SIZE = 64; protected Coordinate app_coord; protected WindowStatistic running_app_error; protected EWMAStatistic running_app_dd; protected EWMAStatistic running_neighbors_used; protected EWMAStatistic running_relative_diff; protected EWMAStatistic running_app_update_frequency; protected EWMAStatistic running_age; protected EWMAStatistic running_gravity; protected long time_of_last_app_update = -1; // note: not just part of statistics // this is returned to querier of our coords so he knows how stale they are protected long time_of_last_sys_update = -1; protected final ObserverList obs_list; protected Coordinate start_centroid; protected boolean updated_app_coord_at_least_once = false; protected final List<Coordinate> start_coords; protected final List<Coordinate> current_coords; protected Coordinate nearest_neighbor; //protected T local_addr; /** * Creates a new instance. Typically an application should only have one * instance of this class, as it only needs one set of Vivaldi coordinates. * * @param _num_dims * the number of Euclidian dimensions coordinates should have */ public AppCoordClient(int _num_dims) { // super.NCClient (_num_dims); app_coord = new Coordinate(num_dims); start_coords = new LinkedList<Coordinate>(); current_coords = new LinkedList<Coordinate>(); nearest_neighbor = null; if (KEEP_STATS) { running_app_update_frequency = new EWMAStatistic(); running_app_error = new WindowStatistic(RUNNING_STAT_HISTORY); running_sys_dd = new EWMAStatistic(); running_app_dd = new EWMAStatistic(); running_neighbors_used = new EWMAStatistic(); running_relative_diff = new EWMAStatistic(); running_age = new EWMAStatistic(); running_gravity = new EWMAStatistic(); } } // See Lua IMC 2005, Pietzuch WORLDS 2005, Ledlie ICDCS 2006 // for description of these statistics protected ApplicationStatistics computeApplicationStatistics() { ApplicationStatistics appStats = new ApplicationStatistics(); if (sys_coord.atOrigin() || neighbors == null || neighbors.size() == 0) return appStats; int rrl_wrong = 0; int rrl_count = 0; double narl_loss = 0; double narl_sum = 0; double ralp_loss = 0; double ralp_sum = 0; // TODO might want to use the app coord here so as to get the average // location for (Iterator<RemoteState<T>> i = neighbors.iterator(); i.hasNext();) { RemoteState<T> A_rs = i.next(); double A_rtt = A_rs.getSample(); double A_metric = sys_coord.distanceTo(A_rs.getLastCoordinate()); if (A_rtt > 0 && A_metric > 0) { for (Iterator<RemoteState<T>> j = neighbors.iterator(); j.hasNext();) { RemoteState<T> B_rs = j.next(); if (!A_rs.addr.equals(B_rs.addr)) { double B_rtt = B_rs.getSample(); double B_metric = sys_coord.distanceTo(B_rs.getLastCoordinate()); if (B_rtt > 0 && B_metric > 0) { double rtt_diff = Math.abs(A_rtt - B_rtt); rrl_count++; narl_sum += rtt_diff; if ((A_rtt > B_rtt && A_metric < B_metric) || (B_rtt > A_rtt && B_metric < A_metric)) { // oops coordinates have incorrectly ranked // these two guys rrl_wrong++; narl_loss += rtt_diff; } // relative latency penalty for using A, // which the metric says is closer, // when A is actually further away if (A_rtt > B_rtt && A_metric < B_metric) { ralp_loss += rtt_diff; ralp_sum += A_rtt; } if (B_rtt > A_rtt && B_metric < A_metric) { ralp_loss += rtt_diff; ralp_sum += B_rtt; } } } } } } appStats.validLinkCount = rrl_count; if (rrl_count > 0) appStats.rrl = rrl_wrong / (double) rrl_count; if (narl_sum > 0) appStats.narl = narl_loss / narl_sum; if (ralp_sum > 0) appStats.ralp = ralp_loss / ralp_sum; return appStats; } // poor man's public struct class ApplicationStatistics { double rrl = 0; double narl = 0; double ralp = 0; int validLinkCount = 0; public ApplicationStatistics() { }; } synchronized public String toString() { if (KEEP_STATS) { ApplicationStatistics appStats = computeApplicationStatistics(); return new String("[sc=" + sys_coord + ",ac=" + app_coord + ",er=" + nf.format(error) + ",sys_re50=" + nf.format(running_sys_error.getPercentile(.5)) + ",sys_re95=" + nf.format(running_sys_error.getPercentile(.95)) + ",app_re50=" + nf.format(running_app_error.getPercentile(.5)) + ",app_re95=" + nf.format(running_app_error.getPercentile(.95)) + ",sys_dd=" + nf.format(running_sys_dd.get()) + ",app_dd=" + nf.format(running_app_dd.get()) + ",ne=" + nf.format(running_neighbors_used.get()) + ",rd=" + nf.format(running_relative_diff.get()) + ",sf=" + nf.format(running_sys_update_frequency.get()) + ",af=" + nf.format(running_app_update_frequency.get()) + ",rrl=" + nf.format(appStats.rrl) + ",narl=" + nf.format(appStats.narl) + ",ralp=" + nf.format(appStats.ralp) + ",age=" + getAge(System.currentTimeMillis()) + ",vl=" + nf.format(appStats.validLinkCount) + ",gr=" + nf.format(running_gravity.get()) + ",nn=" + nf.format(sys_coord.distanceTo(nearest_neighbor)) + "]"); } else { return new String("[sc=" + sys_coord + ",ac=" + app_coord + ",er=" + nf.format(error) + ",nn=" + nf.format(sys_coord.distanceTo(nearest_neighbor)) + "]"); } } synchronized public Hashtable<String, Double> getStatistics() { Hashtable<String, Double> stats = new Hashtable<String, Double>(); for (int i = 0; i < num_dims; i++) { stats.put("sys_coord_" + i, sys_coord.coords[i]); stats.put("app_coord_" + i, sys_coord.coords[i]); } stats.put("er", error); stats.put("nn", sys_coord.distanceTo(nearest_neighbor)); if (KEEP_STATS) { ApplicationStatistics appStats = computeApplicationStatistics(); stats.put("rrl", appStats.rrl); stats.put("narl", appStats.narl); stats.put("ralp", appStats.ralp); stats.put("age", new Double(getAge(System.currentTimeMillis()))); stats.put("vl", new Double(appStats.validLinkCount)); stats.put("gr", running_gravity.get()); stats.put("sys_re50", running_sys_error.getPercentile(.5)); stats.put("sys_re95", running_sys_error.getPercentile(.95)); stats.put("app_re50", running_app_error.getPercentile(.5)); stats.put("app_re95", running_app_error.getPercentile(.95)); stats.put("sys_dd", running_sys_dd.get()); stats.put("app_dd", running_app_dd.get()); stats.put("ne", running_neighbors_used.get()); stats.put("rd", running_relative_diff.get()); stats.put("sf", running_sys_update_frequency.get()); stats.put("af", running_app_update_frequency.get()); } return stats; } synchronized public void reset() { super.reset(); app_coord.reset(); start_coords.clear(); current_coords.clear(); nearest_neighbor = null; } /** * Returns whether the application level coordinates have been updated at * least once. * * @return <code>true</code> if updated, <code>false</code> otherwise */ synchronized public boolean updatedYet() { return updated_app_coord_at_least_once; } /** * Returns the application-level Vivaldi coordinates. * * @return the application-level coordinates */ synchronized public Coordinate getApplicationCoords() { return new Coordinate(app_coord); } /** * This method is invoked when a new RTT sample is made to a host that * supports Vivaldi. This method succeeds and returns <code>true</code> only * if the host is already registered with this <code>VivaldiClient</code> * object, and the RTT sample is valid. * * @param addr * the address of the host * @param _r_coord * the system-level coordinates of the remote host * @param r_error * the system-level error of the remote host * @param sample_rtt * the RTT sample to the remote host * @param curr_time * the current time, in milliseconds * @param can_add * <code>true</code> if this method can add a host not already * present * @return <code>true</code> if <code>addr</code> is registered and the * sample is processed, <code>false</code> otherwise */ synchronized public boolean processSample(T addr, Coordinate _r_coord, double r_error, double sample_rtt, long sample_age, long curr_time, boolean can_add) { boolean didUpdate = super.processSample(addr, _r_coord, r_error, sample_rtt, sample_age, curr_time, can_add); if (didUpdate) { tryUpdateAppCoordinate(curr_time); } return didUpdate; } protected void updateError(T addr, Coordinate r_coord, double r_error, double smoothed_rtt, double sample_rtt, long sample_age, int sample_size, long curr_time) { super.updateError(addr, r_coord, r_error, smoothed_rtt, sample_rtt, sample_age, sample_size, curr_time); double app_distance = 0; if (app_coord != null) app_distance = app_coord.distanceTo(r_coord); if (app_distance == 0.) { if (DEBUG) logger.info("bad distance app " + app_distance); return; } double app_sample_error = Math.abs(app_distance - smoothed_rtt) / smoothed_rtt; if (KEEP_STATS) { running_app_error.add(app_sample_error); } } protected void tryUpdateAppCoordinate(long curr_time) { final double scale_factor = 1.0 / ((double) WINDOW_SIZE); // Make sure app coord always has a value. // calculate centroid of starting coordinates by averaging vectors if (start_coords.size() < WINDOW_SIZE) { Vec start_vec = new Vec(num_dims); start_coords.add(sys_coord); for (Iterator<Coordinate> i = start_coords.iterator(); i.hasNext();) { Coordinate next_coord = i.next(); start_vec.add(next_coord.asVectorFromZero(false)); } start_vec.scale(scale_factor); start_centroid = start_vec.asCoordinateFromZero(false); } current_coords.add(sys_coord); if (current_coords.size() > WINDOW_SIZE) { current_coords.remove(0); } // calculate centroid of current coordinates by averaging vectors Vec curr_vec = new Vec(num_dims); for (Iterator<Coordinate> i = current_coords.iterator(); i.hasNext();) { Coordinate next_coord = i.next(); curr_vec.add(next_coord.asVectorFromZero(false)); } curr_vec.scale(scale_factor); // create centroids Coordinate curr_centroid = curr_vec.asCoordinateFromZero(false); // get distances of centroids from nearest neighbor double start_dist = start_centroid.distanceTo(nearest_neighbor); double curr_dist = curr_centroid.distanceTo(nearest_neighbor); // fraction of space moved through, relative to distance to our NN double relative_diff = Math.abs((start_dist - curr_dist) / start_dist); if (KEEP_STATS) { running_relative_diff.add(relative_diff); } if (relative_diff > APP_UPDATE_THRESHOLD) { // exceed threshold, update application-level coordinate updated_app_coord_at_least_once = true; // clear coordinate windows start_coords.clear(); current_coords.clear(); } // This will keep updating the observers as we get rolling // until we've had one time when the coord windows differ boolean did_update = false; if (relative_diff > APP_UPDATE_THRESHOLD || !updated_app_coord_at_least_once) { if (KEEP_STATS) { double app_dd = app_coord.distanceTo(curr_centroid); running_app_dd.add(app_dd); } app_coord = curr_centroid; did_update = true; // If we've gotten ourselves into a situation where the coord // very accurate, stop always updating the app. // Currently can only use this if keep track of statistics if (KEEP_STATS) { final double MIN_SYS_ERROR_SIZE = (RUNNING_STAT_HISTORY / 8.); if (!updated_app_coord_at_least_once && running_sys_error.getSize() > MIN_SYS_ERROR_SIZE && running_sys_error.getPercentile(.5) < 0.20) { updated_app_coord_at_least_once = true; } } // notify observers of new application-level coordinate for (Iterator<ApplicationObserver> i = obs_list.iterator(); i.hasNext();) { ApplicationObserver obs = i.next(); obs.coordinatesUpdated(app_coord); } if (KEEP_STATS) { if (time_of_last_app_update > 0) { long since_last_app_update = curr_time - time_of_last_app_update; running_app_update_frequency.add(since_last_app_update); } time_of_last_app_update = curr_time; } } if (DEBUG) { System.out.println("app_coord update: done " + did_update + " rolling " + updated_app_coord_at_least_once + " start " + start_coords.size() + " current " + current_coords.size() + " diff " + nf.format(relative_diff)); } } } Index: NCClient.java =================================================================== RCS file: /cvsroot/pyxida/Pyxida/src/edu/harvard/syrah/pyxida/nc/lib/NCClient.java,v retrieving revision 1.13 retrieving revision 1.14 diff -C2 -d -r1.13 -r1.14 *** NCClient.java 21 Jan 2009 19:51:18 -0000 1.13 --- NCClient.java 22 Jan 2009 15:14:17 -0000 1.14 *************** *** 52,80 **** * passed in as parameters. */ ! public class NCClient<T> implements NCClientIF { ! ! // protected static edu.harvard.syrah.prp.Log crawler_log = ! // new edu.harvard.syrah.prp.Log(VivaldiClient.class); ! protected static Logger crawler_log = Logger.getLogger(NCClient.class.getName()); ! ! public static final boolean SIMULATION = false; ! [...1455 lines suppressed...] *************** *** 1289,1292 **** --- 867,873 ---- } + /* (non-Javadoc) + * @see edu.harvard.syrah.pyxida.nc.lib.NCClientIF#startUp(java.io.DataInputStream) + */ public void startUp(DataInputStream is) throws IOException { *************** *** 1303,1306 **** --- 884,890 ---- } + /* (non-Javadoc) + * @see edu.harvard.syrah.pyxida.nc.lib.NCClientIF#shutDown(java.io.DataOutputStream) + */ synchronized public void shutDown(DataOutputStream os) throws IOException { // could also save a number of neighbors |