aboutsummaryrefslogtreecommitdiffstats
path: root/mps/code
diff options
context:
space:
mode:
authorRichard Kistruck2009-01-20 18:23:53 +0000
committerRichard Kistruck2009-01-20 18:23:53 +0000
commitcd3c824df36b557889fb1fd903351b60b629afbd (patch)
tree880270caeb56a8492f9cf30d0752f3c250d60847 /mps/code
parente486078a79edd55a290e4087ee92c114fa301e0d (diff)
downloademacs-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.c201
-rw-r--r--mps/code/testlib.h8
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
58static unsigned long seed = 1; 80static 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
62unsigned long rnd(void) 83unsigned 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 */ 95static unsigned long seed_verify_schrage = 1;
75static void rnd_verify(void) 96#define R_q (R_m / R_a)
97#define R_r (R_m % R_a)
98static 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
109static unsigned long seed_verify_float = 1;
110#define R_m_float 2147483647.0
111#define R_a_float 48271.0
112static 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 */
130void 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
144extern unsigned long rnd(void); 144extern unsigned long rnd(void);
145 145
146 146
147/* rnd_verify() -- checks behaviour of rnd() */
148extern 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 *