Browse Source

Add a function to compute millisecond time difference quickly.

Our main function, though accurate on all platforms, can be very
slow on 32-bit hosts.  This one is faster on all 32-bit hosts, and
accurate everywhere except apple, where it will typically be off by
1%.  But since 32-bit apple is a relic anyway, I think we should be
fine.
Nick Mathewson 6 years ago
parent
commit
9abf541f7f
4 changed files with 84 additions and 5 deletions
  1. 50 0
      src/common/compat_time.c
  2. 27 0
      src/common/compat_time.h
  3. 2 5
      src/or/circuitmux_ewma.c
  4. 5 0
      src/test/test_util.c

+ 50 - 0
src/common/compat_time.c

@@ -279,6 +279,7 @@ monotime_reset_ratchets_for_testing(void)
  * nanoseconds.
  */
 static struct mach_timebase_info mach_time_info;
+static struct mach_timebase_info mach_time_info_msec_cvt;
 static int monotime_shift = 0;
 
 static void
@@ -296,6 +297,14 @@ monotime_init_internal(void)
     // requires that tor_log2(0) == 0.
     monotime_shift = tor_log2(ms_per_tick);
   }
+  {
+    // For converting ticks to milliseconds in a 32-bit-friendly way, we
+    // will first right-shift by 20, and then multiply by 20/19, since
+    // (1<<20) * 19/20 is about 1e6.  We precompute a new numerate and
+    // denominator here to avoid multiple multiplies.
+    mach_time_info_msec_cvt.numer = mach_time_info.numer * 20;
+    mach_time_info_msec_cvt.denom = mach_time_info.denom * 19;
+  }
 }
 
 /**
@@ -345,6 +354,22 @@ monotime_diff_nsec(const monotime_t *start,
   return diff_nsec;
 }
 
+int32_t
+monotime_coarse_diff_msec32_(const monotime_coarse_t *start,
+                             const monotime_coarse_t *end)
+{
+  if (BUG(mach_time_info.denom == 0)) {
+    monotime_init();
+  }
+  const int64_t diff_ticks = end->abstime_ - start->abstime_;
+
+  /* We already require in di_ops.c that right-shift performs a sign-extend. */
+  const int32_t diff_microticks = (int32_t)(diff_ticks >> 20);
+
+  return (diff_microticks * mach_time_info_msec_cvt.numer) /
+    mach_time_info_msec_cvt.denom;
+}
+
 uint32_t
 monotime_coarse_to_stamp(const monotime_coarse_t *t)
 {
@@ -443,6 +468,15 @@ monotime_diff_nsec(const monotime_t *start,
   return diff_nsec;
 }
 
+int32_t
+monotime_coarse_diff_msec32_(const monotime_coarse_t *start,
+                             const monotime_coarse_t *end)
+{
+  const int32_t diff_sec = (int32_t)(end->ts_.tv_sec - start->ts_.tv_sec);
+  const int32_t diff_nsec = (int32_t)(end->ts_.tv_nsec - start->ts_.tv_nsec);
+  return diff_sec * 1000 + diff_nsec / ONE_MILLION;
+}
+
 /* This value is ONE_BILLION >> 20. */
 static const uint32_t STAMP_TICKS_PER_SECOND = 953;
 
@@ -592,6 +626,13 @@ monotime_coarse_diff_msec(const monotime_coarse_t *start,
   return diff_ticks;
 }
 
+int32_t
+monotime_coarse_diff_msec32_(const monotime_coarse_t *start,
+                             const monotime_coarse_t *end)
+{
+  return (int32_t)monotime_coarse_diff_msec(start, end)
+}
+
 int64_t
 monotime_coarse_diff_usec(const monotime_coarse_t *start,
                           const monotime_coarse_t *end)
@@ -677,6 +718,15 @@ monotime_diff_nsec(const monotime_t *start,
   return (diff.tv_sec * ONE_BILLION + diff.tv_usec * 1000);
 }
 
+int32_t
+monotime_coarse_diff_msec32_(const monotime_coarse_t *start,
+                             const monotime_coarse_t *end)
+{
+  struct timeval diff;
+  timersub(&end->tv_, &start->tv_, &diff);
+  return diff.tv_sec * 1000 + diff.tv_usec / 1000;
+}
+
 /* This value is ONE_MILLION >> 10. */
 static const uint32_t STAMP_TICKS_PER_SECOND = 976;
 

+ 27 - 0
src/common/compat_time.h

@@ -173,6 +173,33 @@ void monotime_coarse_add_msec(monotime_coarse_t *out,
 #define monotime_coarse_add_msec monotime_add_msec
 #endif /* defined(MONOTIME_COARSE_TYPE_IS_DIFFERENT) */
 
+/**
+ * As monotime_coarse_diff_msec, but avoid 64-bit division.
+ *
+ * Requires that the difference fit into an int32_t; not for use with
+ * large time differences.
+ */
+int32_t monotime_coarse_diff_msec32_(const monotime_coarse_t *start,
+                                     const monotime_coarse_t *end);
+
+/**
+ * As monotime_coarse_diff_msec, but avoid 64-bit division if it is expensive.
+ *
+ * Requires that the difference fit into an int32_t; not for use with
+ * large time differences.
+ */
+static inline int32_t
+monotime_coarse_diff_msec32(const monotime_coarse_t *start,
+                            const monotime_coarse_t *end)
+{
+#if SIZEOF_VOID_P == 8
+  // on a 64-bit platform, let's assume 64/64 division is cheap.
+  return (int32_t) monotime_coarse_diff_msec(start, end);
+#else
+  return monotime_coarse_diff_msec32_(start, end);
+#endif
+}
+
 MOCK_DECL(void, tor_gettimeofday, (struct timeval *timeval));
 
 #ifdef TOR_UNIT_TESTS

+ 2 - 5
src/or/circuitmux_ewma.c

@@ -626,11 +626,8 @@ cell_ewma_get_current_tick_and_fraction(double *remainder_out)
   }
   monotime_coarse_t now;
   monotime_coarse_get(&now);
-  // XXXX this does a division operation that can be slow on 32-bit
-  // XXXX systems.
-  int32_t msec_diff =
-    (int32_t)monotime_coarse_diff_msec(&start_of_current_tick,
-                                       &now);
+  int32_t msec_diff = monotime_coarse_diff_msec32(&start_of_current_tick,
+                                                  &now);
   if (msec_diff > (1000*EWMA_TICK_LEN)) {
     unsigned ticks_difference = msec_diff / (1000*EWMA_TICK_LEN);
     monotime_coarse_add_msec(&start_of_current_tick,

+ 5 - 0
src/test/test_util.c

@@ -6035,6 +6035,9 @@ test_util_monotonic_time_add_msec(void *arg)
   monotime_coarse_add_msec(&ct2, &ct1, 1337);
   tt_i64_op(monotime_diff_msec(&t1, &t2), OP_EQ, 1337);
   tt_i64_op(monotime_coarse_diff_msec(&ct1, &ct2), OP_EQ, 1337);
+  // The 32-bit variant must be within 1% of the regular one.
+  tt_int_op(monotime_coarse_diff_msec32_(&ct1, &ct2), OP_GT, 1323);
+  tt_int_op(monotime_coarse_diff_msec32_(&ct1, &ct2), OP_LT, 1350);
 
   /* Add 1337 msec twice more; make sure that any second rollover issues
    * worked. */
@@ -6044,6 +6047,8 @@ test_util_monotonic_time_add_msec(void *arg)
   monotime_coarse_add_msec(&ct2, &ct2, 1337);
   tt_i64_op(monotime_diff_msec(&t1, &t2), OP_EQ, 1337*3);
   tt_i64_op(monotime_coarse_diff_msec(&ct1, &ct2), OP_EQ, 1337*3);
+  tt_int_op(monotime_coarse_diff_msec32_(&ct1, &ct2), OP_GT, 3970);
+  tt_int_op(monotime_coarse_diff_msec32_(&ct1, &ct2), OP_LT, 4051);
 
  done:
   ;