1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <stdio.h>
27 #include <fcntl.h>
28 /* this definition prevents warning about
29 using undefined round() function */
30 #include <math.h>
31 #include "filebench.h"
32 #include "ipc.h"
33 #include "gamma_dist.h"
34 #include "cvars/mtwist/mtwist.h"
35
36 /*
37 * Generates a 64-bit random number using mtwist or from a provided random
38 * variable "avd".
39 *
40 * Returned random number "randp" is clipped by the "max" value and rounded off
41 * by the "round" value. Returns 0 on success, shuts down Filebench on
42 * failure.
43 */
44 void
fb_random64(uint64_t * randp,uint64_t max,uint64_t round,avd_t avd)45 fb_random64(uint64_t *randp, uint64_t max, uint64_t round, avd_t avd)
46 {
47 double random_normalized;
48 uint64_t random = 0;
49
50 if (avd) {
51 /* get it from the random variable */
52 if (!AVD_IS_RANDOM(avd)) {
53 /* trying to get random value not from
54 random variable. That's a clear error. */
55 filebench_log(LOG_ERROR, "filebench_randomno64: trying"
56 " to get a random value from not a random variable");
57 filebench_shutdown(1);
58 /* NOT REACHABLE */
59 } else {
60 random = avd_get_int(avd);
61 }
62 } else {
63 random = mt_llrand();
64 }
65
66 /*
67 * If round is not zero, then caller will get a random number in the
68 * range [0; max - round]. This allows the caller to use this
69 * function, for example, to obtain a pointer in an allocated memory
70 * region of [0; max] and read/write round bytes safely starting
71 * at this pointer.
72 */
73 max = max - round;
74
75 random_normalized = (double)random / UINT64_MAX;
76 random = random_normalized * max;
77
78 if (round) {
79 random = random / round;
80 random = random * round;
81 }
82
83 *randp = random;
84 }
85
86 /*
87 * Same as filebench_randomno64, but for 32 bit integers.
88 */
89
90 void
fb_random32(uint32_t * randp,uint32_t max,uint32_t round,avd_t avd)91 fb_random32(uint32_t *randp,
92 uint32_t max, uint32_t round, avd_t avd)
93 {
94 uint64_t rand64;
95
96 fb_random64(&rand64, max, round, avd);
97
98 /* rand64 always fits uint32, since "max" above was 32 bit */
99 *randp = (uint32_t)rand64;
100 }
101
102 /*
103 * Same as filebench_randomno64, but for probability [0-1].
104 */
105 static double
fb_random_probability()106 fb_random_probability()
107 {
108 uint64_t randnum;
109
110 fb_random64(&randnum, UINT64_MAX, 0, NULL);
111
112 /* convert to 0-1 probability */
113 return (double)randnum / (double)(UINT64_MAX);
114 }
115
116 /****************************************
117 * *
118 * randist related functions *
119 * *
120 ****************************************/
121
122 static double
fb_rand_src_rand48(unsigned short * xi)123 fb_rand_src_rand48(unsigned short *xi)
124 {
125 return (erand48(xi));
126 }
127
128 static double
fb_rand_src_random(unsigned short * xi)129 fb_rand_src_random(unsigned short *xi)
130 {
131 return fb_random_probability();
132 }
133
134 /*
135 * fetch a uniformly distributed random number from the supplied
136 * random object.
137 */
138 static double
rand_uniform_get(randdist_t * rndp)139 rand_uniform_get(randdist_t *rndp)
140 {
141 double dprob, dmin, dres, dround;
142
143 dmin = (double)rndp->rnd_vint_min;
144 dround = (double)rndp->rnd_vint_round;
145
146 dprob = (*rndp->rnd_src)(rndp->rnd_xi);
147
148 dres = (dprob * (2.0 * (rndp->rnd_dbl_mean - dmin))) + dmin;
149
150 if (dround == 0.0)
151 return (dres);
152 else
153 return (round(dres / dround) * dround);
154 }
155
156 /*
157 * fetch a gamma distributed random number from the supplied
158 * random object.
159 */
160 static double
rand_gamma_get(randdist_t * rndp)161 rand_gamma_get(randdist_t *rndp)
162 {
163 double dmult, dres, dmin, dround;
164
165 dmin = (double)rndp->rnd_vint_min;
166 dround = (double)rndp->rnd_vint_round;
167
168 dmult = (rndp->rnd_dbl_mean - dmin) / rndp->rnd_dbl_gamma;
169
170 dres = gamma_dist_knuth_src(rndp->rnd_dbl_gamma,
171 dmult, rndp->rnd_src, rndp->rnd_xi) + dmin;
172
173 if (dround == 0.0)
174 return (dres);
175 else
176 return (round(dres / dround) * dround);
177 }
178
179 /*
180 * fetch a table driven random number from the supplied
181 * random object.
182 */
183 static double
rand_table_get(randdist_t * rndp)184 rand_table_get(randdist_t *rndp)
185 {
186 double dprob, dprcnt, dtabres, dsclres, dmin, dround;
187 int idx;
188
189 dmin = (double)rndp->rnd_vint_min;
190 dround = (double)rndp->rnd_vint_round;
191
192 dprob = (*rndp->rnd_src)(rndp->rnd_xi);
193
194 dprcnt = (dprob * (double)(PF_TAB_SIZE));
195 idx = (int)dprcnt;
196
197 dtabres = (rndp->rnd_rft[idx].rf_base +
198 (rndp->rnd_rft[idx].rf_range * (dprcnt - (double)idx)));
199
200 dsclres = (dtabres * (rndp->rnd_dbl_mean - dmin)) + dmin;
201
202 if (dround == 0.0)
203 return (dsclres);
204 else
205 return (round(dsclres / dround) * dround);
206 }
207
208 /*
209 * Set the random seed in the supplied random object.
210 */
211 static void
rand_seed_set(randdist_t * rndp)212 rand_seed_set(randdist_t *rndp)
213 {
214 union {
215 uint64_t ll;
216 uint16_t w[4];
217 } temp1;
218 int idx;
219
220 temp1.ll = (uint64_t)avd_get_int(rndp->rnd_seed);
221
222 for (idx = 0; idx < 3; idx++) {
223
224 #ifdef _BIG_ENDIAN
225 rndp->rnd_xi[idx] = temp1.w[3-idx];
226 #else
227 rndp->rnd_xi[idx] = temp1.w[idx];
228 #endif
229 }
230 }
231
232 /*
233 * Define a random entity which will contain the parameters of a random
234 * distribution.
235 */
236 randdist_t *
randdist_alloc(void)237 randdist_alloc(void)
238 {
239 randdist_t *rndp;
240
241 if ((rndp = (randdist_t *)ipc_malloc(FILEBENCH_RANDDIST)) == NULL) {
242 filebench_log(LOG_ERROR, "Out of memory for random dist");
243 return (NULL);
244 }
245
246 /* place on global list */
247 rndp->rnd_next = filebench_shm->shm_rand_list;
248 filebench_shm->shm_rand_list = rndp;
249
250 return (rndp);
251 }
252
253 /*
254 * Initializes a random distribution entity, converting avd_t parameters to
255 * doubles, and converting the list of probability density function table
256 * entries, if supplied, into a probablilty function table.
257 */
258 void
randdist_init(randdist_t * rndp)259 randdist_init(randdist_t *rndp)
260 {
261 probtabent_t *rdte_hdp, *ptep;
262 double tablemean, tablemin = 0;
263 int pteidx;
264
265 /* convert parameters to doubles */
266 rndp->rnd_dbl_gamma = (double)avd_get_int(rndp->rnd_gamma) / 1000.0;
267 if (rndp->rnd_mean != NULL)
268 rndp->rnd_dbl_mean = (double)avd_get_int(rndp->rnd_mean);
269 else
270 rndp->rnd_dbl_mean = rndp->rnd_dbl_gamma;
271
272 /* de-reference min and round amounts for later use */
273 rndp->rnd_vint_min = avd_get_int(rndp->rnd_min);
274 rndp->rnd_vint_round = avd_get_int(rndp->rnd_round);
275
276 filebench_log(LOG_DEBUG_IMPL,
277 "init random var: Mean = %6.0llf, Gamma = %6.3llf, Min = %llu",
278 rndp->rnd_dbl_mean, rndp->rnd_dbl_gamma,
279 (u_longlong_t)rndp->rnd_vint_min);
280
281 /* initialize distribution to apply */
282 switch (rndp->rnd_type & RAND_TYPE_MASK) {
283 case RAND_TYPE_UNIFORM:
284 rndp->rnd_get = rand_uniform_get;
285 break;
286
287 case RAND_TYPE_GAMMA:
288 rndp->rnd_get = rand_gamma_get;
289 break;
290
291 case RAND_TYPE_TABLE:
292 rndp->rnd_get = rand_table_get;
293 break;
294
295 default:
296 filebench_log(LOG_DEBUG_IMPL, "Random Type not Specified");
297 filebench_shutdown(1);
298 return;
299 }
300
301 /* initialize source of random numbers */
302 if (rndp->rnd_type & RAND_SRC_GENERATOR) {
303 rndp->rnd_src = fb_rand_src_rand48;
304 rand_seed_set(rndp);
305 } else {
306 rndp->rnd_src = fb_rand_src_random;
307 }
308
309 /* any random distribution table to convert? */
310 if ((rdte_hdp = rndp->rnd_probtabs) == NULL)
311 return;
312
313 /* determine random distribution max and mins and initialize table */
314 pteidx = 0;
315 tablemean = 0.0;
316 for (ptep = rdte_hdp; ptep; ptep = ptep->pte_next) {
317 double dmin, dmax;
318 int entcnt;
319
320 dmax = (double)avd_get_int(ptep->pte_segmax);
321 dmin = (double)avd_get_int(ptep->pte_segmin);
322
323 /* initialize table minimum on first pass */
324 if (pteidx == 0)
325 tablemin = dmin;
326
327 /* update table minimum */
328 if (tablemin > dmin)
329 tablemin = dmin;
330
331 entcnt = (int)avd_get_int(ptep->pte_percent);
332 tablemean += (((dmin + dmax)/2.0) * (double)entcnt);
333
334 /* populate the lookup table */
335
336 for (; entcnt > 0; entcnt--) {
337 rndp->rnd_rft[pteidx].rf_base = dmin;
338 rndp->rnd_rft[pteidx].rf_range = dmax - dmin;
339 pteidx++;
340 }
341 }
342
343 /* check to see if probability equals 100% */
344 if (pteidx != PF_TAB_SIZE)
345 filebench_log(LOG_ERROR,
346 "Prob table only totals %d%%", pteidx);
347
348 /* If table is not supplied with a mean value, set it to table mean */
349 if (rndp->rnd_dbl_mean == 0.0)
350 rndp->rnd_dbl_mean = (double)tablemean / (double)PF_TAB_SIZE;
351
352 /* now normalize the entries for a min value of 0, mean of 1 */
353 tablemean = (tablemean / 100.0) - tablemin;
354
355 /* special case if really a constant value */
356 if (tablemean == 0.0) {
357 for (pteidx = 0; pteidx < PF_TAB_SIZE; pteidx++) {
358 rndp->rnd_rft[pteidx].rf_base = 0.0;
359 rndp->rnd_rft[pteidx].rf_range = 0.0;
360 }
361 return;
362 }
363
364 for (pteidx = 0; pteidx < PF_TAB_SIZE; pteidx++) {
365
366 rndp->rnd_rft[pteidx].rf_base =
367 ((rndp->rnd_rft[pteidx].rf_base - tablemin) / tablemean);
368 rndp->rnd_rft[pteidx].rf_range =
369 (rndp->rnd_rft[pteidx].rf_range / tablemean);
370 }
371 }
372