diff --git a/doc/src/sgml/ref/pgbench.sgml b/doc/src/sgml/ref/pgbench.sgml
index 03e1212d50..64c3922134 100644
--- a/doc/src/sgml/ref/pgbench.sgml
+++ b/doc/src/sgml/ref/pgbench.sgml
@@ -1013,6 +1013,14 @@ pgbench options> dbname>
random_gaussian(1, 10, 2.5)>>
an integer between 1> and 10>>
+
+ random_zipfian(lb>, ub>, parameter>)>>>
+ integer>
+ Zipfian-distributed random integer in [lb, ub]>,
+ see below>
+ random_zipfian(1, 10, 0.5)>>
+ an integer between 1> and 10>>
+
sqrt(x>)>>>
double>
@@ -1094,6 +1102,23 @@ f(x) = PHI(2.0 * parameter * (x - mu) / (max - min + 1)) /
of the Box-Muller transform.
+
+
+ random_zipfian> generates only approximate distribution and degree of proximity depends on parameter>.
+ For parameter> in range (0, 1) random number generated by computing Harmonic numbers(algorithm taken from
+ "Quickly Generating Billion-Record Synthetic Databases", Jim Gray et al, SIGMOD 1994).
+ And for parameter bigger than 1 rejection method used(described in "Non-Uniform Random Variate Generation", Luc Devroye, p. 550-551, Springer 1986).
+ It's recommended to pick parameter> in range (0, 1) for better accuracy.
+
+
+
+ parameter>
+ defines how skewed the distribution is. The larger the parameter>, the more
+ frequently value values to the beginning of the interval are drawn.
+ The closer to 0 parameter> is,
+ the flatter (more uniform) the access distribution
+
+
diff --git a/src/bin/pgbench/exprparse.y b/src/bin/pgbench/exprparse.y
index b3a2d9bfd3..25d5ad48e5 100644
--- a/src/bin/pgbench/exprparse.y
+++ b/src/bin/pgbench/exprparse.y
@@ -191,6 +191,9 @@ static const struct
{
"random_exponential", 3, PGBENCH_RANDOM_EXPONENTIAL
},
+ {
+ "random_zipfian", 3, PGBENCH_RANDOM_ZIPFIAN
+ },
/* keep as last array element */
{
NULL, 0, 0
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index ae78c7b1d4..5f30b07d5b 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -93,7 +93,10 @@ static int pthread_join(pthread_t th, void **thread_return);
#define LOG_STEP_SECONDS 5 /* seconds between log messages */
#define DEFAULT_NXACTS 10 /* default nxacts */
+#define ZIPF_CACHE_SIZE 15 /* cache cells number */
+
#define MIN_GAUSSIAN_PARAM 2.0 /* minimum parameter for gauss */
+#define MAX_ZIPFIAN_PARAM 1000 /* maximum parameter for zipfian */
int nxacts = 0; /* number of transactions per client */
int duration = 0; /* duration in seconds */
@@ -333,6 +336,36 @@ typedef struct
int ecnt; /* error count */
} CState;
+/*
+ * Cache cell for zipfian_random call
+ */
+typedef struct
+{
+ /* cell keys */
+ double s; /* s - parameter of zipfan_random function */
+ int64 n; /* number of elements in range (max - min + 1) */
+
+ double harmonicn; /* zipfGeneralizedHarmonic(n, s) */
+ double alpha;
+ double beta;
+ double eta;
+
+ uint64 last_touch_time; /* time when cell was used last time */
+} ZipfCell;
+
+/*
+ * Zipf cache for zeta values
+ */
+typedef struct
+{
+ uint64 current_time; /* counter for LRU cache replacement algorithm */
+
+ int cells_inited; /* number of filled cells */
+ ZipfCell cells[ZIPF_CACHE_SIZE];
+} ZipfCache;
+
+bool zipfCacheOverflowMessagePrinted = false;
+
/*
* Thread state
*/
@@ -345,6 +378,8 @@ typedef struct
unsigned short random_state[3]; /* separate randomness for each thread */
int64 throttle_trigger; /* previous/next throttling (us) */
FILE *logfile; /* where to log, or NULL */
+ ZipfCache zipf_cache; /* for thread-safe zipfian random number
+ * generation */
/* per thread collected stats */
instr_time start_time; /* thread start time */
@@ -737,6 +772,143 @@ getPoissonRand(TState *thread, int64 center)
return (int64) (-log(uniform) * ((double) center) + 0.5);
}
+/* helper function for getZipfianRand */
+static double
+zipfGeneralizedHarmonic(int64 n, double s)
+{
+ int i;
+ double ans = 0.0;
+
+ for (i = n; i > 1; i--)
+ ans += pow(i, -s);
+ return ans + 1.;
+}
+
+/* helper function for getZipfianRand. Computes random variable in range 1..n */
+static int64
+zipfn(double uniform_random, ZipfCell * zipf, int64 n, double s)
+{
+ double uz = uniform_random * zipf->harmonicn;
+
+ if (uz < 1.)
+ return 1;
+ if (uz < 1. + zipf->beta)
+ return 2;
+ return 1 + (int64) (n * pow(zipf->eta * uniform_random - zipf->eta + 1., zipf->alpha));
+}
+
+/* set harmonicn and other parameters to cache cell */
+static void
+zipfSetCacheCell(ZipfCell * cell, int64 n, double s)
+{
+ double harmonic2;
+
+ cell->n = n;
+ cell->s = s;
+
+ harmonic2 = zipfGeneralizedHarmonic(2, s);
+ cell->harmonicn = zipfGeneralizedHarmonic(n, s);
+
+ cell->alpha = 1.0 / (1.0 - s);
+ cell->beta = pow(0.5, s);
+ cell->eta = (1.0 - pow(2.0 / n, 1.0 - s)) / (1.0 - harmonic2 / cell->harmonicn);
+}
+
+/*
+ * search for cache cell with keys (n, s)
+ * and create new cell if it is not exist
+ */
+static ZipfCell *
+zipfFindOrCreateCacheCell(ZipfCache * cache, int64 n, double s)
+{
+ int i,
+ least_recently_used_i = 0;
+ ZipfCell *cell;
+
+ /* search cached cell for given parameters */
+ for (i = 0; i < cache->cells_inited; i++)
+ {
+ cell = &cache->cells[i];
+ if (cell->n == n && cell->s == s)
+ return &cache->cells[i];
+
+ if (cell->last_touch_time < cache->cells[least_recently_used_i].last_touch_time)
+ least_recently_used_i = i;
+ }
+
+ /* create new one if it is not exist */
+ if (cache->cells_inited != ZIPF_CACHE_SIZE)
+ i = cache->cells_inited++;
+ else
+ {
+ /* replace LRU cell if cache is full */
+ i = least_recently_used_i;
+ if (!zipfCacheOverflowMessagePrinted)
+ {
+ /*
+ * flag can be accessed in several threads simultaneously and
+ * message can be printed more than one time
+ */
+ zipfCacheOverflowMessagePrinted = true;
+ fprintf(stderr, "zipfian cache array overflowed\n");
+ }
+ }
+
+ zipfSetCacheCell(&cache->cells[i], n, s);
+
+ return &cache->cells[i];
+}
+
+static int64
+computeIterativeZipfian(unsigned short random_state[3], int64 n, double s)
+{
+ /* computing zipfian using rejection method */
+ double b = pow(2.0, s - 1.0);
+ double x,
+ t,
+ u,
+ v;
+
+ while (true)
+ {
+ /* random variates */
+ u = pg_erand48(random_state);
+ v = pg_erand48(random_state);
+
+ x = floor(pow(u, -1.0 / (s - 1.0)));
+
+ t = pow(1.0 + 1.0 / x, s - 1.0);
+ /* reject if too large or out of bound */
+ if (v * x * (t - 1.0) / (b - 1.0) <= t / b && x <= n)
+ break;
+ }
+ return (int64) x;
+}
+
+static int64
+computeHarmonicZipfian(TState *thread, int64 n, double s)
+{
+ ZipfCell *cell = zipfFindOrCreateCacheCell(&thread->zipf_cache, n, s);
+
+ cell->last_touch_time = thread->zipf_cache.current_time++;
+ double uniform = pg_erand48(thread->random_state);
+
+ return zipfn(uniform, cell, n, s);
+}
+
+/* random number generator: zipfian distribution from min to max inclusive */
+static int64
+getZipfianRand(TState *thread, int64 min, int64 max, double s)
+{
+ int64 n = max - min + 1;
+
+ if (s > 1)
+ /* it is better to use iterative way to compute when s > 1 */
+ return min + computeIterativeZipfian(thread->random_state, n, s) - 1;
+
+ return min + computeHarmonicZipfian(thread, n, s) - 1;
+}
+
/*
* Initialize the given SimpleStats struct to all zeroes
*/
@@ -1567,6 +1739,7 @@ evalFunc(TState *thread, CState *st,
case PGBENCH_RANDOM:
case PGBENCH_RANDOM_EXPONENTIAL:
case PGBENCH_RANDOM_GAUSSIAN:
+ case PGBENCH_RANDOM_ZIPFIAN:
{
int64 imin,
imax;
@@ -1617,6 +1790,18 @@ evalFunc(TState *thread, CState *st,
setIntValue(retval,
getGaussianRand(thread, imin, imax, param));
}
+ else if (func == PGBENCH_RANDOM_ZIPFIAN)
+ {
+ if (param <= 0.0 || param == 1.0 || param > MAX_ZIPFIAN_PARAM)
+ {
+ fprintf(stderr,
+ "zipfian parameter must be in range (0, 1) U (1, %d]"
+ " (got %f)\n", MAX_ZIPFIAN_PARAM,param);
+ return false;
+ }
+ setIntValue(retval,
+ getZipfianRand(thread, imin, imax, param));
+ }
else /* exponential */
{
if (param <= 0.0)
@@ -4273,6 +4458,8 @@ main(int argc, char **argv)
thread->random_state[2] = random();
thread->logfile = NULL; /* filled in later */
thread->latency_late = 0;
+ thread->zipf_cache.cells_inited = 0;
+ thread->zipf_cache.current_time = 0;
initStats(&thread->stats, 0);
nclients_dealt += thread->nstate;
diff --git a/src/bin/pgbench/pgbench.h b/src/bin/pgbench/pgbench.h
index abc13e9463..1a29f1260c 100644
--- a/src/bin/pgbench/pgbench.h
+++ b/src/bin/pgbench/pgbench.h
@@ -75,7 +75,8 @@ typedef enum PgBenchFunction
PGBENCH_SQRT,
PGBENCH_RANDOM,
PGBENCH_RANDOM_GAUSSIAN,
- PGBENCH_RANDOM_EXPONENTIAL
+ PGBENCH_RANDOM_EXPONENTIAL,
+ PGBENCH_RANDOM_ZIPFIAN
} PgBenchFunction;
typedef struct PgBenchExpr PgBenchExpr;