/src/brpc/src/butil/fast_rand.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, |
12 | | // software distributed under the License is distributed on an |
13 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | | // KIND, either express or implied. See the License for the |
15 | | // specific language governing permissions and limitations |
16 | | // under the License. |
17 | | |
18 | | // Date: Thu Dec 31 13:35:39 CST 2015 |
19 | | |
20 | | #include <limits> // numeric_limits |
21 | | #include <math.h> |
22 | | #include "butil/basictypes.h" |
23 | | #include "butil/macros.h" |
24 | | #include "butil/time.h" // gettimeofday_us() |
25 | | #include "butil/fast_rand.h" |
26 | | |
27 | | namespace butil { |
28 | | |
29 | | // This state can be seeded with any value. |
30 | | typedef uint64_t SplitMix64Seed; |
31 | | |
32 | | // A very fast generator passing BigCrush. Due to its 64-bit state, it's |
33 | | // solely for seeding xorshift128+. |
34 | 0 | inline uint64_t splitmix64_next(SplitMix64Seed* seed) { |
35 | 0 | uint64_t z = (*seed += UINT64_C(0x9E3779B97F4A7C15)); |
36 | 0 | z = (z ^ (z >> 30)) * UINT64_C(0xBF58476D1CE4E5B9); |
37 | 0 | z = (z ^ (z >> 27)) * UINT64_C(0x94D049BB133111EB); |
38 | 0 | return z ^ (z >> 31); |
39 | 0 | } |
40 | | |
41 | | // xorshift128+ is the fastest generator passing BigCrush without systematic |
42 | | // failures |
43 | 0 | inline uint64_t xorshift128_next(FastRandSeed* seed) { |
44 | 0 | uint64_t s1 = seed->s[0]; |
45 | 0 | const uint64_t s0 = seed->s[1]; |
46 | 0 | seed->s[0] = s0; |
47 | 0 | s1 ^= s1 << 23; // a |
48 | 0 | seed->s[1] = s1 ^ s0 ^ (s1 >> 18) ^ (s0 >> 5); // b, c |
49 | 0 | return seed->s[1] + s0; |
50 | 0 | } |
51 | | |
52 | | // seed xorshift128+ with splitmix64. |
53 | 0 | void init_fast_rand_seed(FastRandSeed* seed) { |
54 | 0 | SplitMix64Seed seed4seed = butil::gettimeofday_us(); |
55 | 0 | seed->s[0] = splitmix64_next(&seed4seed); |
56 | 0 | seed->s[1] = splitmix64_next(&seed4seed); |
57 | 0 | } |
58 | | |
59 | 0 | inline uint64_t fast_rand_impl(uint64_t range, FastRandSeed* seed) { |
60 | | // Separating uint64_t values into following intervals: |
61 | | // [0,range-1][range,range*2-1] ... [uint64_max/range*range,uint64_max] |
62 | | // If the generated 64-bit random value falls into any interval except the |
63 | | // last one, the probability of taking any value inside [0, range-1] is |
64 | | // same. If the value falls into last interval, we retry the process until |
65 | | // the value falls into other intervals. If min/max are limited to 32-bits, |
66 | | // the retrying is rare. The amortized retrying count at maximum is 1 when |
67 | | // range equals 2^32. A corner case is that even if the range is power of |
68 | | // 2(e.g. min=0 max=65535) in which case the retrying can be avoided, we |
69 | | // still retry currently. The reason is just to keep the code simpler |
70 | | // and faster for most cases. |
71 | 0 | const uint64_t div = std::numeric_limits<uint64_t>::max() / range; |
72 | 0 | uint64_t result; |
73 | 0 | do { |
74 | 0 | result = xorshift128_next(seed) / div; |
75 | 0 | } while (result >= range); |
76 | 0 | return result; |
77 | 0 | } |
78 | | |
79 | | // Seeds for different threads are stored separately in thread-local storage. |
80 | | static __thread FastRandSeed _tls_seed = { { 0, 0 } }; |
81 | | |
82 | | // True if the seed is (probably) uninitialized. There's definitely false |
83 | | // positive, but it's OK for us. |
84 | 0 | inline bool need_init(const FastRandSeed& seed) { |
85 | 0 | return seed.s[0] == 0 && seed.s[1] == 0; |
86 | 0 | } |
87 | | |
88 | 0 | uint64_t fast_rand() { |
89 | 0 | if (need_init(_tls_seed)) { |
90 | 0 | init_fast_rand_seed(&_tls_seed); |
91 | 0 | } |
92 | 0 | return xorshift128_next(&_tls_seed); |
93 | 0 | } |
94 | | |
95 | 0 | uint64_t fast_rand(FastRandSeed* seed) { |
96 | 0 | return xorshift128_next(seed); |
97 | 0 | } |
98 | | |
99 | 0 | uint64_t fast_rand_less_than(uint64_t range) { |
100 | 0 | if (range == 0) { |
101 | 0 | return 0; |
102 | 0 | } |
103 | 0 | if (need_init(_tls_seed)) { |
104 | 0 | init_fast_rand_seed(&_tls_seed); |
105 | 0 | } |
106 | 0 | return fast_rand_impl(range, &_tls_seed); |
107 | 0 | } |
108 | | |
109 | 0 | int64_t fast_rand_in_64(int64_t min, int64_t max) { |
110 | 0 | if (need_init(_tls_seed)) { |
111 | 0 | init_fast_rand_seed(&_tls_seed); |
112 | 0 | } |
113 | 0 | if (min >= max) { |
114 | 0 | if (min == max) { |
115 | 0 | return min; |
116 | 0 | } |
117 | 0 | const int64_t tmp = min; |
118 | 0 | min = max; |
119 | 0 | max = tmp; |
120 | 0 | } |
121 | 0 | int64_t range = max - min + 1; |
122 | 0 | if (range == 0) { |
123 | | // max = INT64_MAX, min = INT64_MIN |
124 | 0 | return (int64_t)xorshift128_next(&_tls_seed); |
125 | 0 | } |
126 | 0 | return min + (int64_t)fast_rand_impl(max - min + 1, &_tls_seed); |
127 | 0 | } |
128 | | |
129 | 0 | uint64_t fast_rand_in_u64(uint64_t min, uint64_t max) { |
130 | 0 | if (need_init(_tls_seed)) { |
131 | 0 | init_fast_rand_seed(&_tls_seed); |
132 | 0 | } |
133 | 0 | if (min >= max) { |
134 | 0 | if (min == max) { |
135 | 0 | return min; |
136 | 0 | } |
137 | 0 | const uint64_t tmp = min; |
138 | 0 | min = max; |
139 | 0 | max = tmp; |
140 | 0 | } |
141 | 0 | uint64_t range = max - min + 1; |
142 | 0 | if (range == 0) { |
143 | | // max = UINT64_MAX, min = UINT64_MIN |
144 | 0 | return xorshift128_next(&_tls_seed); |
145 | 0 | } |
146 | 0 | return min + fast_rand_impl(range, &_tls_seed); |
147 | 0 | } |
148 | | |
149 | 0 | inline double fast_rand_double(FastRandSeed* seed) { |
150 | | // Copied from rand_util.cc |
151 | 0 | COMPILE_ASSERT(std::numeric_limits<double>::radix == 2, otherwise_use_scalbn); |
152 | 0 | static const int kBits = std::numeric_limits<double>::digits; |
153 | 0 | uint64_t random_bits = xorshift128_next(seed) & ((UINT64_C(1) << kBits) - 1); |
154 | 0 | double result = ldexp(static_cast<double>(random_bits), -1 * kBits); |
155 | 0 | return result; |
156 | 0 | } |
157 | | |
158 | 0 | double fast_rand_double() { |
159 | 0 | if (need_init(_tls_seed)) { |
160 | 0 | init_fast_rand_seed(&_tls_seed); |
161 | 0 | } |
162 | 0 | return fast_rand_double(&_tls_seed); |
163 | 0 | } |
164 | | |
165 | 0 | void fast_rand_bytes(void* output, size_t output_length) { |
166 | 0 | const size_t n = output_length / 8; |
167 | 0 | for (size_t i = 0; i < n; ++i) { |
168 | 0 | static_cast<uint64_t*>(output)[i] = fast_rand(); |
169 | 0 | } |
170 | 0 | const size_t m = output_length - n * 8; |
171 | 0 | if (m) { |
172 | 0 | uint8_t* p = static_cast<uint8_t*>(output) + n * 8; |
173 | 0 | uint64_t r = fast_rand(); |
174 | 0 | for (size_t i = 0; i < m; ++i) { |
175 | 0 | p[i] = (r & 0xFF); |
176 | 0 | r = (r >> 8); |
177 | 0 | } |
178 | 0 | } |
179 | 0 | } |
180 | | |
181 | 0 | std::string fast_rand_printable(size_t length) { |
182 | 0 | std::string result(length, 0); |
183 | 0 | const size_t halflen = length/2; |
184 | 0 | fast_rand_bytes(&result[0], halflen); |
185 | 0 | for (size_t i = 0; i < halflen; ++i) { |
186 | 0 | const uint8_t b = result[halflen - 1 - i]; |
187 | 0 | result[length - 1 - 2*i] = 'A' + (b & 0xF); |
188 | 0 | result[length - 2 - 2*i] = 'A' + (b >> 4); |
189 | 0 | } |
190 | 0 | if (halflen * 2 != length) { |
191 | 0 | result[0] = 'A' + (fast_rand() % 16); |
192 | 0 | } |
193 | 0 | return result; |
194 | 0 | } |
195 | | |
196 | | } // namespace butil |