vSMC
vSMC: Scalable Monte Carlo
counter.hpp
Go to the documentation of this file.
1 //============================================================================
2 // vSMC/include/vsmc/rng/counter.hpp
3 //----------------------------------------------------------------------------
4 // vSMC: Scalable Monte Carlo
5 //----------------------------------------------------------------------------
6 // Copyright (c) 2013-2016, Yan Zhou
7 // All rights reserved.
8 //
9 // Redistribution and use in source and binary forms, with or without
10 // modification, are permitted provided that the following conditions are met:
11 //
12 // Redistributions of source code must retain the above copyright notice,
13 // this list of conditions and the following disclaimer.
14 //
15 // Redistributions in binary form must reproduce the above copyright notice,
16 // this list of conditions and the following disclaimer in the documentation
17 // and/or other materials provided with the distribution.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS
20 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 // POSSIBILITY OF SUCH DAMAGE.
30 //============================================================================
31 
32 #ifndef VSMC_RNG_COUNTER_HPP
33 #define VSMC_RNG_COUNTER_HPP
34 
36 
37 namespace vsmc
38 {
39 
40 namespace internal
41 {
42 
43 template <std::size_t, typename T, std::size_t K>
44 inline void increment_single(std::array<T, K> &, std::false_type)
45 {
46 }
47 
48 template <std::size_t N, typename T, std::size_t K>
49 inline void increment_single(std::array<T, K> &ctr, std::true_type)
50 {
51  if (++std::get<N>(ctr) != 0)
52  return;
53 
54  increment_single<N + 1>(ctr, std::integral_constant<bool, N + 1 < K>());
55 }
56 
57 } // namespace vsmc::internal
58 
61 template <typename T, std::size_t K>
62 inline void increment(std::array<T, K> &ctr)
63 {
64  internal::increment_single<0>(ctr, std::true_type());
65 }
66 
69 template <typename T, std::size_t K, T NSkip>
70 inline void increment(std::array<T, K> &ctr, std::integral_constant<T, NSkip>)
71 {
72  if (ctr.front() < std::numeric_limits<T>::max() - NSkip) {
73  ctr.front() += NSkip;
74  } else {
75  ctr.front() += NSkip;
76  internal::increment_single<1>(
77  ctr, std::integral_constant<bool, 1 < K>());
78  }
79 }
80 
83 template <typename T, std::size_t K>
84 inline void increment(std::array<T, K> &ctr, T nskip)
85 {
86  if (ctr.front() < std::numeric_limits<T>::max() - nskip) {
87  ctr.front() += nskip;
88  } else {
89  ctr.front() += nskip;
90  internal::increment_single<1>(
91  ctr, std::integral_constant<bool, 1 < K>());
92  }
93 }
94 
95 namespace internal
96 {
97 
98 template <std::size_t, typename T, std::size_t K, std::size_t Blocks>
99 inline void increment_block_set(const std::array<T, K> &,
100  std::array<std::array<T, K>, Blocks> &, std::false_type)
101 {
102 }
103 
104 template <std::size_t B, typename T, std::size_t K, std::size_t Blocks>
105 inline void increment_block_set(const std::array<T, K> &ctr,
106  std::array<std::array<T, K>, Blocks> &ctr_block, std::true_type)
107 {
108  std::get<B>(ctr_block) = ctr;
109  increment_block_set<B + 1>(
110  ctr, ctr_block, std::integral_constant<bool, B + 1 < Blocks>());
111 }
112 
113 template <std::size_t, typename T, std::size_t K, std::size_t Blocks>
114 inline void increment_block(std::array<T, K> &,
115  std::array<std::array<T, K>, Blocks> &, std::false_type)
116 {
117 }
118 
119 template <std::size_t B, typename T, std::size_t K, std::size_t Blocks>
120 inline void increment_block(std::array<T, K> &ctr,
121  std::array<std::array<T, K>, Blocks> &ctr_block, std::true_type)
122 {
123  increment(std::get<B>(ctr_block), std::integral_constant<T, B + 1>());
124  increment_block<B + 1>(
125  ctr, ctr_block, std::integral_constant<bool, B + 1 < Blocks>());
126 }
127 
128 template <std::size_t, typename T, std::size_t K, std::size_t Blocks>
129 inline void increment_block_safe(std::array<T, K> &,
130  std::array<std::array<T, K>, Blocks> &, std::false_type)
131 {
132 }
133 
134 template <std::size_t B, typename T, std::size_t K, std::size_t Blocks>
135 inline void increment_block_safe(std::array<T, K> &ctr,
136  std::array<std::array<T, K>, Blocks> &ctr_block, std::true_type)
137 {
138  std::get<B>(ctr_block).front() += B + 1;
139  increment_block_safe<B + 1>(
140  ctr, ctr_block, std::integral_constant<bool, B + 1 < Blocks>());
141 }
142 
143 } // namespace vsmc::internal
144 
148 template <typename T, std::size_t K, std::size_t Blocks>
149 inline void increment(
150  std::array<T, K> &ctr, std::array<std::array<T, K>, Blocks> &ctr_block)
151 {
152  internal::increment_block_set<0>(
153  ctr, ctr_block, std::integral_constant<bool, 0 < Blocks>());
154  if (ctr.front() < std::numeric_limits<T>::max() - static_cast<T>(Blocks)) {
155  internal::increment_block_safe<0>(
156  ctr, ctr_block, std::integral_constant<bool, 0 < Blocks>());
157  } else {
158  internal::increment_block<0>(
159  ctr, ctr_block, std::integral_constant<bool, 0 < Blocks>());
160  }
161  ctr = ctr_block.back();
162 }
163 
164 namespace internal
165 {
166 
167 template <typename T, std::size_t K>
168 inline void increment_block_set(const std::array<T, K> &ctr, std::size_t n,
169  std::array<T, K> *ctr_block, std::false_type)
170 {
171  for (std::size_t i = 0; i != n; ++i)
172  ctr_block[i] = ctr;
173 }
174 
175 #if VSMC_HAS_AVX2
176 
177 template <typename T, std::size_t K>
178 inline void increment_block_set(const std::array<T, K> &ctr, std::size_t n,
179  std::array<T, K> *ctr_block, std::true_type)
180 {
181  const std::size_t Blocks = M256I<T>::size() / K;
182  const std::size_t m = n / Blocks;
183  const std::size_t l = n % Blocks;
184  M256I<> c;
185  std::array<std::array<T, K>, Blocks> cb;
186  increment_block_set<0>(ctr, cb, std::true_type());
187  c.load(cb.data());
188  if (reinterpret_cast<std::uintptr_t>(ctr_block) % 32 == 0) {
189  for (std::size_t i = 0; i != m; ++i, ctr_block += Blocks)
190  c.store_a(ctr_block);
191  } else {
192  for (std::size_t i = 0; i != m; ++i, ctr_block += Blocks)
193  c.store_u(ctr_block);
194  }
195  for (std::size_t i = 0; i != l; ++i)
196  ctr_block[i] = ctr;
197 }
198 
199 template <typename T, std::size_t K>
201  const std::array<T, K> &ctr, std::size_t n, std::array<T, K> *ctr_block)
202 {
203  increment_block_set(ctr, n, ctr_block,
204  std::integral_constant<bool, M256I<T>::size() % K == 0>());
205 }
206 
207 #elif VSMC_HAS_SSE2
208 
209 template <typename T, std::size_t K>
210 inline void increment_block_set(const std::array<T, K> &ctr, std::size_t n,
211  std::array<T, K> *ctr_block, std::true_type)
212 {
213  const std::size_t Blocks = M128I<T>::size() / K;
214  const std::size_t m = n / Blocks;
215  const std::size_t l = n % Blocks;
216  M128I<> c;
217  std::array<std::array<T, K>, Blocks> cb;
218  increment_block_set<0>(ctr, cb, std::true_type());
219  c.load(cb.data());
220  if (reinterpret_cast<std::uintptr_t>(ctr_block) % 16 == 0) {
221  for (std::size_t i = 0; i != m; ++i, ctr_block += Blocks)
222  c.store_a(ctr_block);
223  } else {
224  for (std::size_t i = 0; i != m; ++i, ctr_block += Blocks)
225  c.store_u(ctr_block);
226  }
227  for (std::size_t i = 0; i != l; ++i)
228  ctr_block[i] = ctr;
229 }
230 
231 template <typename T, std::size_t K>
232 inline void increment_block_set(
233  const std::array<T, K> &ctr, std::size_t n, std::array<T, K> *ctr_block)
234 {
235  increment_block_set(ctr, n, ctr_block,
236  std::integral_constant<bool, M128I<T>::size() % K == 0>());
237 }
238 
239 #else // VSMC_HAS_SSE2
240 
241 template <typename T, std::size_t K>
242 inline void increment_block_set(
243  const std::array<T, K> &ctr, std::size_t n, std::array<T, K> *ctr_block)
244 {
245  increment_block_set(ctr, n, ctr_block, std::false_type());
246 }
247 
248 #endif // VSMC_HAS_SSE2
249 
250 } // namespace vsmc::internal
251 
255 template <typename T, std::size_t K>
256 inline void increment(
257  std::array<T, K> &ctr, std::size_t n, std::array<T, K> *ctr_block)
258 {
259  if (n == 0)
260  return;
261 
262  increment(ctr);
263  const std::uint64_t m =
264  static_cast<std::uint64_t>(std::numeric_limits<T>::max());
265  const std::uint64_t l = static_cast<std::uint64_t>(ctr.front());
266  const std::uint64_t k = static_cast<std::uint64_t>(n);
267  if (k < m && l < m - k) {
268  internal::increment_block_set(ctr, n, ctr_block);
269  const T p = static_cast<T>(n);
270  for (T i = 0; i != p; ++i)
271  ctr_block[i].front() += i;
272  } else if (k < m) {
273  internal::increment_block_set(ctr, n, ctr_block);
274  const T p = static_cast<T>(n);
275  for (T i = 0; i != p; ++i)
276  increment(ctr_block[i], i);
277  } else {
278  for (std::size_t i = 0; i != n; ++i) {
279  ctr_block[i] = ctr;
280  increment(ctr);
281  }
282  }
283  ctr = ctr_block[n - 1];
284 }
285 
288 template <typename Generator>
290 {
291  public:
292  using result_type = typename Generator::result_type;
293  using ctr_type = typename Generator::ctr_type;
294  using key_type = typename Generator::key_type;
295 
296  explicit CounterEngine(result_type s = 0) : index_(M_) { seed(s); }
297 
298  template <typename SeedSeq>
299  explicit CounterEngine(SeedSeq &seq,
300  typename std::enable_if<internal::is_seed_seq<SeedSeq, result_type,
301  key_type, CounterEngine<Generator>>::value>::type * = nullptr)
302  : index_(M_)
303  {
304  seed(seq);
305  }
306 
307  explicit CounterEngine(const key_type &k) : index_(M_) { seed(k); }
308 
310  {
311  key_.fill(0);
312  key_.front() = s;
313  reset();
314  }
315 
316  template <typename SeedSeq>
317  void seed(
318  SeedSeq &seq, typename std::enable_if<internal::is_seed_seq<SeedSeq,
319  result_type, key_type>::value>::type * = nullptr)
320  {
321  seq.generator(key_.begin(), key_.end());
322  reset();
323  }
324 
325  void seed(const key_type &k)
326  {
327  key_ = k;
328  reset();
329  }
330 
331  const ctr_type &ctr() const { return ctr_; }
332 
333  const key_type &key() const { return key_; }
334 
335  void ctr(const ctr_type &c)
336  {
337  ctr_ = c;
338  index_ = M_;
339  }
340 
341  void key(const key_type &k)
342  {
343  key_ = k;
344  reset();
345  }
346 
348  {
349  if (index_ == M_) {
350  generator_(ctr_, key_, buffer_);
351  index_ = 0;
352  }
353 
354  return buffer_[index_++];
355  }
356 
357  void operator()(std::size_t n, result_type *r)
358  {
359  const std::size_t remain = M_ - index_;
360 
361  if (n < remain) {
362  std::memcpy(r, buffer_.data() + index_, sizeof(result_type) * n);
363  index_ += n;
364  return;
365  }
366 
367  std::memcpy(r, buffer_.data() + index_, sizeof(result_type) * remain);
368  r += remain;
369  n -= remain;
370  index_ = M_;
371 
372  const std::size_t k = 1024 / M_;
373  if (k != 0) {
374  const std::size_t m = (n / M_) / k;
375  const std::size_t l = (n / M_) % k;
376  alignas(32) std::array<result_type, M_> buffer[k];
377  for (std::size_t i = 0; i != m; ++i) {
378  generator_(ctr_, key_, k, buffer);
379  std::memcpy(r, buffer, sizeof(result_type) * M_ * k);
380  r += k * M_;
381  n -= k * M_;
382  }
383  generator_(ctr_, key_, l, buffer);
384  std::memcpy(r, buffer, sizeof(result_type) * M_ * l);
385  r += l * M_;
386  n -= l * M_;
387  }
388  for (std::size_t i = 0; i != n; ++i)
389  r[i] = operator()();
390  }
391 
392  void discard(result_type nskip)
393  {
394  std::size_t n = static_cast<std::size_t>(nskip);
395  if (index_ + n <= M_) {
396  index_ += n;
397  return;
398  }
399 
400  n -= M_ - index_;
401  if (n <= M_) {
402  index_ = M_;
403  operator()();
404  index_ = n;
405  return;
406  }
407 
408  increment(ctr_, static_cast<result_type>(n / M_));
409  index_ = M_;
410  operator()();
411  index_ = n % M_;
412  }
413 
414  static constexpr result_type min()
415  {
416  return std::numeric_limits<result_type>::min();
417  }
418 
419  static constexpr result_type max()
420  {
421  return std::numeric_limits<result_type>::max();
422  }
423 
424  friend bool operator==(const CounterEngine<Generator> &eng1,
425  const CounterEngine<Generator> &eng2)
426  {
427  if (eng1.buffer_ != eng2.buffer_)
428  return false;
429  if (eng1.ctr_ != eng2.ctr_)
430  return false;
431  if (eng1.key_ != eng2.key_)
432  return false;
433  if (eng1.index_ != eng2.index_)
434  return false;
435  return true;
436  }
437 
438  friend bool operator!=(const CounterEngine<Generator> &eng1,
439  const CounterEngine<Generator> &eng2)
440  {
441  return !(eng1 == eng2);
442  }
443 
444  template <typename CharT, typename Traits>
445  friend std::basic_ostream<CharT, Traits> &operator<<(
446  std::basic_ostream<CharT, Traits> &os,
447  const CounterEngine<Generator> &eng)
448  {
449  if (!os.good())
450  return os;
451 
452  os << eng.buffer_ << ' ';
453  os << eng.ctr_ << ' ';
454  os << eng.key_ << ' ';
455  os << eng.index_;
456 
457  return os;
458  }
459 
460  template <typename CharT, typename Traits>
461  friend std::basic_istream<CharT, Traits> &operator>>(
462  std::basic_istream<CharT, Traits> &is, CounterEngine<Generator> &eng)
463  {
464  if (!is.good())
465  return is;
466 
467  CounterEngine<Generator> eng_tmp;
468  is >> std::ws >> eng_tmp.buffer_;
469  is >> std::ws >> eng_tmp.ctr_;
470  is >> std::ws >> eng_tmp.key_;
471  is >> std::ws >> eng_tmp.index_;
472 
473  if (is.good()) {
474  eng_tmp.generator_.reset(eng_tmp.key_);
475  eng = std::move(eng_tmp);
476  }
477 
478  return is;
479  }
480 
481  private:
482  static constexpr std::size_t M_ = Generator::size();
483 
484  alignas(32) std::array<result_type, M_> buffer_;
485  std::size_t index_;
486  ctr_type ctr_;
487  key_type key_;
488  Generator generator_;
489 
490  void reset()
491  {
492  ctr_.fill(0);
493  generator_.reset(key_);
494  index_ = M_;
495  }
496 }; // class CounterEngine
497 
498 template <typename Generator>
499 inline void rng_rand(CounterEngine<Generator> &rng, std::size_t n,
501 {
502  rng(n, r);
503 }
504 
505 } // namespace vsmc
506 
507 #endif // VSMC_RNG_COUNTER_HPP
Definition: monitor.hpp:49
void load(const T *mem)
Definition: simd.hpp:850
Using __mm256i as integer vector.
Definition: simd.hpp:804
const ctr_type & ctr() const
Definition: counter.hpp:331
const key_type & key() const
Definition: counter.hpp:333
friend bool operator==(const CounterEngine< Generator > &eng1, const CounterEngine< Generator > &eng2)
Definition: counter.hpp:424
void store_a(T *mem) const
Definition: simd.hpp:857
result_type operator()()
Definition: counter.hpp:347
void increment(std::array< T, K > &ctr)
Increment a counter by one.
Definition: counter.hpp:62
typename Generator::key_type key_type
Definition: counter.hpp:294
static constexpr std::size_t size()
Definition: simd.hpp:826
ulong uint64_t
Definition: opencl.h:40
typename Generator::ctr_type ctr_type
Definition: counter.hpp:293
void rng_rand(RNGType &rng, BetaDistribution< RealType > &dist, std::size_t n, RealType *r)
void increment_block(std::array< T, K > &, std::array< std::array< T, K >, Blocks > &, std::false_type)
Definition: counter.hpp:114
Counter based RNG engine.
Definition: counter.hpp:289
void store_u(T *mem) const
Definition: simd.hpp:863
CounterEngine(SeedSeq &seq, typename std::enable_if< internal::is_seed_seq< SeedSeq, result_type, key_type, CounterEngine< Generator >>::value >::type *=nullptr)
Definition: counter.hpp:299
void increment_block_set(const std::array< T, K > &, std::array< std::array< T, K >, Blocks > &, std::false_type)
Definition: counter.hpp:99
void operator()(std::size_t n, result_type *r)
Definition: counter.hpp:357
CounterEngine(result_type s=0)
Definition: counter.hpp:296
void key(const key_type &k)
Definition: counter.hpp:341
static constexpr result_type max()
Definition: counter.hpp:419
friend std::basic_ostream< CharT, Traits > & operator<<(std::basic_ostream< CharT, Traits > &os, const CounterEngine< Generator > &eng)
Definition: counter.hpp:445
static constexpr result_type min()
Definition: counter.hpp:414
typename Generator::result_type result_type
Definition: counter.hpp:292
static constexpr std::size_t size()
Definition: simd.hpp:135
void ctr(const ctr_type &c)
Definition: counter.hpp:335
void seed(SeedSeq &seq, typename std::enable_if< internal::is_seed_seq< SeedSeq, result_type, key_type >::value >::type *=nullptr)
Definition: counter.hpp:317
friend bool operator!=(const CounterEngine< Generator > &eng1, const CounterEngine< Generator > &eng2)
Definition: counter.hpp:438
CounterEngine(const key_type &k)
Definition: counter.hpp:307
void load(const T *mem)
Definition: simd.hpp:159
void seed(result_type s)
Definition: counter.hpp:309
void seed(const key_type &k)
Definition: counter.hpp:325
void store_u(T *mem) const
Definition: simd.hpp:172
void discard(result_type nskip)
Definition: counter.hpp:392
friend std::basic_istream< CharT, Traits > & operator>>(std::basic_istream< CharT, Traits > &is, CounterEngine< Generator > &eng)
Definition: counter.hpp:461
void increment_block_safe(std::array< T, K > &, std::array< std::array< T, K >, Blocks > &, std::false_type)
Definition: counter.hpp:129
void store_a(T *mem) const
Definition: simd.hpp:166
void increment_single(std::array< T, K > &, std::false_type)
Definition: counter.hpp:44