|
66 | 66 | #include "getopt_long.h"
|
67 | 67 | #include "libpq-fe.h"
|
68 | 68 | #include "pgbench.h"
|
| 69 | +#include "port/pg_bitutils.h" |
69 | 70 | #include "portability/instr_time.h"
|
70 | 71 |
|
71 | 72 | #ifndef M_PI
|
@@ -1127,6 +1128,113 @@ getHashMurmur2(int64 val, uint64 seed)
|
1127 | 1128 | return (int64) result;
|
1128 | 1129 | }
|
1129 | 1130 |
|
| 1131 | +/* |
| 1132 | + * Pseudorandom permutation function |
| 1133 | + * |
| 1134 | + * For small sizes, this generates each of the (size!) possible permutations |
| 1135 | + * of integers in the range [0, size) with roughly equal probability. Once |
| 1136 | + * the size is larger than 20, the number of possible permutations exceeds the |
| 1137 | + * number of distinct states of the internal pseudorandom number generators, |
| 1138 | + * and so not all possible permutations can be generated, but the permutations |
| 1139 | + * chosen should continue to give the appearance of being random. |
| 1140 | + * |
| 1141 | + * THIS FUNCTION IS NOT CRYPTOGRAPHICALLY SECURE. |
| 1142 | + * DO NOT USE FOR SUCH PURPOSE. |
| 1143 | + */ |
| 1144 | +static int64 |
| 1145 | +permute(const int64 val, const int64 isize, const int64 seed) |
| 1146 | +{ |
| 1147 | + RandomState random_state1; |
| 1148 | + RandomState random_state2; |
| 1149 | + uint64 size; |
| 1150 | + uint64 v; |
| 1151 | + int masklen; |
| 1152 | + uint64 mask; |
| 1153 | + int i; |
| 1154 | + |
| 1155 | + if (isize < 2) |
| 1156 | + return 0; /* nothing to permute */ |
| 1157 | + |
| 1158 | + /* Initialize a pair of random states using the seed */ |
| 1159 | + random_state1.xseed[0] = seed & 0xFFFF; |
| 1160 | + random_state1.xseed[1] = (seed >> 16) & 0xFFFF; |
| 1161 | + random_state1.xseed[2] = (seed >> 32) & 0xFFFF; |
| 1162 | + |
| 1163 | + random_state2.xseed[0] = (((uint64) seed) >> 48) & 0xFFFF; |
| 1164 | + random_state2.xseed[1] = seed & 0xFFFF; |
| 1165 | + random_state2.xseed[2] = (seed >> 16) & 0xFFFF; |
| 1166 | + |
| 1167 | + /* Computations are performed on unsigned values */ |
| 1168 | + size = (uint64) isize; |
| 1169 | + v = (uint64) val % size; |
| 1170 | + |
| 1171 | + /* Mask to work modulo largest power of 2 less than or equal to size */ |
| 1172 | + masklen = pg_leftmost_one_pos64(size); |
| 1173 | + mask = (((uint64) 1) << masklen) - 1; |
| 1174 | + |
| 1175 | + /* |
| 1176 | + * Permute the input value by applying several rounds of pseudorandom |
| 1177 | + * bijective transformations. The intention here is to distribute each |
| 1178 | + * input uniformly randomly across the range, and separate adjacent inputs |
| 1179 | + * approximately uniformly randomly from each other, leading to a fairly |
| 1180 | + * random overall choice of permutation. |
| 1181 | + * |
| 1182 | + * To separate adjacent inputs, we multiply by a random number modulo |
| 1183 | + * (mask + 1), which is a power of 2. For this to be a bijection, the |
| 1184 | + * multiplier must be odd. Since this is known to lead to less randomness |
| 1185 | + * in the lower bits, we also apply a rotation that shifts the topmost bit |
| 1186 | + * into the least significant bit. In the special cases where size <= 3, |
| 1187 | + * mask = 1 and each of these operations is actually a no-op, so we also |
| 1188 | + * XOR the value with a different random number to inject additional |
| 1189 | + * randomness. Since the size is generally not a power of 2, we apply |
| 1190 | + * this bijection on overlapping upper and lower halves of the input. |
| 1191 | + * |
| 1192 | + * To distribute the inputs uniformly across the range, we then also apply |
| 1193 | + * a random offset modulo the full range. |
| 1194 | + * |
| 1195 | + * Taken together, these operations resemble a modified linear |
| 1196 | + * congruential generator, as is commonly used in pseudorandom number |
| 1197 | + * generators. The number of rounds is fairly arbitrary, but six has been |
| 1198 | + * found empirically to give a fairly good tradeoff between performance |
| 1199 | + * and uniform randomness. For small sizes it selects each of the (size!) |
| 1200 | + * possible permutations with roughly equal probability. For larger |
| 1201 | + * sizes, not all permutations can be generated, but the intended random |
| 1202 | + * spread is still produced. |
| 1203 | + */ |
| 1204 | + for (i = 0; i < 6; i++) |
| 1205 | + { |
| 1206 | + uint64 m, |
| 1207 | + r, |
| 1208 | + t; |
| 1209 | + |
| 1210 | + /* Random multiply (by an odd number), XOR and rotate of lower half */ |
| 1211 | + m = (uint64) getrand(&random_state1, 0, mask) | 1; |
| 1212 | + r = (uint64) getrand(&random_state2, 0, mask); |
| 1213 | + if (v <= mask) |
| 1214 | + { |
| 1215 | + v = ((v * m) ^ r) & mask; |
| 1216 | + v = ((v << 1) & mask) | (v >> (masklen - 1)); |
| 1217 | + } |
| 1218 | + |
| 1219 | + /* Random multiply (by an odd number), XOR and rotate of upper half */ |
| 1220 | + m = (uint64) getrand(&random_state1, 0, mask) | 1; |
| 1221 | + r = (uint64) getrand(&random_state2, 0, mask); |
| 1222 | + t = size - 1 - v; |
| 1223 | + if (t <= mask) |
| 1224 | + { |
| 1225 | + t = ((t * m) ^ r) & mask; |
| 1226 | + t = ((t << 1) & mask) | (t >> (masklen - 1)); |
| 1227 | + v = size - 1 - t; |
| 1228 | + } |
| 1229 | + |
| 1230 | + /* Random offset */ |
| 1231 | + r = (uint64) getrand(&random_state2, 0, size - 1); |
| 1232 | + v = (v + r) % size; |
| 1233 | + } |
| 1234 | + |
| 1235 | + return (int64) v; |
| 1236 | +} |
| 1237 | + |
1130 | 1238 | /*
|
1131 | 1239 | * Initialize the given SimpleStats struct to all zeroes
|
1132 | 1240 | */
|
@@ -2475,6 +2583,29 @@ evalStandardFunc(CState *st,
|
2475 | 2583 | return true;
|
2476 | 2584 | }
|
2477 | 2585 |
|
| 2586 | + case PGBENCH_PERMUTE: |
| 2587 | + { |
| 2588 | + int64 val, |
| 2589 | + size, |
| 2590 | + seed; |
| 2591 | + |
| 2592 | + Assert(nargs == 3); |
| 2593 | + |
| 2594 | + if (!coerceToInt(&vargs[0], &val) || |
| 2595 | + !coerceToInt(&vargs[1], &size) || |
| 2596 | + !coerceToInt(&vargs[2], &seed)) |
| 2597 | + return false; |
| 2598 | + |
| 2599 | + if (size <= 0) |
| 2600 | + { |
| 2601 | + pg_log_error("permute size parameter must be greater than zero"); |
| 2602 | + return false; |
| 2603 | + } |
| 2604 | + |
| 2605 | + setIntValue(retval, permute(val, size, seed)); |
| 2606 | + return true; |
| 2607 | + } |
| 2608 | + |
2478 | 2609 | default:
|
2479 | 2610 | /* cannot get here */
|
2480 | 2611 | Assert(0);
|
|
0 commit comments