Change trylock() to try_lock() in folly::SpinLock to conform to standard Lockable.
[folly.git] / folly / detail / Stats.h
index b3fa7e68ea7300d39876871349895f49cef50817..691ad2da351d108f912a969d5d8a926cc01ce841 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2012 Facebook, Inc.
+ * Copyright 2017 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * limitations under the License.
  */
 
-#ifndef FOLLY_DETAIL_STATS_H_
-#define FOLLY_DETAIL_STATS_H_
+#pragma once
 
+#include <chrono>
 #include <cstdint>
+#include <type_traits>
 
 namespace folly { namespace detail {
 
 /*
- * Helper functions for how to perform division based on the desired
+ * Helper function to compute the average, given a specified input type and
  * return type.
  */
 
-// For floating point input types, do floating point division
-template <typename ReturnType, typename ValueType>
-typename std::enable_if<std::is_floating_point<ValueType>::value,
-                        ReturnType>::type
-avgHelper(ValueType sum, uint64_t count) {
+// If the input is long double, divide using long double to avoid losing
+// precision.
+template <typename ReturnType>
+ReturnType avgHelper(long double sum, uint64_t count) {
   if (count == 0) { return ReturnType(0); }
-  return static_cast<ReturnType>(sum / count);
+  const long double countf = count;
+  return static_cast<ReturnType>(sum / countf);
 }
 
-// For floating point return types, do floating point division
+// In all other cases divide using double precision.
+// This should be relatively fast, and accurate enough for most use cases.
 template <typename ReturnType, typename ValueType>
-typename std::enable_if<std::is_floating_point<ReturnType>::value &&
-                        !std::is_floating_point<ValueType>::value,
+typename std::enable_if<!std::is_same<typename std::remove_cv<ValueType>::type,
+                                      long double>::value,
                         ReturnType>::type
 avgHelper(ValueType sum, uint64_t count) {
   if (count == 0) { return ReturnType(0); }
-  return static_cast<ReturnType>(sum) / count;
+  const double sumf = double(sum);
+  const double countf = double(count);
+  return static_cast<ReturnType>(sumf / countf);
 }
 
-// For signed integer input types, do signed division
-template <typename ReturnType, typename ValueType>
-typename std::enable_if<!std::is_floating_point<ReturnType>::value &&
-                        !std::is_floating_point<ValueType>::value &&
-                        std::is_signed<ValueType>::value,
-                        ReturnType>::type
-avgHelper(ValueType sum, uint64_t count) {
-  if (count == 0) { return ReturnType(0); }
-  return sum / static_cast<int64_t>(count);
-}
+/*
+ * Helper function to compute the rate per Interval,
+ * given the specified count recorded over the elapsed time period.
+ */
+template <
+    typename ReturnType = double,
+    typename Duration = std::chrono::seconds,
+    typename Interval = Duration>
+ReturnType rateHelper(ReturnType count, Duration elapsed) {
+  if (elapsed == Duration(0)) {
+    return 0;
+  }
 
-// For unsigned integer input types, do unsigned division
-template <typename ReturnType, typename ValueType>
-typename std::enable_if<!std::is_floating_point<ReturnType>::value &&
-                        !std::is_floating_point<ValueType>::value &&
-                        std::is_unsigned<ValueType>::value,
-                        ReturnType>::type
-avgHelper(ValueType sum, uint64_t count) {
-  if (count == 0) { return ReturnType(0); }
-  return sum / count;
+  // Use std::chrono::duration_cast to convert between the native
+  // duration and the desired interval.  However, convert the rates,
+  // rather than just converting the elapsed duration.  Converting the
+  // elapsed time first may collapse it down to 0 if the elapsed interval
+  // is less than the desired interval, which will incorrectly result in
+  // an infinite rate.
+  typedef std::chrono::duration<
+      ReturnType,
+      std::ratio<Duration::period::den, Duration::period::num>>
+      NativeRate;
+  typedef std::chrono::duration<
+      ReturnType, std::ratio<Interval::period::den,
+                             Interval::period::num>> DesiredRate;
+
+  NativeRate native(count / elapsed.count());
+  DesiredRate desired = std::chrono::duration_cast<DesiredRate>(native);
+  return desired.count();
 }
 
 
@@ -110,5 +124,3 @@ struct Bucket {
 };
 
 }} // folly::detail
-
-#endif // FOLLY_DETAIL_STATS_H_