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 // A change of the major version indicates an API and/or ABI break (change of
42 // in-memory layout of the data structure)
43 #define PXR_TSL_RH_VERSION_MAJOR 1
44 // A change of the minor version indicates the addition of a feature without
45 // impact on the API/ABI
46 #define PXR_TSL_RH_VERSION_MINOR 3
47 // A change of the patch version indicates a bugfix without additional
48 // functionality
49 #define PXR_TSL_RH_VERSION_PATCH 0
50 
51 #ifdef PXR_TSL_DEBUG
52 #define pxr_tsl_rh_assert(expr) assert(expr)
53 #else
54 #define pxr_tsl_rh_assert(expr) (static_cast<void>(0))
55 #endif
56 
57 /**
58  * If exceptions are enabled, throw the exception passed in parameter, otherwise
59  * call std::terminate.
60  */
61 #if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
62  (defined(_MSC_VER) && defined(_CPPUNWIND))) && \
63  !defined(PXR_TSL_NO_EXCEPTIONS)
64 #define PXR_TSL_RH_THROW_OR_TERMINATE(ex, msg) throw ex(msg)
65 #else
66 #define PXR_TSL_RH_NO_EXCEPTIONS
67 #ifdef PXR_TSL_DEBUG
68 #include <iostream>
69 #define PXR_TSL_RH_THROW_OR_TERMINATE(ex, msg) \
70  do { \
71  std::cerr << msg << std::endl; \
72  std::terminate(); \
73  } while (0)
74 #else
75 #define PXR_TSL_RH_THROW_OR_TERMINATE(ex, msg) std::terminate()
76 #endif
77 #endif
78 
79 #if defined(__GNUC__) || defined(__clang__)
80 #define PXR_TSL_RH_LIKELY(exp) (__builtin_expect(!!(exp), true))
81 #else
82 #define PXR_TSL_RH_LIKELY(exp) (exp)
83 #endif
84 
85 #define PXR_TSL_RH_UNUSED(x) static_cast<void>(x)
86 
88 
89 namespace pxr_tsl {
90 namespace rh {
91 
92 /**
93  * Grow the hash table by a factor of GrowthFactor keeping the bucket count to a
94  * power of two. It allows the table to use a mask operation instead of a modulo
95  * operation to map a hash to a bucket.
96  *
97  * GrowthFactor must be a power of two >= 2.
98  */
99 template <std::size_t GrowthFactor>
101  public:
102  /**
103  * Called on the hash table creation and on rehash. The number of buckets for
104  * the table is passed in parameter. This number is a minimum, the policy may
105  * update this value with a higher value if needed (but not lower).
106  *
107  * If 0 is given, min_bucket_count_in_out must still be 0 after the policy
108  * creation and bucket_for_hash must always return 0 in this case.
109  */
110  explicit power_of_two_growth_policy(std::size_t& min_bucket_count_in_out) {
111  if (min_bucket_count_in_out > max_bucket_count()) {
112  PXR_TSL_RH_THROW_OR_TERMINATE(std::length_error,
113  "The hash table exceeds its maximum size.");
114  }
115 
116  if (min_bucket_count_in_out > 0) {
117  min_bucket_count_in_out =
118  round_up_to_power_of_two(min_bucket_count_in_out);
119  m_mask = min_bucket_count_in_out - 1;
120  } else {
121  m_mask = 0;
122  }
123  }
124 
125  /**
126  * Return the bucket [0, bucket_count()) to which the hash belongs.
127  * If bucket_count() is 0, it must always return 0.
128  */
129  std::size_t bucket_for_hash(std::size_t hash) const noexcept {
130  return hash & m_mask;
131  }
132 
133  /**
134  * Return the number of buckets that should be used on next growth.
135  */
136  std::size_t next_bucket_count() const {
137  if ((m_mask + 1) > max_bucket_count() / GrowthFactor) {
138  PXR_TSL_RH_THROW_OR_TERMINATE(std::length_error,
139  "The hash table exceeds its maximum size.");
140  }
141 
142  return (m_mask + 1) * GrowthFactor;
143  }
144 
145  /**
146  * Return the maximum number of buckets supported by the policy.
147  */
148  std::size_t max_bucket_count() const {
149  // Largest power of two.
150  return (std::numeric_limits<std::size_t>::max() / 2) + 1;
151  }
152 
153  /**
154  * Reset the growth policy as if it was created with a bucket count of 0.
155  * After a clear, the policy must always return 0 when bucket_for_hash is
156  * called.
157  */
158  void clear() noexcept { m_mask = 0; }
159 
160  private:
161  static std::size_t round_up_to_power_of_two(std::size_t value) {
162  if (is_power_of_two(value)) {
163  return value;
164  }
165 
166  if (value == 0) {
167  return 1;
168  }
169 
170  --value;
171  for (std::size_t i = 1; i < sizeof(std::size_t) * CHAR_BIT; i *= 2) {
172  value |= value >> i;
173  }
174 
175  return value + 1;
176  }
177 
178  static constexpr bool is_power_of_two(std::size_t value) {
179  return value != 0 && (value & (value - 1)) == 0;
180  }
181 
182  protected:
183  static_assert(is_power_of_two(GrowthFactor) && GrowthFactor >= 2,
184  "GrowthFactor must be a power of two >= 2.");
185 
186  std::size_t m_mask;
187 };
188 
189 /**
190  * Grow the hash table by GrowthFactor::num / GrowthFactor::den and use a modulo
191  * to map a hash to a bucket. Slower but it can be useful if you want a slower
192  * growth.
193  */
194 template <class GrowthFactor = std::ratio<3, 2>>
196  public:
197  explicit mod_growth_policy(std::size_t& min_bucket_count_in_out) {
198  if (min_bucket_count_in_out > max_bucket_count()) {
199  PXR_TSL_RH_THROW_OR_TERMINATE(std::length_error,
200  "The hash table exceeds its maximum size.");
201  }
202 
203  if (min_bucket_count_in_out > 0) {
204  m_mod = min_bucket_count_in_out;
205  } else {
206  m_mod = 1;
207  }
208  }
209 
210  std::size_t bucket_for_hash(std::size_t hash) const noexcept {
211  return hash % m_mod;
212  }
213 
214  std::size_t next_bucket_count() const {
215  if (m_mod == max_bucket_count()) {
216  PXR_TSL_RH_THROW_OR_TERMINATE(std::length_error,
217  "The hash table exceeds its maximum size.");
218  }
219 
220  const double next_bucket_count =
221  std::ceil(double(m_mod) * REHASH_SIZE_MULTIPLICATION_FACTOR);
222  if (!std::isnormal(next_bucket_count)) {
223  PXR_TSL_RH_THROW_OR_TERMINATE(std::length_error,
224  "The hash table exceeds its maximum size.");
225  }
226 
227  if (next_bucket_count > double(max_bucket_count())) {
228  return max_bucket_count();
229  } else {
230  return std::size_t(next_bucket_count);
231  }
232  }
233 
234  std::size_t max_bucket_count() const { return MAX_BUCKET_COUNT; }
235 
236  void clear() noexcept { m_mod = 1; }
237 
238  private:
239  static constexpr double REHASH_SIZE_MULTIPLICATION_FACTOR =
240  1.0 * GrowthFactor::num / GrowthFactor::den;
241  static const std::size_t MAX_BUCKET_COUNT =
242  std::size_t(double(std::numeric_limits<std::size_t>::max() /
243  REHASH_SIZE_MULTIPLICATION_FACTOR));
244 
245  static_assert(REHASH_SIZE_MULTIPLICATION_FACTOR >= 1.1,
246  "Growth factor should be >= 1.1.");
247 
248  std::size_t m_mod;
249 };
250 
251 namespace detail {
252 
253 #if SIZE_MAX >= ULLONG_MAX
254 #define PXR_TSL_RH_NB_PRIMES 51
255 #elif SIZE_MAX >= ULONG_MAX
256 #define PXR_TSL_RH_NB_PRIMES 40
257 #else
258 #define PXR_TSL_RH_NB_PRIMES 23
259 #endif
260 
261 static constexpr const std::array<std::size_t, PXR_TSL_RH_NB_PRIMES> PRIMES = {{
262  1u,
263  5u,
264  17u,
265  29u,
266  37u,
267  53u,
268  67u,
269  79u,
270  97u,
271  131u,
272  193u,
273  257u,
274  389u,
275  521u,
276  769u,
277  1031u,
278  1543u,
279  2053u,
280  3079u,
281  6151u,
282  12289u,
283  24593u,
284  49157u,
285 #if SIZE_MAX >= ULONG_MAX
286  98317ul,
287  196613ul,
288  393241ul,
289  786433ul,
290  1572869ul,
291  3145739ul,
292  6291469ul,
293  12582917ul,
294  25165843ul,
295  50331653ul,
296  100663319ul,
297  201326611ul,
298  402653189ul,
299  805306457ul,
300  1610612741ul,
301  3221225473ul,
302  4294967291ul,
303 #endif
304 #if SIZE_MAX >= ULLONG_MAX
305  6442450939ull,
306  12884901893ull,
307  25769803751ull,
308  51539607551ull,
309  103079215111ull,
310  206158430209ull,
311  412316860441ull,
312  824633720831ull,
313  1649267441651ull,
314  3298534883309ull,
315  6597069766657ull,
316 #endif
317 }};
318 
319 template <unsigned int IPrime>
320 static constexpr std::size_t mod(std::size_t hash) {
321  return hash % PRIMES[IPrime];
322 }
323 
324 // MOD_PRIME[iprime](hash) returns hash % PRIMES[iprime]. This table allows for
325 // faster modulo as the compiler can optimize the modulo code better with a
326 // constant known at the compilation.
327 static constexpr const std::array<std::size_t (*)(std::size_t),
329  MOD_PRIME = {{
330  &mod<0>, &mod<1>, &mod<2>, &mod<3>, &mod<4>, &mod<5>,
331  &mod<6>, &mod<7>, &mod<8>, &mod<9>, &mod<10>, &mod<11>,
332  &mod<12>, &mod<13>, &mod<14>, &mod<15>, &mod<16>, &mod<17>,
333  &mod<18>, &mod<19>, &mod<20>, &mod<21>, &mod<22>,
334 #if SIZE_MAX >= ULONG_MAX
335  &mod<23>, &mod<24>, &mod<25>, &mod<26>, &mod<27>, &mod<28>,
336  &mod<29>, &mod<30>, &mod<31>, &mod<32>, &mod<33>, &mod<34>,
337  &mod<35>, &mod<36>, &mod<37>, &mod<38>, &mod<39>,
338 #endif
339 #if SIZE_MAX >= ULLONG_MAX
340  &mod<40>, &mod<41>, &mod<42>, &mod<43>, &mod<44>, &mod<45>,
341  &mod<46>, &mod<47>, &mod<48>, &mod<49>, &mod<50>,
342 #endif
343  }};
344 
345 } // namespace detail
346 
347 /**
348  * Grow the hash table by using prime numbers as bucket count. Slower than
349  * tsl::rh::power_of_two_growth_policy in general but will probably distribute
350  * the values around better in the buckets with a poor hash function.
351  *
352  * To allow the compiler to optimize the modulo operation, a lookup table is
353  * used with constant primes numbers.
354  *
355  * With a switch the code would look like:
356  * \code
357  * switch(iprime) { // iprime is the current prime of the hash table
358  * case 0: hash % 5ul;
359  * break;
360  * case 1: hash % 17ul;
361  * break;
362  * case 2: hash % 29ul;
363  * break;
364  * ...
365  * }
366  * \endcode
367  *
368  * Due to the constant variable in the modulo the compiler is able to optimize
369  * the operation by a series of multiplications, substractions and shifts.
370  *
371  * The 'hash % 5' could become something like 'hash - (hash * 0xCCCCCCCD) >> 34)
372  * * 5' in a 64 bits environment.
373  */
375  public:
376  explicit prime_growth_policy(std::size_t& min_bucket_count_in_out) {
377  auto it_prime = std::lower_bound(
378  detail::PRIMES.begin(), detail::PRIMES.end(), min_bucket_count_in_out);
379  if (it_prime == detail::PRIMES.end()) {
380  PXR_TSL_RH_THROW_OR_TERMINATE(std::length_error,
381  "The hash table exceeds its maximum size.");
382  }
383 
384  m_iprime = static_cast<unsigned int>(
385  std::distance(detail::PRIMES.begin(), it_prime));
386  if (min_bucket_count_in_out > 0) {
387  min_bucket_count_in_out = *it_prime;
388  } else {
389  min_bucket_count_in_out = 0;
390  }
391  }
392 
393  std::size_t bucket_for_hash(std::size_t hash) const noexcept {
394  return detail::MOD_PRIME[m_iprime](hash);
395  }
396 
397  std::size_t next_bucket_count() const {
398  if (m_iprime + 1 >= detail::PRIMES.size()) {
399  PXR_TSL_RH_THROW_OR_TERMINATE(std::length_error,
400  "The hash table exceeds its maximum size.");
401  }
402 
403  return detail::PRIMES[m_iprime + 1];
404  }
405 
406  std::size_t max_bucket_count() const { return detail::PRIMES.back(); }
407 
408  void clear() noexcept { m_iprime = 0; }
409 
410  private:
411  unsigned int m_iprime;
412 
413  static_assert(std::numeric_limits<decltype(m_iprime)>::max() >=
414  detail::PRIMES.size(),
415  "The type of m_iprime is not big enough.");
416 };
417 
418 } // namespace rh
419 } // namespace pxr_tsl
420 
422 
423 #endif
std::size_t next_bucket_count() const
T mod(T x, int y)
Definition: chrono.h:1648
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:1425
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:74
prime_growth_policy(std::size_t &min_bucket_count_in_out)
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:566
#define PXR_TSL_RH_NB_PRIMES