/src/brpc/src/butil/fast_rand.cpp
Line | Count | Source |
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 | | #include "butil/numerics/safe_conversions.h" // safe_abs |
27 | | |
28 | | namespace butil { |
29 | | |
30 | | // This state can be seeded with any value. |
31 | | typedef uint64_t SplitMix64Seed; |
32 | | |
33 | | // A very fast generator passing BigCrush. Due to its 64-bit state, it's |
34 | | // solely for seeding xorshift128+. |
35 | 0 | inline uint64_t splitmix64_next(SplitMix64Seed* seed) { |
36 | 0 | uint64_t z = (*seed += UINT64_C(0x9E3779B97F4A7C15)); |
37 | 0 | z = (z ^ (z >> 30)) * UINT64_C(0xBF58476D1CE4E5B9); |
38 | 0 | z = (z ^ (z >> 27)) * UINT64_C(0x94D049BB133111EB); |
39 | 0 | return z ^ (z >> 31); |
40 | 0 | } |
41 | | |
42 | | // xorshift128+ is the fastest generator passing BigCrush without systematic |
43 | | // failures |
44 | 0 | inline uint64_t xorshift128_next(FastRandSeed* seed) { |
45 | 0 | uint64_t s1 = seed->s[0]; |
46 | 0 | const uint64_t s0 = seed->s[1]; |
47 | 0 | seed->s[0] = s0; |
48 | 0 | s1 ^= s1 << 23; // a |
49 | 0 | seed->s[1] = s1 ^ s0 ^ (s1 >> 18) ^ (s0 >> 5); // b, c |
50 | 0 | return seed->s[1] + s0; |
51 | 0 | } |
52 | | |
53 | | // seed xorshift128+ with splitmix64. |
54 | 0 | void init_fast_rand_seed(FastRandSeed* seed) { |
55 | 0 | SplitMix64Seed seed4seed = butil::gettimeofday_us(); |
56 | 0 | seed->s[0] = splitmix64_next(&seed4seed); |
57 | 0 | seed->s[1] = splitmix64_next(&seed4seed); |
58 | 0 | } |
59 | | |
60 | 0 | inline uint64_t fast_rand_impl(uint64_t range, FastRandSeed* seed) { |
61 | | // Separating uint64_t values into following intervals: |
62 | | // [0,range-1][range,range*2-1] ... [uint64_max/range*range,uint64_max] |
63 | | // If the generated 64-bit random value falls into any interval except the |
64 | | // last one, the probability of taking any value inside [0, range-1] is |
65 | | // same. If the value falls into last interval, we retry the process until |
66 | | // the value falls into other intervals. If min/max are limited to 32-bits, |
67 | | // the retrying is rare. The amortized retrying count at maximum is 1 when |
68 | | // range equals 2^32. A corner case is that even if the range is power of |
69 | | // 2(e.g. min=0 max=65535) in which case the retrying can be avoided, we |
70 | | // still retry currently. The reason is just to keep the code simpler |
71 | | // and faster for most cases. |
72 | 0 | const uint64_t div = std::numeric_limits<uint64_t>::max() / range; |
73 | 0 | uint64_t result; |
74 | 0 | do { |
75 | 0 | result = xorshift128_next(seed) / div; |
76 | 0 | } while (result >= range); |
77 | 0 | return result; |
78 | 0 | } |
79 | | |
80 | | // Seeds for different threads are stored separately in thread-local storage. |
81 | | static __thread FastRandSeed _tls_seed = { { 0, 0 } }; |
82 | | |
83 | | // True if the seed is (probably) uninitialized. There's definitely false |
84 | | // positive, but it's OK for us. |
85 | 0 | inline bool need_init(const FastRandSeed& seed) { |
86 | 0 | return seed.s[0] == 0 && seed.s[1] == 0; |
87 | 0 | } |
88 | | |
89 | 0 | uint64_t fast_rand() { |
90 | 0 | if (need_init(_tls_seed)) { |
91 | 0 | init_fast_rand_seed(&_tls_seed); |
92 | 0 | } |
93 | 0 | return xorshift128_next(&_tls_seed); |
94 | 0 | } |
95 | | |
96 | 0 | uint64_t fast_rand(FastRandSeed* seed) { |
97 | 0 | return xorshift128_next(seed); |
98 | 0 | } |
99 | | |
100 | 0 | uint64_t fast_rand_less_than(uint64_t range) { |
101 | 0 | if (range == 0) { |
102 | 0 | return 0; |
103 | 0 | } |
104 | 0 | if (need_init(_tls_seed)) { |
105 | 0 | init_fast_rand_seed(&_tls_seed); |
106 | 0 | } |
107 | 0 | return fast_rand_impl(range, &_tls_seed); |
108 | 0 | } |
109 | | |
110 | 0 | int64_t fast_rand_in_64(int64_t min, int64_t max) { |
111 | 0 | if (need_init(_tls_seed)) { |
112 | 0 | init_fast_rand_seed(&_tls_seed); |
113 | 0 | } |
114 | 0 | if (BAIDU_UNLIKELY(min >= max)) { |
115 | 0 | if (min == max) { |
116 | 0 | return min; |
117 | 0 | } |
118 | 0 | std::swap(min, max); |
119 | 0 | } |
120 | 0 | uint64_t range; |
121 | 0 | if (min >= 0) { |
122 | | // Always safe to do subtraction. |
123 | 0 | range = (uint64_t)(max - min) + 1; |
124 | 0 | return min + (int64_t)fast_rand_impl(range, &_tls_seed); |
125 | 0 | } |
126 | | |
127 | 0 | uint64_t abs_min = safe_abs(min); |
128 | 0 | if (max >= 0) { |
129 | 0 | range = abs_min + (uint64_t)(max) + 1; |
130 | 0 | } else { |
131 | 0 | range = abs_min - safe_abs(max) + 1; |
132 | 0 | } |
133 | 0 | if (range == 0) { |
134 | | // max = INT64_MAX, min = INT64_MIN |
135 | 0 | return (int64_t)xorshift128_next(&_tls_seed); |
136 | 0 | } |
137 | 0 | uint64_t r = fast_rand_impl(range, &_tls_seed); |
138 | 0 | return r >= abs_min ? (int64_t)(r - abs_min) : -((int64_t)(abs_min - r)); |
139 | 0 | } |
140 | | |
141 | 0 | uint64_t fast_rand_in_u64(uint64_t min, uint64_t max) { |
142 | 0 | if (need_init(_tls_seed)) { |
143 | 0 | init_fast_rand_seed(&_tls_seed); |
144 | 0 | } |
145 | 0 | if (min >= max) { |
146 | 0 | if (min == max) { |
147 | 0 | return min; |
148 | 0 | } |
149 | 0 | const uint64_t tmp = min; |
150 | 0 | min = max; |
151 | 0 | max = tmp; |
152 | 0 | } |
153 | 0 | uint64_t range = max - min + 1; |
154 | 0 | if (range == 0) { |
155 | | // max = UINT64_MAX, min = UINT64_MIN |
156 | 0 | return xorshift128_next(&_tls_seed); |
157 | 0 | } |
158 | 0 | return min + fast_rand_impl(range, &_tls_seed); |
159 | 0 | } |
160 | | |
161 | 0 | inline double fast_rand_double(FastRandSeed* seed) { |
162 | | // Copied from rand_util.cc |
163 | 0 | COMPILE_ASSERT(std::numeric_limits<double>::radix == 2, otherwise_use_scalbn); |
164 | 0 | static const int kBits = std::numeric_limits<double>::digits; |
165 | 0 | uint64_t random_bits = xorshift128_next(seed) & ((UINT64_C(1) << kBits) - 1); |
166 | 0 | double result = ldexp(static_cast<double>(random_bits), -1 * kBits); |
167 | 0 | return result; |
168 | 0 | } |
169 | | |
170 | 0 | double fast_rand_double() { |
171 | 0 | if (need_init(_tls_seed)) { |
172 | 0 | init_fast_rand_seed(&_tls_seed); |
173 | 0 | } |
174 | 0 | return fast_rand_double(&_tls_seed); |
175 | 0 | } |
176 | | |
177 | 0 | void fast_rand_bytes(void* output, size_t output_length) { |
178 | 0 | const size_t n = output_length / 8; |
179 | 0 | for (size_t i = 0; i < n; ++i) { |
180 | 0 | static_cast<uint64_t*>(output)[i] = fast_rand(); |
181 | 0 | } |
182 | 0 | const size_t m = output_length - n * 8; |
183 | 0 | if (m) { |
184 | 0 | uint8_t* p = static_cast<uint8_t*>(output) + n * 8; |
185 | 0 | uint64_t r = fast_rand(); |
186 | 0 | for (size_t i = 0; i < m; ++i) { |
187 | 0 | p[i] = (r & 0xFF); |
188 | 0 | r = (r >> 8); |
189 | 0 | } |
190 | 0 | } |
191 | 0 | } |
192 | | |
193 | 0 | std::string fast_rand_printable(size_t length) { |
194 | 0 | std::string result(length, 0); |
195 | 0 | const size_t halflen = length/2; |
196 | 0 | fast_rand_bytes(&result[0], halflen); |
197 | 0 | for (size_t i = 0; i < halflen; ++i) { |
198 | 0 | const uint8_t b = result[halflen - 1 - i]; |
199 | 0 | result[length - 1 - 2*i] = 'A' + (b & 0xF); |
200 | 0 | result[length - 2 - 2*i] = 'A' + (b >> 4); |
201 | 0 | } |
202 | 0 | if (halflen * 2 != length) { |
203 | 0 | result[0] = 'A' + (fast_rand() % 16); |
204 | 0 | } |
205 | 0 | return result; |
206 | 0 | } |
207 | | |
208 | | } // namespace butil |