F14TestUtil.h 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  1. /*
  2. * Copyright 2017-present Facebook, Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #pragma once
  17. #include <cstddef>
  18. #include <limits>
  19. #include <memory>
  20. #include <ostream>
  21. #include <vector>
  22. #include <folly/Demangle.h>
  23. #include <folly/Function.h>
  24. #include <folly/container/detail/F14Policy.h>
  25. #include <folly/container/detail/F14Table.h>
  26. namespace folly {
  27. namespace f14 {
  28. struct Histo {
  29. std::vector<std::size_t> const& data;
  30. };
  31. std::ostream& operator<<(std::ostream& xo, Histo const& histo) {
  32. xo << "[";
  33. size_t sum = 0;
  34. for (auto v : histo.data) {
  35. sum += v;
  36. }
  37. size_t partial = 0;
  38. for (size_t i = 0; i < histo.data.size(); ++i) {
  39. if (i > 0) {
  40. xo << ", ";
  41. }
  42. partial += histo.data[i];
  43. if (histo.data[i] > 0) {
  44. xo << i << ": " << histo.data[i] << " ("
  45. << (static_cast<double>(partial) * 100.0 / sum) << "%)";
  46. }
  47. }
  48. xo << "]";
  49. return xo;
  50. }
  51. void accumulate(
  52. std::vector<std::size_t>& a,
  53. std::vector<std::size_t> const& d) {
  54. if (a.size() < d.size()) {
  55. a.resize(d.size());
  56. }
  57. for (std::size_t i = 0; i < d.size(); ++i) {
  58. a[i] += d[i];
  59. }
  60. }
  61. double expectedProbe(std::vector<std::size_t> const& probeLengths) {
  62. std::size_t sum = 0;
  63. std::size_t count = 0;
  64. for (std::size_t i = 1; i < probeLengths.size(); ++i) {
  65. sum += i * probeLengths[i];
  66. count += probeLengths[i];
  67. }
  68. return static_cast<double>(sum) / static_cast<double>(count);
  69. }
  70. // Returns i such that probeLengths elements 0 to i (inclusive) account
  71. // for at least 99% of the samples.
  72. std::size_t p99Probe(std::vector<std::size_t> const& probeLengths) {
  73. std::size_t count = 0;
  74. for (std::size_t i = 1; i < probeLengths.size(); ++i) {
  75. count += probeLengths[i];
  76. }
  77. std::size_t rv = probeLengths.size();
  78. std::size_t suffix = 0;
  79. while ((suffix + probeLengths[rv - 1]) * 100 <= count) {
  80. --rv;
  81. }
  82. return rv;
  83. }
  84. struct MoveOnlyTestInt {
  85. int x;
  86. bool destroyed{false};
  87. MoveOnlyTestInt() noexcept : x(0) {}
  88. /* implicit */ MoveOnlyTestInt(int x0) : x(x0) {}
  89. MoveOnlyTestInt(MoveOnlyTestInt&& rhs) noexcept : x(rhs.x) {}
  90. MoveOnlyTestInt(MoveOnlyTestInt const&) = delete;
  91. MoveOnlyTestInt& operator=(MoveOnlyTestInt&& rhs) noexcept {
  92. x = rhs.x;
  93. destroyed = rhs.destroyed;
  94. return *this;
  95. }
  96. MoveOnlyTestInt& operator=(MoveOnlyTestInt const&) = delete;
  97. ~MoveOnlyTestInt() {
  98. destroyed = true;
  99. }
  100. bool operator==(MoveOnlyTestInt const& rhs) const {
  101. return x == rhs.x && destroyed == rhs.destroyed;
  102. }
  103. bool operator!=(MoveOnlyTestInt const& rhs) const {
  104. return !(*this == rhs);
  105. }
  106. };
  107. // Tracked is implicitly constructible across tags
  108. struct Counts {
  109. uint64_t copyConstruct{0};
  110. uint64_t moveConstruct{0};
  111. uint64_t copyConvert{0};
  112. uint64_t moveConvert{0};
  113. uint64_t copyAssign{0};
  114. uint64_t moveAssign{0};
  115. uint64_t defaultConstruct{0};
  116. uint64_t destroyed{0};
  117. explicit Counts(
  118. uint64_t copConstr = 0,
  119. uint64_t movConstr = 0,
  120. uint64_t copConv = 0,
  121. uint64_t movConv = 0,
  122. uint64_t copAssign = 0,
  123. uint64_t movAssign = 0,
  124. uint64_t def = 0,
  125. uint64_t destr = 0)
  126. : copyConstruct{copConstr},
  127. moveConstruct{movConstr},
  128. copyConvert{copConv},
  129. moveConvert{movConv},
  130. copyAssign{copAssign},
  131. moveAssign{movAssign},
  132. defaultConstruct{def},
  133. destroyed{destr} {}
  134. int64_t liveCount() const {
  135. return copyConstruct + moveConstruct + copyConvert + moveConvert +
  136. defaultConstruct - destroyed;
  137. }
  138. // dist ignores destroyed count
  139. uint64_t dist(Counts const& rhs) const {
  140. auto d = [](uint64_t x, uint64_t y) { return (x - y) * (x - y); };
  141. return d(copyConstruct, rhs.copyConstruct) +
  142. d(moveConstruct, rhs.moveConstruct) + d(copyConvert, rhs.copyConvert) +
  143. d(moveConvert, rhs.moveConvert) + d(copyAssign, rhs.copyAssign) +
  144. d(moveAssign, rhs.moveAssign) +
  145. d(defaultConstruct, rhs.defaultConstruct);
  146. }
  147. bool operator==(Counts const& rhs) const {
  148. return dist(rhs) == 0 && destroyed == rhs.destroyed;
  149. }
  150. bool operator!=(Counts const& rhs) const {
  151. return !(*this == rhs);
  152. }
  153. };
  154. std::ostream& operator<<(std::ostream& xo, Counts const& counts) {
  155. xo << "[";
  156. std::string glue = "";
  157. if (counts.copyConstruct > 0) {
  158. xo << glue << counts.copyConstruct << " copy";
  159. glue = ", ";
  160. }
  161. if (counts.moveConstruct > 0) {
  162. xo << glue << counts.moveConstruct << " move";
  163. glue = ", ";
  164. }
  165. if (counts.copyConvert > 0) {
  166. xo << glue << counts.copyConvert << " copy convert";
  167. glue = ", ";
  168. }
  169. if (counts.moveConvert > 0) {
  170. xo << glue << counts.moveConvert << " move convert";
  171. glue = ", ";
  172. }
  173. if (counts.copyAssign > 0) {
  174. xo << glue << counts.copyAssign << " copy assign";
  175. glue = ", ";
  176. }
  177. if (counts.moveAssign > 0) {
  178. xo << glue << counts.moveAssign << " move assign";
  179. glue = ", ";
  180. }
  181. if (counts.defaultConstruct > 0) {
  182. xo << glue << counts.defaultConstruct << " default construct";
  183. glue = ", ";
  184. }
  185. if (counts.destroyed > 0) {
  186. xo << glue << counts.destroyed << " destroyed";
  187. glue = ", ";
  188. }
  189. xo << "]";
  190. return xo;
  191. }
  192. thread_local Counts sumCounts{};
  193. template <int Tag>
  194. struct Tracked {
  195. static_assert(Tag <= 5, "Need to extend Tracked<Tag> in F14TestUtil.h");
  196. static thread_local Counts counts;
  197. uint64_t val_;
  198. Tracked() : val_{0} {
  199. sumCounts.defaultConstruct++;
  200. counts.defaultConstruct++;
  201. }
  202. /* implicit */ Tracked(uint64_t const& val) : val_{val} {
  203. sumCounts.copyConvert++;
  204. counts.copyConvert++;
  205. }
  206. /* implicit */ Tracked(uint64_t&& val) : val_{val} {
  207. sumCounts.moveConvert++;
  208. counts.moveConvert++;
  209. }
  210. Tracked(Tracked const& rhs) : val_{rhs.val_} {
  211. sumCounts.copyConstruct++;
  212. counts.copyConstruct++;
  213. }
  214. Tracked(Tracked&& rhs) noexcept : val_{rhs.val_} {
  215. sumCounts.moveConstruct++;
  216. counts.moveConstruct++;
  217. }
  218. Tracked& operator=(Tracked const& rhs) {
  219. val_ = rhs.val_;
  220. sumCounts.copyAssign++;
  221. counts.copyAssign++;
  222. return *this;
  223. }
  224. Tracked& operator=(Tracked&& rhs) noexcept {
  225. val_ = rhs.val_;
  226. sumCounts.moveAssign++;
  227. counts.moveAssign++;
  228. return *this;
  229. }
  230. template <int T>
  231. /* implicit */ Tracked(Tracked<T> const& rhs) : val_{rhs.val_} {
  232. sumCounts.copyConvert++;
  233. counts.copyConvert++;
  234. }
  235. template <int T>
  236. /* implicit */ Tracked(Tracked<T>&& rhs) : val_{rhs.val_} {
  237. sumCounts.moveConvert++;
  238. counts.moveConvert++;
  239. }
  240. ~Tracked() {
  241. sumCounts.destroyed++;
  242. counts.destroyed++;
  243. }
  244. bool operator==(Tracked const& rhs) const {
  245. return val_ == rhs.val_;
  246. }
  247. bool operator!=(Tracked const& rhs) const {
  248. return !(*this == rhs);
  249. }
  250. };
  251. template <int Tag>
  252. struct TransparentTrackedHash {
  253. using is_transparent = std::true_type;
  254. size_t operator()(Tracked<Tag> const& tracked) const {
  255. return tracked.val_ ^ Tag;
  256. }
  257. size_t operator()(uint64_t v) const {
  258. return v ^ Tag;
  259. }
  260. };
  261. template <int Tag>
  262. struct TransparentTrackedEqual {
  263. using is_transparent = std::true_type;
  264. uint64_t unwrap(Tracked<Tag> const& v) const {
  265. return v.val_;
  266. }
  267. uint64_t unwrap(uint64_t v) const {
  268. return v;
  269. }
  270. template <typename A, typename B>
  271. bool operator()(A const& lhs, B const& rhs) const {
  272. return unwrap(lhs) == unwrap(rhs);
  273. }
  274. };
  275. template <>
  276. thread_local Counts Tracked<0>::counts{};
  277. template <>
  278. thread_local Counts Tracked<1>::counts{};
  279. template <>
  280. thread_local Counts Tracked<2>::counts{};
  281. template <>
  282. thread_local Counts Tracked<3>::counts{};
  283. template <>
  284. thread_local Counts Tracked<4>::counts{};
  285. template <>
  286. thread_local Counts Tracked<5>::counts{};
  287. thread_local size_t testAllocatedMemorySize{0};
  288. thread_local size_t testAllocatedBlockCount{0};
  289. thread_local size_t testAllocationCount{0};
  290. thread_local size_t testAllocationMaxCount{
  291. std::numeric_limits<std::size_t>::max()};
  292. inline void limitTestAllocations(std::size_t allocationsBeforeException = 0) {
  293. testAllocationMaxCount = testAllocationCount + allocationsBeforeException;
  294. }
  295. inline void unlimitTestAllocations() {
  296. testAllocationMaxCount = std::numeric_limits<std::size_t>::max();
  297. }
  298. inline void resetTracking() {
  299. sumCounts = Counts{};
  300. Tracked<0>::counts = Counts{};
  301. Tracked<1>::counts = Counts{};
  302. Tracked<2>::counts = Counts{};
  303. Tracked<3>::counts = Counts{};
  304. Tracked<4>::counts = Counts{};
  305. Tracked<5>::counts = Counts{};
  306. testAllocatedMemorySize = 0;
  307. testAllocatedBlockCount = 0;
  308. testAllocationCount = 0;
  309. testAllocationMaxCount = std::numeric_limits<std::size_t>::max();
  310. }
  311. template <class T>
  312. class SwapTrackingAlloc {
  313. public:
  314. using Alloc = std::allocator<T>;
  315. using value_type = typename Alloc::value_type;
  316. using pointer = typename Alloc::pointer;
  317. using const_pointer = typename Alloc::const_pointer;
  318. using reference = typename Alloc::reference;
  319. using const_reference = typename Alloc::const_reference;
  320. using size_type = typename Alloc::size_type;
  321. using propagate_on_container_swap = std::true_type;
  322. using propagate_on_container_copy_assignment = std::true_type;
  323. using propagate_on_container_move_assignment = std::true_type;
  324. SwapTrackingAlloc() {}
  325. template <class U>
  326. SwapTrackingAlloc(SwapTrackingAlloc<U> const& other) noexcept
  327. : a_(other.a_), t_(other.t_) {}
  328. template <class U>
  329. SwapTrackingAlloc& operator=(SwapTrackingAlloc<U> const& other) noexcept {
  330. a_ = other.a_;
  331. t_ = other.t_;
  332. return *this;
  333. }
  334. template <class U>
  335. SwapTrackingAlloc(SwapTrackingAlloc<U>&& other) noexcept
  336. : a_(std::move(other.a_)), t_(std::move(other.t_)) {}
  337. template <class U>
  338. SwapTrackingAlloc& operator=(SwapTrackingAlloc<U>&& other) noexcept {
  339. a_ = std::move(other.a_);
  340. t_ = std::move(other.t_);
  341. return *this;
  342. }
  343. T* allocate(size_t n) {
  344. if (testAllocationCount >= testAllocationMaxCount) {
  345. throw std::bad_alloc();
  346. }
  347. ++testAllocationCount;
  348. testAllocatedMemorySize += n * sizeof(T);
  349. ++testAllocatedBlockCount;
  350. return a_.allocate(n);
  351. }
  352. void deallocate(T* p, size_t n) {
  353. testAllocatedMemorySize -= n * sizeof(T);
  354. --testAllocatedBlockCount;
  355. a_.deallocate(p, n);
  356. }
  357. private:
  358. std::allocator<T> a_;
  359. folly::f14::Tracked<0> t_;
  360. template <class U>
  361. friend class SwapTrackingAlloc;
  362. };
  363. template <class T>
  364. void swap(SwapTrackingAlloc<T>&, SwapTrackingAlloc<T>&) {
  365. // For argument dependent lookup:
  366. // This function will be called if the custom swap functions of F14 containers
  367. // are used. Otherwise, std::swap() will do 1 move construct and 2 move
  368. // assigns which will get tracked by t_.
  369. }
  370. template <class T1, class T2>
  371. bool operator==(SwapTrackingAlloc<T1> const&, SwapTrackingAlloc<T2> const&) {
  372. return true;
  373. }
  374. template <class T1, class T2>
  375. bool operator!=(SwapTrackingAlloc<T1> const&, SwapTrackingAlloc<T2> const&) {
  376. return false;
  377. }
  378. std::ostream& operator<<(std::ostream& xo, F14TableStats const& stats) {
  379. using f14::Histo;
  380. xo << "{ " << std::endl;
  381. xo << " policy: "
  382. #if FOLLY_HAS_RTTI
  383. << folly::demangle(stats.policy)
  384. #else
  385. << "unknown (RTTI not availabe)"
  386. #endif
  387. << std::endl;
  388. xo << " size: " << stats.size << std::endl;
  389. xo << " valueSize: " << stats.valueSize << std::endl;
  390. xo << " bucketCount: " << stats.bucketCount << std::endl;
  391. xo << " chunkCount: " << stats.chunkCount << std::endl;
  392. xo << " chunkOccupancyHisto" << Histo{stats.chunkOccupancyHisto}
  393. << std::endl;
  394. xo << " chunkOutboundOverflowHisto"
  395. << Histo{stats.chunkOutboundOverflowHisto} << std::endl;
  396. xo << " chunkHostedOverflowHisto" << Histo{stats.chunkHostedOverflowHisto}
  397. << std::endl;
  398. xo << " keyProbeLengthHisto" << Histo{stats.keyProbeLengthHisto}
  399. << std::endl;
  400. xo << " missProbeLengthHisto" << Histo{stats.missProbeLengthHisto}
  401. << std::endl;
  402. xo << " totalBytes: " << stats.totalBytes << std::endl;
  403. xo << " valueBytes: " << (stats.size * stats.valueSize) << std::endl;
  404. xo << " overheadBytes: " << stats.overheadBytes << std::endl;
  405. if (stats.size > 0) {
  406. xo << " overheadBytesPerKey: "
  407. << (static_cast<double>(stats.overheadBytes) /
  408. static_cast<double>(stats.size))
  409. << std::endl;
  410. }
  411. xo << "}";
  412. return xo;
  413. }
  414. template <class T>
  415. class GenericAlloc {
  416. public:
  417. using value_type = T;
  418. using pointer = T*;
  419. using const_pointer = T const*;
  420. using reference = T&;
  421. using const_reference = T const&;
  422. using size_type = std::size_t;
  423. using propagate_on_container_swap = std::true_type;
  424. using propagate_on_container_copy_assignment = std::true_type;
  425. using propagate_on_container_move_assignment = std::true_type;
  426. using AllocBytesFunc = folly::Function<void*(std::size_t)>;
  427. using DeallocBytesFunc = folly::Function<void(void*, std::size_t)>;
  428. GenericAlloc() = delete;
  429. template <typename A, typename D>
  430. GenericAlloc(A&& alloc, D&& dealloc)
  431. : alloc_{std::make_shared<AllocBytesFunc>(std::forward<A>(alloc))},
  432. dealloc_{std::make_shared<DeallocBytesFunc>(std::forward<D>(dealloc))} {
  433. }
  434. template <class U>
  435. GenericAlloc(GenericAlloc<U> const& other) noexcept
  436. : alloc_{other.alloc_}, dealloc_{other.dealloc_} {}
  437. template <class U>
  438. GenericAlloc& operator=(GenericAlloc<U> const& other) noexcept {
  439. alloc_ = other.alloc_;
  440. dealloc_ = other.dealloc_;
  441. return *this;
  442. }
  443. template <class U>
  444. GenericAlloc(GenericAlloc<U>&& other) noexcept
  445. : alloc_(std::move(other.alloc_)), dealloc_(std::move(other.dealloc_)) {}
  446. template <class U>
  447. GenericAlloc& operator=(GenericAlloc<U>&& other) noexcept {
  448. alloc_ = std::move(other.alloc_);
  449. dealloc_ = std::move(other.dealloc_);
  450. return *this;
  451. }
  452. T* allocate(size_t n) {
  453. return static_cast<T*>((*alloc_)(n * sizeof(T)));
  454. }
  455. void deallocate(T* p, size_t n) {
  456. (*dealloc_)(static_cast<void*>(p), n * sizeof(T));
  457. }
  458. template <typename U>
  459. bool operator==(GenericAlloc<U> const& rhs) const {
  460. return alloc_ == rhs.alloc_;
  461. }
  462. template <typename U>
  463. bool operator!=(GenericAlloc<U> const& rhs) const {
  464. return !(*this == rhs);
  465. }
  466. private:
  467. std::shared_ptr<AllocBytesFunc> alloc_;
  468. std::shared_ptr<DeallocBytesFunc> dealloc_;
  469. template <class U>
  470. friend class GenericAlloc;
  471. };
  472. template <typename T>
  473. class GenericEqual {
  474. public:
  475. using EqualFunc = folly::Function<bool(T const&, T const&)>;
  476. GenericEqual() = delete;
  477. template <typename E>
  478. GenericEqual(E&& equal)
  479. : equal_{std::make_shared<EqualFunc>(std::forward<E>(equal))} {}
  480. bool operator()(T const& lhs, T const& rhs) const {
  481. return (*equal_)(lhs, rhs);
  482. }
  483. private:
  484. std::shared_ptr<EqualFunc> equal_;
  485. };
  486. template <typename T>
  487. class GenericHasher {
  488. public:
  489. using HasherFunc = folly::Function<std::size_t(T const&)>;
  490. GenericHasher() = delete;
  491. template <typename H>
  492. GenericHasher(H&& hasher)
  493. : hasher_{std::make_shared<HasherFunc>(std::forward<H>(hasher))} {}
  494. std::size_t operator()(T const& val) const {
  495. return (*hasher_)(val);
  496. }
  497. private:
  498. std::shared_ptr<HasherFunc> hasher_;
  499. };
  500. } // namespace f14
  501. } // namespace folly
  502. namespace std {
  503. template <>
  504. struct hash<folly::f14::MoveOnlyTestInt> {
  505. std::size_t operator()(folly::f14::MoveOnlyTestInt const& val) const {
  506. return val.x;
  507. }
  508. };
  509. template <int Tag>
  510. struct hash<folly::f14::Tracked<Tag>> {
  511. size_t operator()(folly::f14::Tracked<Tag> const& tracked) const {
  512. return tracked.val_ ^ Tag;
  513. }
  514. };
  515. } // namespace std