Meter.hpp 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  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: 2025-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. * This class is lock-free and thread-safe.
  23. *
  24. * This maintains a set of buckets numbered according to the current time
  25. * modulo TUNIT. Each bucket is incremented within that time window. When
  26. * the time moves on to a new bucket, its old contents are added to a
  27. * total accumulator and a new counter for that bucket starts.
  28. *
  29. * @tparam TUNIT Unit of time in milliseconds (default: 1000 for one second)
  30. * @tparam LSIZE Log size in units of time (default: 10 for 10s worth of data)
  31. */
  32. template<int64_t TUNIT = 1000, unsigned long LSIZE = 10>
  33. class Meter
  34. {
  35. public:
  36. /**
  37. * Create and initialize a new meter
  38. *
  39. * @param now Start time
  40. */
  41. ZT_INLINE Meter() noexcept :
  42. m_counts(),
  43. m_totalExclCounts(0),
  44. m_bucket(0)
  45. {}
  46. /**
  47. * Add a measurement
  48. *
  49. * @param now Current time
  50. * @param count Count of items (usually bytes)
  51. */
  52. ZT_INLINE void log(const int64_t now, uint64_t count) noexcept
  53. {
  54. // We log by choosing a log bucket based on the current time in units modulo
  55. // the log size and then if it's a new bucket setting it or otherwise adding
  56. // to it.
  57. const unsigned long bucket = ((unsigned long)(now / TUNIT)) % LSIZE;
  58. if (m_bucket.exchange(bucket) != bucket) {
  59. m_totalExclCounts.fetch_add(m_counts[bucket].exchange(count));
  60. } else {
  61. m_counts[bucket].fetch_add(count);
  62. }
  63. }
  64. /**
  65. * Get rate per TUNIT time
  66. *
  67. * @param now Current time
  68. * @param rate Result parameter: rate in count/TUNIT
  69. * @param total Total count for life of object
  70. */
  71. ZT_INLINE void rate(double &rate, uint64_t &total) const noexcept
  72. {
  73. total = 0;
  74. for (unsigned long i = 0;i < LSIZE;++i)
  75. total += m_counts[i].load();
  76. rate = (double) total / (double) LSIZE;
  77. total += m_totalExclCounts.load();
  78. }
  79. private:
  80. std::atomic<uint64_t> m_counts[LSIZE];
  81. std::atomic<uint64_t> m_totalExclCounts;
  82. std::atomic<unsigned long> m_bucket;
  83. };
  84. } // namespace ZeroTier
  85. #endif