[or-cvs] [tor/master] Favor quiet circuits when choosing which order to relay cells in.

Nick Mathewson nickm at seul.org
Sat Dec 19 03:34:45 UTC 2009


Author: Can Tang <c24tang at uwaterloo.ca>
Date: Thu, 10 Dec 2009 11:12:42 -0500
Subject: Favor quiet circuits when choosing which order to relay cells in.
Commit: d3be00e0f454998db6387c8547d218a0db93db21

Each circuit is ranked in terms of how many cells from it have been
relayed recently, using a time-weighted average.

This patch has been tested this on a private Tor network on PlanetLab,
and gotten improvements of 12-35% in time it takes to fetch a small
web page while there's a simultaneous large data transfer going on
simultaneously.

[Commit msg by nickm based on mail from Ian Goldberg.]
---
 src/or/circuitlist.c |   12 +++++
 src/or/config.c      |    5 ++
 src/or/connection.c  |   28 ++++++++++--
 src/or/or.h          |   32 +++++++++++++
 src/or/relay.c       |  120 +++++++++++++++++++++++++++++++++++++++++++++++++-
 5 files changed, 191 insertions(+), 6 deletions(-)

diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c
index 2c949de..eb8c90f 100644
--- a/src/or/circuitlist.c
+++ b/src/or/circuitlist.c
@@ -385,6 +385,10 @@ init_circuit_base(circuit_t *circ)
   circ->package_window = circuit_initial_package_window();
   circ->deliver_window = CIRCWINDOW_START;
 
+  /* Initialize the cell_ewma_t structure */
+  circ->n_cell_ewma.last_cell_time = circ->highres_created;
+  circ->n_cell_ewma.cell_count = 0.0;
+
   circuit_add(circ);
 }
 
@@ -432,6 +436,14 @@ or_circuit_new(circid_t p_circ_id, or_connection_t *p_conn)
 
   init_circuit_base(TO_CIRCUIT(circ));
 
+  /* Initialize the cell_ewma_t structure */
+
+  /* Fetch the timeval that init_circuit_base filled in. */
+  circ->p_cell_ewma.last_cell_time = TO_CIRCUIT(circ)->highres_created;
+
+  /* Initialize the cell counts to 0 */
+  circ->p_cell_ewma.cell_count = 0.0;
+
   return circ;
 }
 
diff --git a/src/or/config.c b/src/or/config.c
index deeda16..fe5fe9f 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -355,6 +355,11 @@ static config_var_t _option_vars[] = {
   VAR("__HashedControlSessionPassword", LINELIST, HashedControlSessionPassword,
       NULL),
   V(MinUptimeHidServDirectoryV2, INTERVAL, "24 hours"),
+
+  /* Options for EWMA selection of circuit to write from */
+  VAR("EWMASignificance", DOUBLE, EWMASignificance, "-1.0"),
+  VAR("EWMAInterval", DOUBLE, EWMAInterval, "-1.0"),
+
   { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
 };
 
diff --git a/src/or/connection.c b/src/or/connection.c
index 0ff1cc5..bd12e36 100644
--- a/src/or/connection.c
+++ b/src/or/connection.c
@@ -2275,8 +2275,8 @@ connection_read_bucket_should_increase(or_connection_t *conn)
  * Mark the connection and return -1 if you want to close it, else
  * return 0.
  */
-int
-connection_handle_read(connection_t *conn)
+static int
+connection_handle_read_impl(connection_t *conn)
 {
   int max_to_read=-1, try_to_read;
   size_t before, n_read = 0;
@@ -2371,6 +2371,17 @@ loop_again:
   return 0;
 }
 
+int
+connection_handle_read(connection_t *conn)
+{
+  int res;
+
+  tor_gettimeofday_cache_clear();
+  res = connection_handle_read_impl(conn);
+  return res;
+  
+}
+
 /** Pull in new bytes from conn-\>s or conn-\>linked_conn onto conn-\>inbuf,
  * either directly or via TLS. Reduce the token buckets by the number of bytes
  * read.
@@ -2572,8 +2583,8 @@ connection_outbuf_too_full(connection_t *conn)
  * Mark the connection and return -1 if you want to close it, else
  * return 0.
  */
-int
-connection_handle_write(connection_t *conn, int force)
+static int
+connection_handle_write_impl(connection_t *conn, int force)
 {
   int e;
   socklen_t len=(socklen_t)sizeof(e);
@@ -2740,6 +2751,15 @@ connection_handle_write(connection_t *conn, int force)
   return 0;
 }
 
+int
+connection_handle_write(connection_t *conn, int force)
+{
+    int res;
+    tor_gettimeofday_cache_clear();
+    res = connection_handle_write_impl(conn, force);
+    return res;
+}
+
 /** OpenSSL TLS record size is 16383; this is close. The goal here is to
  * push data out as soon as we know there's enough for a TLS record, so
  * during periods of high load we won't read entire megabytes from
diff --git a/src/or/or.h b/src/or/or.h
index 2e575f5..003d0eb 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -1992,6 +1992,20 @@ typedef struct {
   time_t expiry_time;
 } cpath_build_state_t;
 
+/**
+ * The cell_ewma_t structure keeps track of how many cells a circuit has
+ * transferred recently.  It keeps an EWMA (exponentially weighted
+ * moving average) of the number of cells flushed in
+ * connection_or_flush_from_first_active_circuit().
+ */
+
+typedef struct {
+    /** The last time a cell was flushed from this circuit. */
+    struct timeval last_cell_time;
+    /** The EWMA of the cell count. */
+    double cell_count;
+} cell_ewma_t;
+
 #define ORIGIN_CIRCUIT_MAGIC 0x35315243u
 #define OR_CIRCUIT_MAGIC 0x98ABC04Fu
 
@@ -2081,6 +2095,10 @@ typedef struct circuit_t {
 
   /** Unique ID for measuring tunneled network status requests. */
   uint64_t dirreq_id;
+
+  /** The EWMA count for the number of cells flushed from the
+   * n_conn_cells queue. */
+  cell_ewma_t n_cell_ewma;
 } circuit_t;
 
 /** Largest number of relay_early cells that we can send on a given
@@ -2212,6 +2230,10 @@ typedef struct or_circuit_t {
    * exit-ward queues of this circuit; reset every time when writing
    * buffer stats to disk. */
   uint64_t total_cell_waiting_time;
+
+  /** The EWMA count for the number of cells flushed from the
+   * p_conn_cells queue. */
+  cell_ewma_t p_cell_ewma;
 } or_circuit_t;
 
 /** Convert a circuit subtype to a circuit_t.*/
@@ -2740,6 +2762,14 @@ typedef struct {
    * to make this false. */
   int ReloadTorrcOnSIGHUP;
 
+  /* The EWMA parameters for circuit selection within a connection.
+   * The most recent EWMAInterval seconds will account for an
+   * EWMASignificance (between 0 and 1) portion of the weight.
+   * If these values are negative, use the global defaults (soon to be
+   * set in the consensus). */
+  double EWMASignificance;
+  double EWMAInterval;
+
 } or_options_t;
 
 /** Persistent state for an onion router, as saved to disk. */
@@ -5122,5 +5152,7 @@ int rend_parse_introduction_points(rend_service_descriptor_t *parsed,
                                    size_t intro_points_encoded_size);
 int rend_parse_client_keys(strmap_t *parsed_clients, const char *str);
 
+void tor_gettimeofday_cache_clear(void);
+
 #endif
 
diff --git a/src/or/relay.c b/src/or/relay.c
index ac305ce..147412f 100644
--- a/src/or/relay.c
+++ b/src/or/relay.c
@@ -10,6 +10,7 @@
  *    receiving from circuits, plus queuing on circuits.
  **/
 
+#include <math.h>
 #include "or.h"
 #include "mempool.h"
 
@@ -35,6 +36,26 @@ circuit_resume_edge_reading_helper(edge_connection_t *conn,
 static int
 circuit_consider_stop_edge_reading(circuit_t *circ, crypt_path_t *layer_hint);
 
+/** Cache the current hi-res time; the cache gets reset when libevent
+ * calls us. */
+
+static struct timeval cached_time_hires = {0, 0};
+
+static void
+tor_gettimeofday_cached(struct timeval *tv)
+{
+  if (cached_time_hires.tv_sec == 0) {
+    tor_gettimeofday(&cached_time_hires);
+  }
+  *tv = cached_time_hires;
+}
+
+void
+tor_gettimeofday_cache_clear(void)
+{
+    cached_time_hires.tv_sec = 0;
+}
+
 /** Stats: how many relay cells have originated at this hop, or have
  * been relayed onward (not recognized at this hop)?
  */
@@ -1633,7 +1654,7 @@ cell_queue_append_packed_copy(cell_queue_t *queue, const cell_t *cell)
     insertion_time_queue_t *it_queue = queue->insertion_times;
     if (!it_pool)
       it_pool = mp_pool_new(sizeof(insertion_time_elem_t), 1024);
-    tor_gettimeofday(&now);
+    tor_gettimeofday_cached(&now);
 #define SECONDS_IN_A_DAY 86400L
     added = (uint32_t)(((now.tv_sec % SECONDS_IN_A_DAY) * 100L)
             + ((uint32_t)now.tv_usec / (uint32_t)10000L));
@@ -1857,9 +1878,101 @@ connection_or_flush_from_first_active_circuit(or_connection_t *conn, int max,
   cell_queue_t *queue;
   circuit_t *circ;
   int streams_blocked;
+
+  /* The current (hi-res) time */
+  struct timeval now_hires;
+
+  /* The EWMA cell counter for the circuit we're flushing. */
+  cell_ewma_t *cell_ewma = NULL;
+
+  /* The global cell EWMA parameters.  The algorithm is parameterized by
+   * two values (type double):
+   *
+   * "significance" (between 0 and 1) and "interval"
+   *
+   * The cell count is weighted so that the most recent "interval"
+   * seconds will account for "significance" of the weight.
+   *
+   * If "interval" is set to 0, it disables the algorithm, and the old
+   * algorithm (round-robin) is used.
+   *
+   * These parameters should really be set by the consensus, but can be
+   * overridden by the torrc (in which case the options values will be
+   * >= 0.0).
+   */
+  static double cell_ewma_significance = 0.9;
+  static double cell_ewma_interval = 10.0;
+
+  double significance_override = get_options()->EWMASignificance;
+  double interval_override = get_options()->EWMAInterval;
+  if (significance_override >= 0.0) {
+    cell_ewma_significance = significance_override;
+  }
+  if (interval_override >= 0.0) {
+    cell_ewma_interval = interval_override;
+  }
+
   circ = conn->active_circuits;
   if (!circ) return 0;
   assert_active_circuits_ok_paranoid(conn);
+
+  /* See if we're doing the ewma circuit selection algorithm. */
+  if (cell_ewma_interval > 0.0) {
+    /* Is there another circuit we might like better? */
+    circuit_t *circ_iter, *circ_start;
+    circuit_t *circ_min_cell_count = NULL;
+    double min_cell_count = 0.0;
+    tor_gettimeofday_cached(&now_hires);
+
+    /* Start with circ, and go around the circular linked list */
+    circ_start = circ_iter = circ;
+    do {
+      double delta_t;
+
+      /* Find the appropriate EWMA cell counter to use. */
+      if (circ_iter->n_conn == conn) {
+        cell_ewma = &(circ_iter->n_cell_ewma);
+      } else {
+        cell_ewma = &(TO_OR_CIRCUIT(circ_iter)->p_cell_ewma);
+      }
+
+      /* Update the EWMA cell counter to account for the passage of time. */
+      delta_t = (double)(now_hires.tv_sec -
+			    cell_ewma->last_cell_time.tv_sec);
+      delta_t += ((double)(now_hires.tv_usec -
+			    cell_ewma->last_cell_time.tv_usec)) / 1000000.0;
+
+      if (delta_t > 0.0) {
+        cell_ewma->cell_count *=
+	    pow(cell_ewma_significance, delta_t / cell_ewma_interval);
+		//printf("cc: %f ", cell_ewma->cell_count);
+      }
+      cell_ewma->last_cell_time = now_hires;
+
+      /* Now keep track of the lowest cell count we've seen. */
+      if (circ_min_cell_count == NULL ||
+	    cell_ewma->cell_count < min_cell_count) {
+		min_cell_count = cell_ewma->cell_count;
+		circ_min_cell_count = circ_iter;
+      }
+      
+      circ_iter = *next_circ_on_conn_p(circ_iter, conn);
+    } while (circ_iter != circ_start);
+
+    /* OK, we've gone all the way around.  Let's use the circ with the
+     * lowest (recent) cell count. */
+    circ = circ_min_cell_count;
+
+    /* Now set the appropriate EWMA cell counter to use below to add the
+     * cells we actually send. */
+    if (circ_min_cell_count->n_conn == conn) {
+      cell_ewma = &(circ_min_cell_count->n_cell_ewma);
+    } else {
+      cell_ewma = &(TO_OR_CIRCUIT(circ_min_cell_count)->p_cell_ewma);
+    }
+  }
+  
+
   if (circ->n_conn == conn) {
     queue = &circ->n_conn_cells;
     streams_blocked = circ->streams_blocked_on_n_conn;
@@ -1879,7 +1992,7 @@ connection_or_flush_from_first_active_circuit(or_connection_t *conn, int max,
       uint32_t flushed;
       uint32_t cell_waiting_time;
       insertion_time_queue_t *it_queue = queue->insertion_times;
-      tor_gettimeofday(&now);
+      tor_gettimeofday_cached(&now);
       flushed = (uint32_t)((now.tv_sec % SECONDS_IN_A_DAY) * 100L +
                  (uint32_t)now.tv_usec / (uint32_t)10000L);
       if (!it_queue || !it_queue->first) {
@@ -1915,6 +2028,9 @@ connection_or_flush_from_first_active_circuit(or_connection_t *conn, int max,
 
     packed_cell_free_unchecked(cell);
     ++n_flushed;
+    if (cell_ewma) {
+      cell_ewma->cell_count += 1.0;
+    }
     if (circ != conn->active_circuits) {
       /* If this happens, the current circuit just got made inactive by
        * a call in connection_write_to_buf().  That's nothing to worry about:
-- 
1.5.6.5




More information about the tor-commits mailing list