Meter.hpp 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. /*
  2. * Copyright (c)2013-2020 ZeroTier, Inc.
  3. *
  4. * Use of this software is governed by the Business Source License included
  5. * in the LICENSE.TXT file in the project's root directory.
  6. *
  7. * Change Date: 2024-01-01
  8. *
  9. * On the date above, in accordance with the Business Source License, use
  10. * of this software will be governed by version 2.0 of the Apache License.
  11. */
  12. /****/
  13. #ifndef ZT_METER_HPP
  14. #define ZT_METER_HPP
  15. #include "Constants.hpp"
  16. #include "Mutex.hpp"
  17. #include <algorithm>
  18. namespace ZeroTier {
  19. /**
  20. * Transfer rate and total transferred amount meter
  21. *
  22. * @tparam TUNIT Unit of time in milliseconds (default: 1000 for one second)
  23. * @tparam LSIZE Log size in units of time (default: 60 for one minute worth of data)
  24. */
  25. template<int64_t TUNIT = 1000,unsigned long LSIZE = 60>
  26. class Meter
  27. {
  28. public:
  29. /**
  30. * Create and initialize a new meter
  31. *
  32. * @param now Start time
  33. */
  34. ZT_ALWAYS_INLINE Meter(const int64_t now) noexcept : startTime(now) {}
  35. /**
  36. * Add a measurement
  37. *
  38. * @tparam I Type of 'count' (usually inferred)
  39. * @param now Current time
  40. * @param count Count of items (usually bytes)
  41. */
  42. template<typename I>
  43. ZT_ALWAYS_INLINE void log(const int64_t now,I count) noexcept
  44. {
  45. _total += (uint64_t)count;
  46. // We log by choosing a log bucket based on the current time in units modulo
  47. // the log size and then if it's a new bucket setting it or otherwise adding
  48. // to it.
  49. const unsigned long bucket = ((unsigned int)((uint64_t)(now / TUNIT))) % LSIZE;
  50. if (_bucket.exchange(bucket) != bucket)
  51. _counts[bucket].store((uint64_t)count);
  52. else _counts[bucket].fetch_add((uint64_t)count);
  53. }
  54. /**
  55. * Get rate per TUNIT time
  56. *
  57. * @param now Current time
  58. * @return Count per TUNIT time (rate)
  59. */
  60. ZT_ALWAYS_INLINE double rate(const int64_t now) const noexcept
  61. {
  62. // Rate is computed by looking back at N buckets where N is the smaller of
  63. // the size of the log or the number of units since the start time.
  64. const unsigned long lookback = std::min((unsigned long)((now - startTime) / TUNIT),LSIZE);
  65. if (lookback == 0)
  66. return 0.0;
  67. unsigned long bi = ((unsigned int)((uint64_t)(now / TUNIT)));
  68. double sum = 0.0;
  69. for(unsigned long l=0;l<lookback;++l)
  70. sum += (double)_counts[bi-- % LSIZE].load();
  71. return sum / (double)lookback;
  72. }
  73. /**
  74. * @return Total count since meter was created
  75. */
  76. ZT_ALWAYS_INLINE uint64_t total() const noexcept { return _total.load(); }
  77. private:
  78. const int64_t startTime;
  79. std::atomic<uint64_t> _total;
  80. std::atomic<uint64_t> _counts[LSIZE];
  81. std::atomic<unsigned long> _bucket;
  82. };
  83. } // namespace ZeroTier
  84. #endif