alpaka
Abstraction Library for Parallel Kernel Acceleration
Loading...
Searching...
No Matches
PhiloxSingle.hpp
Go to the documentation of this file.
1/* Copyright 2022 Jiri Vyskocil, Rene Widera, Bernhard Manfred Gruber
2 * SPDX-License-Identifier: MPL-2.0
3 */
4
5#pragma once
6
9
10#include <utility>
11
12namespace alpaka::rand::engine::internal
13{
14
15
16 /** Philox engine generating a single number
17 *
18 * This engine's operator() will return a single number. Since the result is the same size as the counter,
19 * and so it contains more than one number, it has to be stored between individual invocations of
20 * operator(). Additionally a pointer has to be stored indicating which part of the result array is to be
21 * returned next.
22 *
23 * @tparam TParams Basic parameters for the Philox algorithm
24 */
25 template<typename TParams>
26 class PhiloxSingle : public PhiloxBaseCommon<TParams, PhiloxSingle<TParams>>
27 {
28 public:
29 using Base = PhiloxBaseCommon<TParams, PhiloxSingle<TParams>>;
30
31 /// Counter type
32 using Counter = typename Base::Counter;
33 /// Key type
34 using Key = typename Base::Key;
35 using State = PhiloxState<Counter, Key, PhiloxSingle<TParams>>;
36
37
38 protected:
39 /** Advance internal counter to the next value
40 *
41 * Advances the full internal counter array, resets the position pointer and stores the intermediate
42 * result to be recalled when the user requests a number.
43 */
44 constexpr void advanceState()
45 {
46 this->advanceCounter(this->state.counter);
47 this->state.result = this->nRounds(this->state.counter, this->state.key);
48 this->state.position = 0;
49 }
50
51 /** Get the next random number and advance internal state
52 *
53 * The intermediate result stores N = TParams::counterSize numbers. Check if we've already given out
54 * all of them. If so, generate a new intermediate result (this also resets the pointer to the position
55 * of the actual number). Finally, we return the actual number.
56 *
57 * @return The next random number
58 */
59 constexpr auto nextNumber()
60 {
61 // Element zero will always contain the next valid random number.
62 auto result = this->state.result[0];
63 ++this->state.position;
64 if(this->state.position == TParams::counterSize)
65 {
66 advanceState();
67 }
68 else
69 {
70 /* Shift state results to allow hard coded access to element zero.
71 * This will avoid high register usage on NVIDIA devices.
72 * @todo Check if this shifting of the result vector is decreasing CPU performance.
73 * If so this optimization for GPUs (mostly NVIDIA/AMD) should be made optional.
74 */
75 this->state.result[0] = this->state.result[1];
76 this->state.result[1] = this->state.result[2];
77 this->state.result[2] = this->state.result[3];
78 }
79
80 return result;
81 }
82
83 /// Skips the next \a offset numbers
84 constexpr void skip(uint64_t offset)
85 {
86 static_assert(TParams::counterSize == 4, "Only counterSize is supported.");
87 this->state.position = static_cast<decltype(this->state.position)>(this->state.position + (offset & 3));
88 offset += this->state.position < 4 ? 0 : 4;
89 this->state.position -= this->state.position < 4 ? 0 : 4u;
90 for(auto numShifts = this->state.position; numShifts > 0; --numShifts)
91 {
92 // Shift state results to allow hard coded access to element zero.
93 // This will avoid high register usage on NVIDIA devices.
94 this->state.result[0] = this->state.result[1];
95 this->state.result[1] = this->state.result[2];
96 this->state.result[2] = this->state.result[3];
97 }
98 this->skip4(offset / 4);
99 }
100
101 public:
102 /** Construct a new Philox engine with single-value output
103 *
104 * @param seed Set the Philox generator key
105 * @param subsequence Select a subsequence of size 2^64
106 * @param offset Skip \a offset numbers form the start of the subsequence
107 */
108 constexpr PhiloxSingle(uint64_t seed = 0, uint64_t subsequence = 0, uint64_t offset = 0)
109 : Base(State{{0, 0, 0, 0}, {low32Bits(seed), high32Bits(seed)}, {0, 0, 0, 0}, 0u})
110 {
111 this->skipSubsequence(subsequence);
112 skip(offset);
113 advanceState();
114 }
115
116 /** Get the next random number
117 *
118 * @return The next random number
119 */
120 constexpr auto operator()()
121 {
122 return nextNumber();
123 }
124 };
125} // namespace alpaka::rand::engine::internal