Meter.hpp 2.3 KB

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