diff options
| author | Richard Kistruck | 2009-01-20 18:23:53 +0000 |
|---|---|---|
| committer | Richard Kistruck | 2009-01-20 18:23:53 +0000 |
| commit | cd3c824df36b557889fb1fd903351b60b629afbd (patch) | |
| tree | 880270caeb56a8492f9cf30d0752f3c250d60847 /mps/code | |
| parent | e486078a79edd55a290e4087ee92c114fa301e0d (diff) | |
| download | emacs-cd3c824df36b557889fb1fd903351b60b629afbd.tar.gz emacs-cd3c824df36b557889fb1fd903351b60b629afbd.zip | |
Mps br/timing rnd():
Use a = 48271. Use fast implementation. Much improved verification by rnd_verify().
Copied from Perforce
Change: 167182
ServerID: perforce.ravenbrook.com
Diffstat (limited to 'mps/code')
| -rw-r--r-- | mps/code/testlib.c | 201 | ||||
| -rw-r--r-- | mps/code/testlib.h | 8 |
2 files changed, 155 insertions, 54 deletions
diff --git a/mps/code/testlib.c b/mps/code/testlib.c index 5c92969d2e0..947ead5fb82 100644 --- a/mps/code/testlib.c +++ b/mps/code/testlib.c | |||
| @@ -34,72 +34,167 @@ struct itimerspec; /* stop complaints from time.h */ | |||
| 34 | 34 | ||
| 35 | /* rnd -- a random number generator | 35 | /* rnd -- a random number generator |
| 36 | * | 36 | * |
| 37 | * I nabbed it from "ML for the Working Programmer", originally from: | 37 | * We use the (Multiplicative) Linear Congruential Generator |
| 38 | * Stephen K Park & Keith W Miller (1988). Random number generators: | 38 | * Xn = a * Xn-1 mod m |
| 39 | * good ones are hard to find. Communications of the ACM, 31:1192-1201. | 39 | * with: m = 2147483647 (2^31 - 1, a Mersenne prime), and a = 48271. |
| 40 | * This is a 'full-period' generator: all values from [1..(mod-1)], | ||
| 41 | * or 0x00000001 to 0x7ffffffe inclusive, are returned once, and then | ||
| 42 | * the cycle begins again. (The value 0 is not part of the cycle and | ||
| 43 | * is never returned). So the period = mod-1, ie. 2147483646. | ||
| 40 | * | 44 | * |
| 41 | * This is a 'full-period' generator: all values from [1..(mod-1)] | 45 | * This generator is extremely simple and has been very well studied. |
| 42 | * are returned once, and then the generator cycles round again. | 46 | * It is free of major vices we might care about for this application. |
| 43 | * (The value 0 is not part of the cycle and is never returned). That | 47 | * In particular, as m is prime, low order bits are random. Therefore |
| 44 | * means the period = mod-1, ie. 2147483646. | 48 | * to roll an N-sided die (N << m), "rnd() % N" is acceptable, giving |
| 49 | * a value in [0..N-1]. | ||
| 45 | * | 50 | * |
| 46 | * This "x 16807 mod 2^31-1" generator has been very well studied. | 51 | * It was popularised by the much-cited Park & Miller paper: |
| 47 | * It is not the 'best' (most random) simple generator, but it is | 52 | * Stephen K Park & Keith W Miller (1988). Random number generators: |
| 48 | * free of all vices we might care about for this application, so | 53 | * good ones are hard to find. Communications of the ACM, |
| 49 | * we'll keep it simple and stick with this, thanks. | 54 | * 31:1192-1201. |
| 55 | * The recommended multiplier a was later updated from 16807 to 48271: | ||
| 56 | * Stephen K Park, Keith W Miller, Paul K. Stockmeyer (1993). | ||
| 57 | * Technical Correspondence. Communications of the ACM, 36:105-110. | ||
| 50 | * | 58 | * |
| 51 | * There are faster implementations of this generator, summarised | 59 | * (Many more elaborate generators have been invented. The next simple |
| 52 | * briefly here: | 60 | * step would be to combine with the MLCG m = 2147483399 a = 40692, to |
| 53 | * - L. Schrage (1979 & 1983) showed how to do all the calculations | 61 | * make the period "about 74 quadrillion". See the summary of chapter |
| 54 | * within 32-bit integers. See also Numerical recipes in C. | 62 | * 3 in Knuth's "The Art of Computer Programming".) |
| 55 | * - David Carta (1990) showed how to also eliminate division. | 63 | * |
| 64 | * This (fast) implementation uses the identity: | ||
| 65 | * 0x80000000 == 0x7FFFFFFF + 0x00000001 | ||
| 66 | * noted by David Carta (1990), where 0x7FFFFFFF == 2^31-1 == m, which | ||
| 67 | * means that bits above the first 31 can simply be shifted >> 31 and | ||
| 68 | * added, preserving Xn mod m. To remain within 32-bit unsigned | ||
| 69 | * arithmetic when multiplying the previous seed (31 bits) by a (16 | ||
| 70 | * bits), the seed is split into bottom and top halves; bits above | ||
| 71 | * the first 31 are simply "top >> 16". (Code by RHSK, inspired by | ||
| 72 | * Robin Whittle's article at "http://www.firstpr.com.au/dsp/rand31/"). | ||
| 73 | * | ||
| 74 | * Slower implementations, used for verification: | ||
| 75 | * rnd_verify_schrage uses the method of L. Schrage (1979 & 1983), | ||
| 76 | * namely splitting the seed by q, where q = m div a. | ||
| 77 | * rnd_verify_float simply uses floating point arithmetic. | ||
| 56 | */ | 78 | */ |
| 57 | 79 | ||
| 58 | static unsigned long seed = 1; | 80 | static unsigned long seed = 1; |
| 59 | /* 2^31 - 1 == (1UL<<31) - 1 == 2147483647 */ | 81 | #define R_m 2147483647UL |
| 60 | #define RND_MOD_2_31_1 2147483647 | 82 | #define R_a 48271UL |
| 61 | #define RND_MOD_2_31_1_FLOAT 2147483647.0 | ||
| 62 | unsigned long rnd(void) | 83 | unsigned long rnd(void) |
| 63 | { | 84 | { |
| 64 | double s; | 85 | /* requires m == 2^31-1, a < 2^16 */ |
| 65 | 86 | unsigned long bot = R_a * (seed & 0x7FFF); | |
| 66 | s = seed; | 87 | unsigned long top = R_a * (seed >> 15); |
| 67 | s *= 16807.0; | 88 | seed = bot + ((top & 0xFFFF) << 15) + (top >> 16); |
| 68 | s = fmod(s, RND_MOD_2_31_1_FLOAT); /* 2^31 - 1 */ | 89 | if(seed > R_m) |
| 69 | seed = (unsigned long)s; | 90 | seed -= R_m; |
| 70 | return seed; | 91 | return seed; |
| 71 | /* Have you modified this code? Run rnd_verify() please! RHSK */ | 92 | /* Have you modified this code? Run rnd_verify(3) please! RHSK */ |
| 72 | } | 93 | } |
| 73 | 94 | ||
| 74 | /* rnd_verify -- verify that rnd() returns the correct results */ | 95 | static unsigned long seed_verify_schrage = 1; |
| 75 | static void rnd_verify(void) | 96 | #define R_q (R_m / R_a) |
| 97 | #define R_r (R_m % R_a) | ||
| 98 | static unsigned long rnd_verify_schrage(void) | ||
| 99 | { | ||
| 100 | /* requires m < 2^31, q > r; see Park & Miller (1988) */ | ||
| 101 | unsigned long alpha = R_a * (seed_verify_schrage % R_q); /* < m */ | ||
| 102 | unsigned long beta = R_r * (seed_verify_schrage / R_q); /* < m */ | ||
| 103 | seed_verify_schrage = alpha - beta; | ||
| 104 | if(alpha < beta) | ||
| 105 | seed_verify_schrage += R_m; | ||
| 106 | return seed_verify_schrage; | ||
| 107 | } | ||
| 108 | |||
| 109 | static unsigned long seed_verify_float = 1; | ||
| 110 | #define R_m_float 2147483647.0 | ||
| 111 | #define R_a_float 48271.0 | ||
| 112 | static unsigned long rnd_verify_float(void) | ||
| 113 | { | ||
| 114 | double s; | ||
| 115 | s = seed_verify_float; | ||
| 116 | s *= R_a_float; | ||
| 117 | s = fmod(s, R_m_float); | ||
| 118 | seed_verify_float = (unsigned long)s; | ||
| 119 | return seed_verify_float; | ||
| 120 | } | ||
| 121 | |||
| 122 | /* rnd_verify -- verify that rnd() returns the correct results | ||
| 123 | * | ||
| 124 | * depth = how much time to spend verifying | ||
| 125 | * 0: very quick -- just verify the next rnd() value | ||
| 126 | * 1: quick -- verify the first 10000 calls from seed = 1 | ||
| 127 | * 2: slow (< 5 minutes) -- run the fast generator for a full cycle | ||
| 128 | * 3: very slow (several hours) -- verify a full cycle | ||
| 129 | */ | ||
| 130 | void rnd_verify(int depth) | ||
| 76 | { | 131 | { |
| 77 | unsigned long orig_seed = seed; | 132 | unsigned long orig_seed = seed; |
| 78 | unsigned long i; | 133 | unsigned long i; |
| 134 | unsigned long r; | ||
| 135 | |||
| 136 | /* 0: the next value from rnd() matches rnd_verify_*() */ | ||
| 137 | if(depth >= 0) { | ||
| 138 | seed_verify_schrage = seed; | ||
| 139 | seed_verify_float = seed; | ||
| 140 | r = rnd(); | ||
| 141 | Insist(rnd_verify_schrage() == r); | ||
| 142 | Insist(rnd_verify_float() == r); | ||
| 143 | } | ||
| 79 | 144 | ||
| 80 | /* This test is from: | 145 | /* 1: first 10000 (from Park & Miller, note: 1-based indexing!) */ |
| 81 | * Stephen K Park & Keith W Miller (1988). Random number generators: | 146 | if(depth >= 1) { |
| 82 | * good ones are hard to find. Communications of the ACM, 31:1192-1201. | 147 | i = 1; |
| 83 | * Note: The Park-Miller paper uses 1-based indices. | 148 | seed = 1; |
| 84 | */ | 149 | seed_verify_schrage = 1; |
| 85 | i = 1; | 150 | seed_verify_float = 1; |
| 86 | seed = 1; | 151 | for(i = 2; i <= 10001; i += 1) { |
| 87 | for(i = 2; i < 10001; i += 1) { | 152 | r = rnd(); |
| 88 | (void)rnd(); | 153 | Insist(rnd_verify_schrage() == r); |
| 154 | Insist(rnd_verify_float() == r); | ||
| 155 | } | ||
| 156 | printf("%lu\n", r); | ||
| 157 | /* Insist(r == 1043618065UL); -- correct value for a = 16807 */ | ||
| 158 | Insist(r == 399268537UL); /* correct for a = 48271 */ | ||
| 89 | } | 159 | } |
| 90 | Insist(i == 10001); | ||
| 91 | Insist(rnd() == 1043618065); | ||
| 92 | 160 | ||
| 93 | /* from Robin Whittle's compendium at | 161 | /* 1: observe wrap-around (note: 0-based indexing) */ |
| 94 | * http://www.firstpr.com.au/dsp/rand31/ | 162 | if(depth >= 1) { |
| 95 | * Observe wrap-around after 'full-period' (which excludes 0). | 163 | i = 2147483645UL; |
| 96 | * Note: uses 0-based indices, ie. rnd_0 = 1, rnd_1 = 16807, ... | 164 | /* seed = 1407677000UL; -- correct value for a = 16807 */ |
| 97 | */ | 165 | seed = 1899818559UL; /* correct for a = 48271 */ |
| 98 | i = 2147483645; | 166 | seed_verify_schrage = 1899818559UL; |
| 99 | seed = 1407677000; | 167 | seed_verify_float = 1899818559UL; |
| 100 | i += 1; | 168 | i += 1; |
| 101 | Insist(i == (1UL<<31) - 2 ); /* 'full-period' excludes 0 */ | 169 | Insist(i == (1UL<<31) - 2 ); /* 'full-period' excludes 0 */ |
| 102 | Insist(rnd() == 1); /* wrap-around */ | 170 | Insist(rnd() == 1); /* wrap-around */ |
| 171 | Insist(rnd_verify_schrage() == 1); | ||
| 172 | Insist(rnd_verify_float() == 1); | ||
| 173 | } | ||
| 174 | |||
| 175 | /* 2 & 3: Full cycle (3 => verifying each value) */ | ||
| 176 | if(depth >= 2) { | ||
| 177 | unsigned long r1; | ||
| 178 | i = 0; | ||
| 179 | seed = 1; | ||
| 180 | seed_verify_schrage = 1; | ||
| 181 | seed_verify_float = 1; | ||
| 182 | while(1) { | ||
| 183 | i += 1; | ||
| 184 | r = rnd(); | ||
| 185 | if(depth >= 3) { | ||
| 186 | Insist(rnd_verify_schrage() == r); | ||
| 187 | Insist(rnd_verify_float() == r); | ||
| 188 | } | ||
| 189 | if(r == 1) { | ||
| 190 | printf("Wrap at i=%lu, r=%lu, r(i-1)=%lu.\n", | ||
| 191 | i, r, r1); | ||
| 192 | break; | ||
| 193 | } else { | ||
| 194 | r1 = r; | ||
| 195 | } | ||
| 196 | } | ||
| 197 | } | ||
| 103 | 198 | ||
| 104 | seed = orig_seed; | 199 | seed = orig_seed; |
| 105 | } | 200 | } |
| @@ -135,18 +230,20 @@ void randomize(int argc, char **argv) | |||
| 135 | n = sscanf(argv[1], "%lu", &seed0); | 230 | n = sscanf(argv[1], "%lu", &seed0); |
| 136 | Insist(n == 1); | 231 | Insist(n == 1); |
| 137 | printf("randomize(): resetting initial seed to: %lu.\n", seed0); | 232 | printf("randomize(): resetting initial seed to: %lu.\n", seed0); |
| 138 | rnd_verify(); /* good to verify occasionally: slip it in here */ | ||
| 139 | } else { | 233 | } else { |
| 140 | /* time_t uses an arbitrary encoding, but hopefully the low order */ | 234 | /* time_t uses an arbitrary encoding, but hopefully the low order */ |
| 141 | /* 31 bits will have at least one bit changed from run to run. */ | 235 | /* 31 bits will have at least one bit changed from run to run. */ |
| 142 | seed0 = 1 + time(NULL) % (RND_MOD_2_31_1 - 1); | 236 | seed0 = 1 + time(NULL) % (R_m - 1); |
| 143 | printf("randomize(): choosing initial seed: %lu.\n", seed0); | 237 | printf("randomize(): choosing initial seed: %lu.\n", seed0); |
| 144 | } | 238 | } |
| 145 | 239 | ||
| 146 | Insist(seed0 < RND_MOD_2_31_1); /* 2^31 - 1 */ | 240 | Insist(seed0 < R_m); |
| 147 | Insist(seed0 != 0); | 241 | Insist(seed0 != 0); |
| 148 | seed = seed0; | 242 | seed = seed0; |
| 149 | 243 | ||
| 244 | rnd_verify(0); | ||
| 245 | Insist(seed == seed0); | ||
| 246 | |||
| 150 | /* The 'random' seed is taken from time(), which may simply be a | 247 | /* The 'random' seed is taken from time(), which may simply be a |
| 151 | * count of seconds: therefore successive runs may start with | 248 | * count of seconds: therefore successive runs may start with |
| 152 | * nearby seeds, possibly differing only by 1. So the first value | 249 | * nearby seeds, possibly differing only by 1. So the first value |
diff --git a/mps/code/testlib.h b/mps/code/testlib.h index 94161adb58d..cf40053c2d3 100644 --- a/mps/code/testlib.h +++ b/mps/code/testlib.h | |||
| @@ -138,12 +138,16 @@ extern void verror(const char *format, va_list args); | |||
| 138 | 138 | ||
| 139 | /* rnd -- random number generator | 139 | /* rnd -- random number generator |
| 140 | * | 140 | * |
| 141 | * rnd() generates a sequence of integers in the range [0, 2^31-2]. | 141 | * rnd() generates a sequence of integers in the range [1, 2^31-1]. |
| 142 | */ | 142 | */ |
| 143 | 143 | ||
| 144 | extern unsigned long rnd(void); | 144 | extern unsigned long rnd(void); |
| 145 | 145 | ||
| 146 | 146 | ||
| 147 | /* rnd_verify() -- checks behaviour of rnd() */ | ||
| 148 | extern void rnd_verify(int depth); | ||
| 149 | |||
| 150 | |||
| 147 | /* rnd_addr -- random number generator | 151 | /* rnd_addr -- random number generator |
| 148 | * | 152 | * |
| 149 | * rnd_addr() generates a sequence of addresses all over the address space. | 153 | * rnd_addr() generates a sequence of addresses all over the address space. |
| @@ -167,7 +171,7 @@ extern void randomize(int argc, char **argv); | |||
| 167 | 171 | ||
| 168 | /* C. COPYRIGHT AND LICENSE | 172 | /* C. COPYRIGHT AND LICENSE |
| 169 | * | 173 | * |
| 170 | * Copyright (C) 2001-2002 Ravenbrook Limited <http://www.ravenbrook.com/>. | 174 | * Copyright (C) 2001-2002, 2008 Ravenbrook Limited <http://www.ravenbrook.com/>. |
| 171 | * All rights reserved. This is an open source license. Contact | 175 | * All rights reserved. This is an open source license. Contact |
| 172 | * Ravenbrook for commercial licensing options. | 176 | * Ravenbrook for commercial licensing options. |
| 173 | * | 177 | * |