You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

hashtablez_sampler.h 13 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. // Copyright 2018 The Abseil Authors.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // https://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. //
  15. // -----------------------------------------------------------------------------
  16. // File: hashtablez_sampler.h
  17. // -----------------------------------------------------------------------------
  18. //
  19. // This header file defines the API for a low level library to sample hashtables
  20. // and collect runtime statistics about them.
  21. //
  22. // `HashtablezSampler` controls the lifecycle of `HashtablezInfo` objects which
  23. // store information about a single sample.
  24. //
  25. // `Record*` methods store information into samples.
  26. // `Sample()` and `Unsample()` make use of a single global sampler with
  27. // properties controlled by the flags hashtablez_enabled,
  28. // hashtablez_sample_rate, and hashtablez_max_samples.
  29. //
  30. // WARNING
  31. //
  32. // Using this sampling API may cause sampled Swiss tables to use the global
  33. // allocator (operator `new`) in addition to any custom allocator. If you
  34. // are using a table in an unusual circumstance where allocation or calling a
  35. // linux syscall is unacceptable, this could interfere.
  36. //
  37. // This utility is internal-only. Use at your own risk.
  38. #ifndef ABSL_CONTAINER_INTERNAL_HASHTABLEZ_SAMPLER_H_
  39. #define ABSL_CONTAINER_INTERNAL_HASHTABLEZ_SAMPLER_H_
  40. #include <atomic>
  41. #include <functional>
  42. #include <memory>
  43. #include <vector>
  44. #include "absl/base/config.h"
  45. #include "absl/base/internal/per_thread_tls.h"
  46. #include "absl/base/optimization.h"
  47. #include "absl/profiling/internal/sample_recorder.h"
  48. #include "absl/synchronization/mutex.h"
  49. #include "absl/utility/utility.h"
  50. namespace absl
  51. {
  52. ABSL_NAMESPACE_BEGIN
  53. namespace container_internal
  54. {
  55. // Stores information about a sampled hashtable. All mutations to this *must*
  56. // be made through `Record*` functions below. All reads from this *must* only
  57. // occur in the callback to `HashtablezSampler::Iterate`.
  58. struct HashtablezInfo : public profiling_internal::Sample<HashtablezInfo>
  59. {
  60. // Constructs the object but does not fill in any fields.
  61. HashtablezInfo();
  62. ~HashtablezInfo();
  63. HashtablezInfo(const HashtablezInfo&) = delete;
  64. HashtablezInfo& operator=(const HashtablezInfo&) = delete;
  65. // Puts the object into a clean state, fills in the logically `const` members,
  66. // blocking for any readers that are currently sampling the object.
  67. void PrepareForSampling(int64_t stride, size_t inline_element_size_value)
  68. ABSL_EXCLUSIVE_LOCKS_REQUIRED(init_mu);
  69. // These fields are mutated by the various Record* APIs and need to be
  70. // thread-safe.
  71. std::atomic<size_t> capacity;
  72. std::atomic<size_t> size;
  73. std::atomic<size_t> num_erases;
  74. std::atomic<size_t> num_rehashes;
  75. std::atomic<size_t> max_probe_length;
  76. std::atomic<size_t> total_probe_length;
  77. std::atomic<size_t> hashes_bitwise_or;
  78. std::atomic<size_t> hashes_bitwise_and;
  79. std::atomic<size_t> hashes_bitwise_xor;
  80. std::atomic<size_t> max_reserve;
  81. // All of the fields below are set by `PrepareForSampling`, they must not be
  82. // mutated in `Record*` functions. They are logically `const` in that sense.
  83. // These are guarded by init_mu, but that is not externalized to clients,
  84. // which can read them only during `SampleRecorder::Iterate` which will hold
  85. // the lock.
  86. static constexpr int kMaxStackDepth = 64;
  87. absl::Time create_time;
  88. int32_t depth;
  89. void* stack[kMaxStackDepth];
  90. size_t inline_element_size; // How big is the slot?
  91. };
  92. inline void RecordRehashSlow(HashtablezInfo* info, size_t total_probe_length)
  93. {
  94. #ifdef ABSL_INTERNAL_HAVE_SSE2
  95. total_probe_length /= 16;
  96. #else
  97. total_probe_length /= 8;
  98. #endif
  99. info->total_probe_length.store(total_probe_length, std::memory_order_relaxed);
  100. info->num_erases.store(0, std::memory_order_relaxed);
  101. // There is only one concurrent writer, so `load` then `store` is sufficient
  102. // instead of using `fetch_add`.
  103. info->num_rehashes.store(
  104. 1 + info->num_rehashes.load(std::memory_order_relaxed),
  105. std::memory_order_relaxed
  106. );
  107. }
  108. inline void RecordReservationSlow(HashtablezInfo* info, size_t target_capacity)
  109. {
  110. info->max_reserve.store(
  111. (std::max)(info->max_reserve.load(std::memory_order_relaxed), target_capacity),
  112. std::memory_order_relaxed
  113. );
  114. }
  115. inline void RecordClearedReservationSlow(HashtablezInfo* info)
  116. {
  117. info->max_reserve.store(0, std::memory_order_relaxed);
  118. }
  119. inline void RecordStorageChangedSlow(HashtablezInfo* info, size_t size, size_t capacity)
  120. {
  121. info->size.store(size, std::memory_order_relaxed);
  122. info->capacity.store(capacity, std::memory_order_relaxed);
  123. if (size == 0)
  124. {
  125. // This is a clear, reset the total/num_erases too.
  126. info->total_probe_length.store(0, std::memory_order_relaxed);
  127. info->num_erases.store(0, std::memory_order_relaxed);
  128. }
  129. }
  130. void RecordInsertSlow(HashtablezInfo* info, size_t hash, size_t distance_from_desired);
  131. inline void RecordEraseSlow(HashtablezInfo* info)
  132. {
  133. info->size.fetch_sub(1, std::memory_order_relaxed);
  134. // There is only one concurrent writer, so `load` then `store` is sufficient
  135. // instead of using `fetch_add`.
  136. info->num_erases.store(
  137. 1 + info->num_erases.load(std::memory_order_relaxed),
  138. std::memory_order_relaxed
  139. );
  140. }
  141. struct SamplingState
  142. {
  143. int64_t next_sample;
  144. // When we make a sampling decision, we record that distance so we can weight
  145. // each sample.
  146. int64_t sample_stride;
  147. };
  148. HashtablezInfo* SampleSlow(SamplingState& next_sample, size_t inline_element_size);
  149. void UnsampleSlow(HashtablezInfo* info);
  150. #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
  151. #error ABSL_INTERNAL_HASHTABLEZ_SAMPLE cannot be directly set
  152. #endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
  153. #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
  154. class HashtablezInfoHandle
  155. {
  156. public:
  157. explicit HashtablezInfoHandle() :
  158. info_(nullptr)
  159. {
  160. }
  161. explicit HashtablezInfoHandle(HashtablezInfo* info) :
  162. info_(info)
  163. {
  164. }
  165. ~HashtablezInfoHandle()
  166. {
  167. if (ABSL_PREDICT_TRUE(info_ == nullptr))
  168. return;
  169. UnsampleSlow(info_);
  170. }
  171. HashtablezInfoHandle(const HashtablezInfoHandle&) = delete;
  172. HashtablezInfoHandle& operator=(const HashtablezInfoHandle&) = delete;
  173. HashtablezInfoHandle(HashtablezInfoHandle&& o) noexcept
  174. :
  175. info_(absl::exchange(o.info_, nullptr))
  176. {
  177. }
  178. HashtablezInfoHandle& operator=(HashtablezInfoHandle&& o) noexcept
  179. {
  180. if (ABSL_PREDICT_FALSE(info_ != nullptr))
  181. {
  182. UnsampleSlow(info_);
  183. }
  184. info_ = absl::exchange(o.info_, nullptr);
  185. return *this;
  186. }
  187. inline void RecordStorageChanged(size_t size, size_t capacity)
  188. {
  189. if (ABSL_PREDICT_TRUE(info_ == nullptr))
  190. return;
  191. RecordStorageChangedSlow(info_, size, capacity);
  192. }
  193. inline void RecordRehash(size_t total_probe_length)
  194. {
  195. if (ABSL_PREDICT_TRUE(info_ == nullptr))
  196. return;
  197. RecordRehashSlow(info_, total_probe_length);
  198. }
  199. inline void RecordReservation(size_t target_capacity)
  200. {
  201. if (ABSL_PREDICT_TRUE(info_ == nullptr))
  202. return;
  203. RecordReservationSlow(info_, target_capacity);
  204. }
  205. inline void RecordClearedReservation()
  206. {
  207. if (ABSL_PREDICT_TRUE(info_ == nullptr))
  208. return;
  209. RecordClearedReservationSlow(info_);
  210. }
  211. inline void RecordInsert(size_t hash, size_t distance_from_desired)
  212. {
  213. if (ABSL_PREDICT_TRUE(info_ == nullptr))
  214. return;
  215. RecordInsertSlow(info_, hash, distance_from_desired);
  216. }
  217. inline void RecordErase()
  218. {
  219. if (ABSL_PREDICT_TRUE(info_ == nullptr))
  220. return;
  221. RecordEraseSlow(info_);
  222. }
  223. friend inline void swap(HashtablezInfoHandle& lhs, HashtablezInfoHandle& rhs)
  224. {
  225. std::swap(lhs.info_, rhs.info_);
  226. }
  227. private:
  228. friend class HashtablezInfoHandlePeer;
  229. HashtablezInfo* info_;
  230. };
  231. #else
  232. // Ensure that when Hashtablez is turned off at compile time, HashtablezInfo can
  233. // be removed by the linker, in order to reduce the binary size.
  234. class HashtablezInfoHandle
  235. {
  236. public:
  237. explicit HashtablezInfoHandle() = default;
  238. explicit HashtablezInfoHandle(std::nullptr_t)
  239. {
  240. }
  241. inline void RecordStorageChanged(size_t /*size*/, size_t /*capacity*/)
  242. {
  243. }
  244. inline void RecordRehash(size_t /*total_probe_length*/)
  245. {
  246. }
  247. inline void RecordReservation(size_t /*target_capacity*/)
  248. {
  249. }
  250. inline void RecordClearedReservation()
  251. {
  252. }
  253. inline void RecordInsert(size_t /*hash*/, size_t /*distance_from_desired*/)
  254. {
  255. }
  256. inline void RecordErase()
  257. {
  258. }
  259. friend inline void swap(HashtablezInfoHandle& /*lhs*/, HashtablezInfoHandle& /*rhs*/)
  260. {
  261. }
  262. };
  263. #endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
  264. #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
  265. extern ABSL_PER_THREAD_TLS_KEYWORD SamplingState global_next_sample;
  266. #endif // defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
  267. // Returns an RAII sampling handle that manages registration and unregistation
  268. // with the global sampler.
  269. inline HashtablezInfoHandle Sample(
  270. size_t inline_element_size ABSL_ATTRIBUTE_UNUSED
  271. )
  272. {
  273. #if defined(ABSL_INTERNAL_HASHTABLEZ_SAMPLE)
  274. if (ABSL_PREDICT_TRUE(--global_next_sample.next_sample > 0))
  275. {
  276. return HashtablezInfoHandle(nullptr);
  277. }
  278. return HashtablezInfoHandle(
  279. SampleSlow(global_next_sample, inline_element_size)
  280. );
  281. #else
  282. return HashtablezInfoHandle(nullptr);
  283. #endif // !ABSL_PER_THREAD_TLS
  284. }
  285. using HashtablezSampler =
  286. ::absl::profiling_internal::SampleRecorder<HashtablezInfo>;
  287. // Returns a global Sampler.
  288. HashtablezSampler& GlobalHashtablezSampler();
  289. using HashtablezConfigListener = void (*)();
  290. void SetHashtablezConfigListener(HashtablezConfigListener l);
  291. // Enables or disables sampling for Swiss tables.
  292. bool IsHashtablezEnabled();
  293. void SetHashtablezEnabled(bool enabled);
  294. void SetHashtablezEnabledInternal(bool enabled);
  295. // Sets the rate at which Swiss tables will be sampled.
  296. int32_t GetHashtablezSampleParameter();
  297. void SetHashtablezSampleParameter(int32_t rate);
  298. void SetHashtablezSampleParameterInternal(int32_t rate);
  299. // Sets a soft max for the number of samples that will be kept.
  300. int32_t GetHashtablezMaxSamples();
  301. void SetHashtablezMaxSamples(int32_t max);
  302. void SetHashtablezMaxSamplesInternal(int32_t max);
  303. // Configuration override.
  304. // This allows process-wide sampling without depending on order of
  305. // initialization of static storage duration objects.
  306. // The definition of this constant is weak, which allows us to inject a
  307. // different value for it at link time.
  308. extern "C" bool ABSL_INTERNAL_C_SYMBOL(AbslContainerInternalSampleEverything)();
  309. } // namespace container_internal
  310. ABSL_NAMESPACE_END
  311. } // namespace absl
  312. #endif // ABSL_CONTAINER_INTERNAL_HASHTABLEZ_SAMPLER_H_