/*
- * 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();
}
};
}} // folly::detail
-
-#endif // FOLLY_DETAIL_STATS_H_