@@ -98,6 +98,8 @@ static int pthread_join(pthread_t th, void **thread_return);
9898#define LOG_STEP_SECONDS 5 /* seconds between log messages */
9999#define DEFAULT_NXACTS 10 /* default nxacts */
100100
101+ #define MIN_GAUSSIAN_THRESHOLD 2.0 /* minimum threshold for gauss */
102+
101103int nxacts = 0 ; /* number of transactions per client */
102104int duration = 0 ; /* duration in seconds */
103105
@@ -471,6 +473,76 @@ getrand(TState *thread, int64 min, int64 max)
471473 return min + (int64 ) ((max - min + 1 ) * pg_erand48 (thread -> random_state ));
472474}
473475
476+ /*
477+ * random number generator: exponential distribution from min to max inclusive.
478+ * the threshold is so that the density of probability for the last cut-off max
479+ * value is exp(-threshold).
480+ */
481+ static int64
482+ getExponentialRand (TState * thread , int64 min , int64 max , double threshold )
483+ {
484+ double cut , uniform , rand ;
485+ Assert (threshold > 0.0 );
486+ cut = exp (- threshold );
487+ /* erand in [0, 1), uniform in (0, 1] */
488+ uniform = 1.0 - pg_erand48 (thread -> random_state );
489+ /*
490+ * inner expresion in (cut, 1] (if threshold > 0),
491+ * rand in [0, 1)
492+ */
493+ Assert ((1.0 - cut ) != 0.0 );
494+ rand = - log (cut + (1.0 - cut ) * uniform ) / threshold ;
495+ /* return int64 random number within between min and max */
496+ return min + (int64 )((max - min + 1 ) * rand );
497+ }
498+
499+ /* random number generator: gaussian distribution from min to max inclusive */
500+ static int64
501+ getGaussianRand (TState * thread , int64 min , int64 max , double threshold )
502+ {
503+ double stdev ;
504+ double rand ;
505+
506+ /*
507+ * Get user specified random number from this loop, with
508+ * -threshold < stdev <= threshold
509+ *
510+ * This loop is executed until the number is in the expected range.
511+ *
512+ * As the minimum threshold is 2.0, the probability of looping is low:
513+ * sqrt(-2 ln(r)) <= 2 => r >= e^{-2} ~ 0.135, then when taking the average
514+ * sinus multiplier as 2/pi, we have a 8.6% looping probability in the
515+ * worst case. For a 5.0 threshold value, the looping probability
516+ * is about e^{-5} * 2 / pi ~ 0.43%.
517+ */
518+ do
519+ {
520+ /*
521+ * pg_erand48 generates [0,1), but for the basic version of the
522+ * Box-Muller transform the two uniformly distributed random numbers
523+ * are expected in (0, 1] (see http://en.wikipedia.org/wiki/Box_muller)
524+ */
525+ double rand1 = 1.0 - pg_erand48 (thread -> random_state );
526+ double rand2 = 1.0 - pg_erand48 (thread -> random_state );
527+
528+ /* Box-Muller basic form transform */
529+ double var_sqrt = sqrt (-2.0 * log (rand1 ));
530+ stdev = var_sqrt * sin (2.0 * M_PI * rand2 );
531+
532+ /*
533+ * we may try with cos, but there may be a bias induced if the previous
534+ * value fails the test. To be on the safe side, let us try over.
535+ */
536+ }
537+ while (stdev < - threshold || stdev >= threshold );
538+
539+ /* stdev is in [-threshold, threshold), normalization to [0,1) */
540+ rand = (stdev + threshold ) / (threshold * 2.0 );
541+
542+ /* return int64 random number within between min and max */
543+ return min + (int64 )((max - min + 1 ) * rand );
544+ }
545+
474546/* call PQexec() and exit() on failure */
475547static void
476548executeStatement (PGconn * con , const char * sql )
@@ -1319,6 +1391,7 @@ doCustom(TState *thread, CState *st, instr_time *conn_time, FILE *logfile, AggVa
13191391 char * var ;
13201392 int64 min ,
13211393 max ;
1394+ double threshold = 0 ;
13221395 char res [64 ];
13231396
13241397 if (* argv [2 ] == ':' )
@@ -1364,11 +1437,11 @@ doCustom(TState *thread, CState *st, instr_time *conn_time, FILE *logfile, AggVa
13641437 }
13651438
13661439 /*
1367- * getrand() needs to be able to subtract max from min and add one
1368- * to the result without overflowing. Since we know max > min, we
1369- * can detect overflow just by checking for a negative result. But
1370- * we must check both that the subtraction doesn't overflow, and
1371- * that adding one to the result doesn't overflow either.
1440+ * Generate random number functions need to be able to subtract
1441+ * max from min and add one to the result without overflowing.
1442+ * Since we know max > min, we can detect overflow just by checking
1443+ * for a negative result. But we must check both that the subtraction
1444+ * doesn't overflow, and that adding one to the result doesn't overflow either.
13721445 */
13731446 if (max - min < 0 || (max - min ) + 1 < 0 )
13741447 {
@@ -1377,10 +1450,64 @@ doCustom(TState *thread, CState *st, instr_time *conn_time, FILE *logfile, AggVa
13771450 return true;
13781451 }
13791452
1453+ if (argc == 4 || /* uniform without or with "uniform" keyword */
1454+ (argc == 5 && pg_strcasecmp (argv [4 ], "uniform" ) == 0 ))
1455+ {
1456+ #ifdef DEBUG
1457+ printf ("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n" , min , max , getrand (thread , min , max ));
1458+ #endif
1459+ snprintf (res , sizeof (res ), INT64_FORMAT , getrand (thread , min , max ));
1460+ }
1461+ else if (argc == 6 &&
1462+ ((pg_strcasecmp (argv [4 ], "gaussian" ) == 0 ) ||
1463+ (pg_strcasecmp (argv [4 ], "exponential" ) == 0 )))
1464+ {
1465+ if (* argv [5 ] == ':' )
1466+ {
1467+ if ((var = getVariable (st , argv [5 ] + 1 )) == NULL )
1468+ {
1469+ fprintf (stderr , "%s: invalid threshold number %s\n" , argv [0 ], argv [5 ]);
1470+ st -> ecnt ++ ;
1471+ return true;
1472+ }
1473+ threshold = strtod (var , NULL );
1474+ }
1475+ else
1476+ threshold = strtod (argv [5 ], NULL );
1477+
1478+ if (pg_strcasecmp (argv [4 ], "gaussian" ) == 0 )
1479+ {
1480+ if (threshold < MIN_GAUSSIAN_THRESHOLD )
1481+ {
1482+ fprintf (stderr , "%s: gaussian threshold must be at least %f\n," , argv [5 ], MIN_GAUSSIAN_THRESHOLD );
1483+ st -> ecnt ++ ;
1484+ return true;
1485+ }
1486+ #ifdef DEBUG
1487+ printf ("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n" , min , max , getGaussianRand (thread , min , max , threshold ));
1488+ #endif
1489+ snprintf (res , sizeof (res ), INT64_FORMAT , getGaussianRand (thread , min , max , threshold ));
1490+ }
1491+ else if (pg_strcasecmp (argv [4 ], "exponential" ) == 0 )
1492+ {
1493+ if (threshold <= 0.0 )
1494+ {
1495+ fprintf (stderr , "%s: exponential threshold must be strictly positive\n," , argv [5 ]);
1496+ st -> ecnt ++ ;
1497+ return true;
1498+ }
13801499#ifdef DEBUG
1381- printf ("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n" , min , max , getrand (thread , min , max ));
1500+ printf ("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n" , min , max , getExponentialRand (thread , min , max , threshold ));
13821501#endif
1383- snprintf (res , sizeof (res ), INT64_FORMAT , getrand (thread , min , max ));
1502+ snprintf (res , sizeof (res ), INT64_FORMAT , getExponentialRand (thread , min , max , threshold ));
1503+ }
1504+ }
1505+ else /* this means an error somewhere in the parsing phase... */
1506+ {
1507+ fprintf (stderr , "%s: unexpected arguments\n" , argv [0 ]);
1508+ st -> ecnt ++ ;
1509+ return true;
1510+ }
13841511
13851512 if (!putVariable (st , argv [0 ], argv [1 ], res ))
13861513 {
@@ -1914,15 +2041,51 @@ process_commands(char *buf)
19142041
19152042 if (pg_strcasecmp (my_commands -> argv [0 ], "setrandom" ) == 0 )
19162043 {
2044+ /* parsing:
2045+ * \setrandom variable min max [uniform]
2046+ * \setrandom variable min max (gaussian|exponential) threshold
2047+ */
2048+
19172049 if (my_commands -> argc < 4 )
19182050 {
19192051 fprintf (stderr , "%s: missing argument\n" , my_commands -> argv [0 ]);
19202052 exit (1 );
19212053 }
2054+ /* argc >= 4 */
19222055
1923- for (j = 4 ; j < my_commands -> argc ; j ++ )
1924- fprintf (stderr , "%s: extra argument \"%s\" ignored\n" ,
1925- my_commands -> argv [0 ], my_commands -> argv [j ]);
2056+ if (my_commands -> argc == 4 || /* uniform without/with "uniform" keyword */
2057+ (my_commands -> argc == 5 &&
2058+ pg_strcasecmp (my_commands -> argv [4 ], "uniform" ) == 0 ))
2059+ {
2060+ /* nothing to do */
2061+ }
2062+ else if (/* argc >= 5 */
2063+ (pg_strcasecmp (my_commands -> argv [4 ], "gaussian" ) == 0 ) ||
2064+ (pg_strcasecmp (my_commands -> argv [4 ], "exponential" ) == 0 ))
2065+ {
2066+ if (my_commands -> argc < 6 )
2067+ {
2068+ fprintf (stderr , "%s(%s): missing threshold argument\n" , my_commands -> argv [0 ], my_commands -> argv [4 ]);
2069+ exit (1 );
2070+ }
2071+ else if (my_commands -> argc > 6 )
2072+ {
2073+ fprintf (stderr , "%s(%s): too many arguments (extra:" ,
2074+ my_commands -> argv [0 ], my_commands -> argv [4 ]);
2075+ for (j = 6 ; j < my_commands -> argc ; j ++ )
2076+ fprintf (stderr , " %s" , my_commands -> argv [j ]);
2077+ fprintf (stderr , ")\n" );
2078+ exit (1 );
2079+ }
2080+ }
2081+ else /* cannot parse, unexpected arguments */
2082+ {
2083+ fprintf (stderr , "%s: unexpected arguments (bad:" , my_commands -> argv [0 ]);
2084+ for (j = 4 ; j < my_commands -> argc ; j ++ )
2085+ fprintf (stderr , " %s" , my_commands -> argv [j ]);
2086+ fprintf (stderr , ")\n" );
2087+ exit (1 );
2088+ }
19262089 }
19272090 else if (pg_strcasecmp (my_commands -> argv [0 ], "set" ) == 0 )
19282091 {
0 commit comments