HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
robin_growth_policy.h
Go to the documentation of this file.
1 /**
2  * MIT License
3  *
4  * Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  */
24 #ifndef PXR_TSL_ROBIN_GROWTH_POLICY_H
25 #define PXR_TSL_ROBIN_GROWTH_POLICY_H
26 
27 #include <algorithm>
28 #include <array>
29 #include <climits>
30 #include <cmath>
31 #include <cstddef>
32 #include <cstdint>
33 #include <iterator>
34 #include <limits>
35 #include <ratio>
36 #include <stdexcept>
37 
38 // Pixar modification, modify namespace for isolation.
39 #include "pxr/pxr.h"
40 
41 #ifdef PXR_TSL_DEBUG
42 #define pxr_tsl_rh_assert(expr) assert(expr)
43 #else
44 #define pxr_tsl_rh_assert(expr) (static_cast<void>(0))
45 #endif
46 
47 /**
48  * If exceptions are enabled, throw the exception passed in parameter, otherwise
49  * call std::terminate.
50  */
51 #if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
52  (defined(_MSC_VER) && defined(_CPPUNWIND))) && \
53  !defined(PXR_TSL_NO_EXCEPTIONS)
54 #define PXR_TSL_RH_THROW_OR_TERMINATE(ex, msg) throw ex(msg)
55 #else
56 #define PXR_TSL_RH_NO_EXCEPTIONS
57 #ifdef NDEBUG
58 #define PXR_TSL_RH_THROW_OR_TERMINATE(ex, msg) std::terminate()
59 #else
60 #include <iostream>
61 #define PXR_TSL_RH_THROW_OR_TERMINATE(ex, msg) \
62  do { \
63  std::cerr << msg << std::endl; \
64  std::terminate(); \
65  } while (0)
66 #endif
67 #endif
68 
69 #if defined(__GNUC__) || defined(__clang__)
70 #define PXR_TSL_RH_LIKELY(exp) (__builtin_expect(!!(exp), true))
71 #else
72 #define PXR_TSL_RH_LIKELY(exp) (exp)
73 #endif
74 
75 #define PXR_TSL_RH_UNUSED(x) static_cast<void>(x)
76 
78 
79 namespace pxr_tsl {
80 namespace rh {
81 
82 /**
83  * Grow the hash table by a factor of GrowthFactor keeping the bucket count to a
84  * power of two. It allows the table to use a mask operation instead of a modulo
85  * operation to map a hash to a bucket.
86  *
87  * GrowthFactor must be a power of two >= 2.
88  */
89 template <std::size_t GrowthFactor>
91  public:
92  /**
93  * Called on the hash table creation and on rehash. The number of buckets for
94  * the table is passed in parameter. This number is a minimum, the policy may
95  * update this value with a higher value if needed (but not lower).
96  *
97  * If 0 is given, min_bucket_count_in_out must still be 0 after the policy
98  * creation and bucket_for_hash must always return 0 in this case.
99  */
100  explicit power_of_two_growth_policy(std::size_t& min_bucket_count_in_out) {
101  if (min_bucket_count_in_out > max_bucket_count()) {
102  PXR_TSL_RH_THROW_OR_TERMINATE(std::length_error,
103  "The hash table exceeds its maximum size.");
104  }
105 
106  if (min_bucket_count_in_out > 0) {
107  min_bucket_count_in_out =
108  round_up_to_power_of_two(min_bucket_count_in_out);
109  m_mask = min_bucket_count_in_out - 1;
110  } else {
111  m_mask = 0;
112  }
113  }
114 
115  /**
116  * Return the bucket [0, bucket_count()) to which the hash belongs.
117  * If bucket_count() is 0, it must always return 0.
118  */
119  std::size_t bucket_for_hash(std::size_t hash) const noexcept {
120  return hash & m_mask;
121  }
122 
123  /**
124  * Return the number of buckets that should be used on next growth.
125  */
126  std::size_t next_bucket_count() const {
127  if ((m_mask + 1) > max_bucket_count() / GrowthFactor) {
128  PXR_TSL_RH_THROW_OR_TERMINATE(std::length_error,
129  "The hash table exceeds its maximum size.");
130  }
131 
132  return (m_mask + 1) * GrowthFactor;
133  }
134 
135  /**
136  * Return the maximum number of buckets supported by the policy.
137  */
138  std::size_t max_bucket_count() const {
139  // Largest power of two.
140  return (std::numeric_limits<std::size_t>::max() / 2) + 1;
141  }
142 
143  /**
144  * Reset the growth policy as if it was created with a bucket count of 0.
145  * After a clear, the policy must always return 0 when bucket_for_hash is
146  * called.
147  */
148  void clear() noexcept { m_mask = 0; }
149 
150  private:
151  static std::size_t round_up_to_power_of_two(std::size_t value) {
152  if (is_power_of_two(value)) {
153  return value;
154  }
155 
156  if (value == 0) {
157  return 1;
158  }
159 
160  --value;
161  for (std::size_t i = 1; i < sizeof(std::size_t) * CHAR_BIT; i *= 2) {
162  value |= value >> i;
163  }
164 
165  return value + 1;
166  }
167 
168  static constexpr bool is_power_of_two(std::size_t value) {
169  return value != 0 && (value & (value - 1)) == 0;
170  }
171 
172  protected:
173  static_assert(is_power_of_two(GrowthFactor) && GrowthFactor >= 2,
174  "GrowthFactor must be a power of two >= 2.");
175 
176  std::size_t m_mask;
177 };
178 
179 /**
180  * Grow the hash table by GrowthFactor::num / GrowthFactor::den and use a modulo
181  * to map a hash to a bucket. Slower but it can be useful if you want a slower
182  * growth.
183  */
184 template <class GrowthFactor = std::ratio<3, 2>>
186  public:
187  explicit mod_growth_policy(std::size_t& min_bucket_count_in_out) {
188  if (min_bucket_count_in_out > max_bucket_count()) {
189  PXR_TSL_RH_THROW_OR_TERMINATE(std::length_error,
190  "The hash table exceeds its maximum size.");
191  }
192 
193  if (min_bucket_count_in_out > 0) {
194  m_mod = min_bucket_count_in_out;
195  } else {
196  m_mod = 1;
197  }
198  }
199 
200  std::size_t bucket_for_hash(std::size_t hash) const noexcept {
201  return hash % m_mod;
202  }
203 
204  std::size_t next_bucket_count() const {
205  if (m_mod == max_bucket_count()) {
206  PXR_TSL_RH_THROW_OR_TERMINATE(std::length_error,
207  "The hash table exceeds its maximum size.");
208  }
209 
210  const double next_bucket_count =
211  std::ceil(double(m_mod) * REHASH_SIZE_MULTIPLICATION_FACTOR);
212  if (!std::isnormal(next_bucket_count)) {
213  PXR_TSL_RH_THROW_OR_TERMINATE(std::length_error,
214  "The hash table exceeds its maximum size.");
215  }
216 
217  if (next_bucket_count > double(max_bucket_count())) {
218  return max_bucket_count();
219  } else {
220  return std::size_t(next_bucket_count);
221  }
222  }
223 
224  std::size_t max_bucket_count() const { return MAX_BUCKET_COUNT; }
225 
226  void clear() noexcept { m_mod = 1; }
227 
228  private:
229  static constexpr double REHASH_SIZE_MULTIPLICATION_FACTOR =
230  1.0 * GrowthFactor::num / GrowthFactor::den;
231  static const std::size_t MAX_BUCKET_COUNT =
232  std::size_t(double(std::numeric_limits<std::size_t>::max() /
233  REHASH_SIZE_MULTIPLICATION_FACTOR));
234 
235  static_assert(REHASH_SIZE_MULTIPLICATION_FACTOR >= 1.1,
236  "Growth factor should be >= 1.1.");
237 
238  std::size_t m_mod;
239 };
240 
241 namespace detail {
242 
243 #if SIZE_MAX >= ULLONG_MAX
244 #define PXR_TSL_RH_NB_PRIMES 51
245 #elif SIZE_MAX >= ULONG_MAX
246 #define PXR_TSL_RH_NB_PRIMES 40
247 #else
248 #define PXR_TSL_RH_NB_PRIMES 23
249 #endif
250 
251 static constexpr const std::array<std::size_t, PXR_TSL_RH_NB_PRIMES> PRIMES = {{
252  1u,
253  5u,
254  17u,
255  29u,
256  37u,
257  53u,
258  67u,
259  79u,
260  97u,
261  131u,
262  193u,
263  257u,
264  389u,
265  521u,
266  769u,
267  1031u,
268  1543u,
269  2053u,
270  3079u,
271  6151u,
272  12289u,
273  24593u,
274  49157u,
275 #if SIZE_MAX >= ULONG_MAX
276  98317ul,
277  196613ul,
278  393241ul,
279  786433ul,
280  1572869ul,
281  3145739ul,
282  6291469ul,
283  12582917ul,
284  25165843ul,
285  50331653ul,
286  100663319ul,
287  201326611ul,
288  402653189ul,
289  805306457ul,
290  1610612741ul,
291  3221225473ul,
292  4294967291ul,
293 #endif
294 #if SIZE_MAX >= ULLONG_MAX
295  6442450939ull,
296  12884901893ull,
297  25769803751ull,
298  51539607551ull,
299  103079215111ull,
300  206158430209ull,
301  412316860441ull,
302  824633720831ull,
303  1649267441651ull,
304  3298534883309ull,
305  6597069766657ull,
306 #endif
307 }};
308 
309 template <unsigned int IPrime>
310 static constexpr std::size_t mod(std::size_t hash) {
311  return hash % PRIMES[IPrime];
312 }
313 
314 // MOD_PRIME[iprime](hash) returns hash % PRIMES[iprime]. This table allows for
315 // faster modulo as the compiler can optimize the modulo code better with a
316 // constant known at the compilation.
317 static constexpr const std::array<std::size_t (*)(std::size_t),
319  MOD_PRIME = {{
320  &mod<0>, &mod<1>, &mod<2>, &mod<3>, &mod<4>, &mod<5>,
321  &mod<6>, &mod<7>, &mod<8>, &mod<9>, &mod<10>, &mod<11>,
322  &mod<12>, &mod<13>, &mod<14>, &mod<15>, &mod<16>, &mod<17>,
323  &mod<18>, &mod<19>, &mod<20>, &mod<21>, &mod<22>,
324 #if SIZE_MAX >= ULONG_MAX
325  &mod<23>, &mod<24>, &mod<25>, &mod<26>, &mod<27>, &mod<28>,
326  &mod<29>, &mod<30>, &mod<31>, &mod<32>, &mod<33>, &mod<34>,
327  &mod<35>, &mod<36>, &mod<37>, &mod<38>, &mod<39>,
328 #endif
329 #if SIZE_MAX >= ULLONG_MAX
330  &mod<40>, &mod<41>, &mod<42>, &mod<43>, &mod<44>, &mod<45>,
331  &mod<46>, &mod<47>, &mod<48>, &mod<49>, &mod<50>,
332 #endif
333  }};
334 
335 } // namespace detail
336 
337 /**
338  * Grow the hash table by using prime numbers as bucket count. Slower than
339  * tsl::rh::power_of_two_growth_policy in general but will probably distribute
340  * the values around better in the buckets with a poor hash function.
341  *
342  * To allow the compiler to optimize the modulo operation, a lookup table is
343  * used with constant primes numbers.
344  *
345  * With a switch the code would look like:
346  * \code
347  * switch(iprime) { // iprime is the current prime of the hash table
348  * case 0: hash % 5ul;
349  * break;
350  * case 1: hash % 17ul;
351  * break;
352  * case 2: hash % 29ul;
353  * break;
354  * ...
355  * }
356  * \endcode
357  *
358  * Due to the constant variable in the modulo the compiler is able to optimize
359  * the operation by a series of multiplications, substractions and shifts.
360  *
361  * The 'hash % 5' could become something like 'hash - (hash * 0xCCCCCCCD) >> 34)
362  * * 5' in a 64 bits environment.
363  */
365  public:
366  explicit prime_growth_policy(std::size_t& min_bucket_count_in_out) {
367  auto it_prime = std::lower_bound(
368  detail::PRIMES.begin(), detail::PRIMES.end(), min_bucket_count_in_out);
369  if (it_prime == detail::PRIMES.end()) {
370  PXR_TSL_RH_THROW_OR_TERMINATE(std::length_error,
371  "The hash table exceeds its maximum size.");
372  }
373 
374  m_iprime = static_cast<unsigned int>(
375  std::distance(detail::PRIMES.begin(), it_prime));
376  if (min_bucket_count_in_out > 0) {
377  min_bucket_count_in_out = *it_prime;
378  } else {
379  min_bucket_count_in_out = 0;
380  }
381  }
382 
383  std::size_t bucket_for_hash(std::size_t hash) const noexcept {
384  return detail::MOD_PRIME[m_iprime](hash);
385  }
386 
387  std::size_t next_bucket_count() const {
388  if (m_iprime + 1 >= detail::PRIMES.size()) {
389  PXR_TSL_RH_THROW_OR_TERMINATE(std::length_error,
390  "The hash table exceeds its maximum size.");
391  }
392 
393  return detail::PRIMES[m_iprime + 1];
394  }
395 
396  std::size_t max_bucket_count() const { return detail::PRIMES.back(); }
397 
398  void clear() noexcept { m_iprime = 0; }
399 
400  private:
401  unsigned int m_iprime;
402 
403  static_assert(std::numeric_limits<decltype(m_iprime)>::max() >=
404  detail::PRIMES.size(),
405  "The type of m_iprime is not big enough.");
406 };
407 
408 } // namespace rh
409 } // namespace pxr_tsl
410 
412 
413 #endif
std::size_t next_bucket_count() const
GLsizei const GLfloat * value
Definition: glcorearb.h:824
std::size_t bucket_for_hash(std::size_t hash) const noexcept
std::size_t bucket_for_hash(std::size_t hash) const noexcept
GLuint GLuint end
Definition: glcorearb.h:475
#define PXR_TSL_RH_THROW_OR_TERMINATE(ex, msg)
std::size_t bucket_for_hash(std::size_t hash) const noexcept
power_of_two_growth_policy(std::size_t &min_bucket_count_in_out)
GLsizeiptr size
Definition: glcorearb.h:664
IMATH_HOSTDEVICE constexpr int ceil(T x) IMATH_NOEXCEPT
Definition: ImathFun.h:119
PXR_NAMESPACE_CLOSE_SCOPE PXR_NAMESPACE_OPEN_SCOPE
Definition: path.h:1441
std::size_t max_bucket_count() const
ImageBuf OIIO_API max(Image_or_Const A, Image_or_Const B, ROI roi={}, int nthreads=0)
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:91
prime_growth_policy(std::size_t &min_bucket_count_in_out)
Definition: core.h:1131
SIM_API const UT_StringHolder distance
mod_growth_policy(std::size_t &min_bucket_count_in_out)
PcpNodeRef_ChildrenIterator begin(const PcpNodeRef::child_const_range &r)
Support for range-based for loops for PcpNodeRef children ranges.
Definition: node.h:483
#define PXR_TSL_RH_NB_PRIMES