core_local.h 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. // Copyright (c) 2017-present, Facebook, Inc. All rights reserved.
  2. // This source code is licensed under both the GPLv2 (found in the
  3. // COPYING file in the root directory) and Apache 2.0 License
  4. // (found in the LICENSE.Apache file in the root directory).
  5. #pragma once
  6. #include <cassert>
  7. #include <cstddef>
  8. #include <thread>
  9. #include <utility>
  10. #include <vector>
  11. #include "port/likely.h"
  12. #include "port/port.h"
  13. #include "util/math.h"
  14. #include "util/random.h"
  15. namespace ROCKSDB_NAMESPACE {
  16. // An array of core-local values. Ideally the value type, T, is cache aligned to
  17. // prevent false sharing.
  18. template <typename T>
  19. class CoreLocalArray {
  20. public:
  21. CoreLocalArray();
  22. size_t Size() const;
  23. // returns pointer to the element corresponding to the core that the thread
  24. // currently runs on.
  25. T* Access() const;
  26. // same as above, but also returns the core index, which the client can cache
  27. // to reduce how often core ID needs to be retrieved. Only do this if some
  28. // inaccuracy is tolerable, as the thread may migrate to a different core.
  29. std::pair<T*, size_t> AccessElementAndIndex() const;
  30. // returns pointer to element for the specified core index. This can be used,
  31. // e.g., for aggregation, or if the client caches core index.
  32. T* AccessAtCore(size_t core_idx) const;
  33. private:
  34. std::unique_ptr<T[]> data_;
  35. int size_shift_;
  36. };
  37. template <typename T>
  38. CoreLocalArray<T>::CoreLocalArray() {
  39. int num_cpus = static_cast<int>(std::thread::hardware_concurrency());
  40. // find a power of two >= num_cpus and >= 8
  41. size_shift_ = 3;
  42. while (1 << size_shift_ < num_cpus) {
  43. ++size_shift_;
  44. }
  45. data_.reset(new T[static_cast<size_t>(1) << size_shift_]);
  46. }
  47. template <typename T>
  48. size_t CoreLocalArray<T>::Size() const {
  49. return static_cast<size_t>(1) << size_shift_;
  50. }
  51. template <typename T>
  52. T* CoreLocalArray<T>::Access() const {
  53. return AccessElementAndIndex().first;
  54. }
  55. template <typename T>
  56. std::pair<T*, size_t> CoreLocalArray<T>::AccessElementAndIndex() const {
  57. int cpuid = port::PhysicalCoreID();
  58. size_t core_idx;
  59. if (UNLIKELY(cpuid < 0)) {
  60. // cpu id unavailable, just pick randomly
  61. core_idx = Random::GetTLSInstance()->Uniform(1 << size_shift_);
  62. } else {
  63. core_idx = static_cast<size_t>(BottomNBits(cpuid, size_shift_));
  64. }
  65. return {AccessAtCore(core_idx), core_idx};
  66. }
  67. template <typename T>
  68. T* CoreLocalArray<T>::AccessAtCore(size_t core_idx) const {
  69. assert(core_idx < static_cast<size_t>(1) << size_shift_);
  70. return &data_[core_idx];
  71. }
  72. } // namespace ROCKSDB_NAMESPACE