Meter.hpp 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  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. * 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. {}
  43. /**
  44. * Add a measurement
  45. *
  46. * @param now Current time
  47. * @param count Count of items (usually bytes)
  48. */
  49. ZT_INLINE void log(const int64_t now, uint64_t count) noexcept
  50. {
  51. // We log by choosing a log bucket based on the current time in units modulo
  52. // the log size and then if it's a new bucket setting it or otherwise adding
  53. // to it.
  54. const unsigned long bucket = ((unsigned long)(now / TUNIT)) % LSIZE;
  55. if (m_bucket.exchange(bucket) != bucket) {
  56. m_totalExclCounts.fetch_add(m_counts[bucket].exchange(count));
  57. } else {
  58. m_counts[bucket].fetch_add(count);
  59. }
  60. }
  61. /**
  62. * Get rate per TUNIT time
  63. *
  64. * @param now Current time
  65. * @param rate Result parameter: rate in count/TUNIT
  66. * @param total Total count for life of object
  67. */
  68. ZT_INLINE void rate(double &rate, uint64_t &total) const noexcept
  69. {
  70. total = 0;
  71. for (unsigned long i = 0;i < LSIZE;++i)
  72. total += m_counts[i].load();
  73. rate = (double) total / (double) LSIZE;
  74. total += m_totalExclCounts.load();
  75. }
  76. private:
  77. std::atomic<uint64_t> m_counts[LSIZE];
  78. std::atomic<uint64_t> m_totalExclCounts;
  79. std::atomic<unsigned long> m_bucket;
  80. };
  81. } // namespace ZeroTier
  82. #endif