gaussian distribution pgbench

Started by KONDO Mitsumasaover 12 years ago61 messages
#1KONDO Mitsumasa
kondo.mitsumasa@lab.ntt.co.jp
4 attachment(s)

Hello,

I create gaussinan distribution pgbench patch that can access records with
gaussian frequency. And I submit this commit fest.

* Purpose this patch
In the general transaction situation, clients access for all records equally is
hard to happen. I think gaussian distribution access patterns are most of
transaction petterns in general. My patch realizes neary this access pattern.

I think that not only it can simulate a general access pattern as an effect of
this patch, but also it is useful for new development features such as effective
use and free of shared_buffers, the readahead optimization in the OS, and the
speed-up of the tuple level lock.

* Usage
It is easy to use, only put -g with standard deviation threshold parameter.
If we set larger standard deviation threshold, pgbench access patern limited
more specific records. Min standard deviation threshold is 2.

Execution example command is here.

[mitsu-ko@localhost postgresql]$ bin/pgbench -g 10 -c 16 -j 8 -T 300
starting vacuum...end.
transaction type: TPC-B (sort of)
scaling factor: 1
standard deviation threshold: 10.00000
access probability of top 20%, 10% and 5% records: 0.95450 0.68269 0.38292
query mode: simple
number of clients: 16
number of threads: 8
duration: 300 s
number of transactions actually processed: 566367
tps = 1887.821409 (including connections establishing)
tps = 1887.949390 (excluding connections establishing)

"access probability" indicates top N access probability in this benchmark.
If we set larger standard deviation threshold parameter, it become more large.

Attached png files which are "gausian_2.png" and "gaussian_10.png" indicate
gaussian distribution access patern by my patch. "no_gaussian.png" is not with -g
option (normal). I think my patch realize gaussian distribution access patern.

* Approach
It replaces uniform random number generator to gaussian distribution random
number generator using by box-muller tansform method. Then, I use standard
deviation threshold parameter for mapping a normal distribution access pattern in
each record and normalization. It is linear mappping method that is a floating
point to an integer value.

* other
I also create another patches that can get more accurate benchmark result in
pgbench, and will submit them this commit fest. They are like that I submitted
checkpoint patch in the past. They are all right, too!

Any question?

Best regards,
--
Mitsumasa KONDO
NTT Open Source Software Center

Attachments:

gaussian_pgbench_v0.patchtext/x-diff; name=gaussian_pgbench_v0.patchDownload
diff --git a/contrib/pgbench/pgbench.c b/contrib/pgbench/pgbench.c
index ad8e272..77f60ae 100644
--- a/contrib/pgbench/pgbench.c
+++ b/contrib/pgbench/pgbench.c
@@ -40,6 +40,7 @@
 #include <ctype.h>
 #include <math.h>
 #include <signal.h>
+#include <limits.h>
 
 #ifndef WIN32
 #include <sys/time.h>
@@ -175,6 +176,8 @@ int         progress_nclients = 0; /* number of clients for progress report */
 bool		is_connect;			/* establish connection for each transaction */
 bool		is_latencies;		/* report per-command latencies */
 int			main_pid;			/* main process id used in log filename */
+bool		use_gaussian = false;		/* use gaussian distribution benchmark */
+double		stdev_threshold = 5;		/* standard deviation threshold */
 
 char	   *pghost = "";
 char	   *pgport = "";
@@ -360,6 +363,7 @@ usage(void)
 		   "  -D, --define=VARNAME=VALUE\n"
 		   "                           define variable for use by custom script\n"
 		   "  -f, --file=FILENAME      read transaction script from FILENAME\n"
+		   "  -g, --gaussian=NUM       gaussian distribution benchmark with NUM standard deviation threshold\n"
 		   "  -j, --jobs=NUM           number of threads (default: 1)\n"
 		   "  -l, --log                write transaction times to log file\n"
 		   "  -M, --protocol=simple|extended|prepared\n"
@@ -471,7 +475,27 @@ getrand(TState *thread, int64 min, int64 max)
 	 * protected by a mutex, and therefore a bottleneck on machines with many
 	 * CPUs.
 	 */
-	return min + (int64) ((max - min + 1) * pg_erand48(thread->random_state));
+	double rand = pg_erand48(thread->random_state);
+	double rand1;
+	double rand2;
+	double stdev;
+
+	if(!use_gaussian)
+		return min + (int64) ((max - min + 1) * rand);
+
+	/* generate gaussian distribution */
+	rand1 = (rand * (LONG_MAX - 1.0) + 0.5) / LONG_MAX;
+	do
+	{
+		rand2 = pg_erand48(thread->random_state);
+		/* Box-Muller transform */
+		stdev = sqrt(-2.0 * log(rand1)) * sin(2.0 * M_PI * rand2);
+	}while( stdev < (-1.0 * stdev_threshold) || stdev > stdev_threshold);
+
+	/* normalization */
+	rand = (stdev + stdev_threshold) / (stdev_threshold * 2.0);
+
+	return min + (int64) (max - min + 1) * rand ;
 }
 
 /* call PQexec() and exit() on failure */
@@ -935,7 +959,7 @@ top:
 		 * a transaction, the next transaction will start right away.
 		 */
 		int64 wait = (int64)
-			throttle_delay * -log(getrand(thread, 1, 1000)/1000.0);
+			throttle_delay * - log((int64) ((pg_erand48(thread->random_state) * (LONG_MAX - 1.0) + 0.5) / LONG_MAX));
 
 		thread->throttle_trigger += wait;
 
@@ -2119,6 +2143,15 @@ printResults(int ttype, int normal_xacts, int nclients,
 
 	printf("transaction type: %s\n", s);
 	printf("scaling factor: %d\n", scale);
+	if(use_gaussian)
+	{
+		printf("standard deviation threshold: %.5f\n", stdev_threshold);
+		printf("access probability of top 20%%, 10%% and 5%% records: %.5f %.5f %.5f\n",
+			(double) ((erf (stdev_threshold * 0.2 / sqrt(2.0))) / (erf (stdev_threshold / sqrt(2.0)))),
+			(double) ((erf (stdev_threshold * 0.1 / sqrt(2.0))) / (erf (stdev_threshold / sqrt(2.0)))),
+			(double) ((erf (stdev_threshold * 0.05 / sqrt(2.0))) / (erf (stdev_threshold / sqrt(2.0))))
+			);
+	}
 	printf("query mode: %s\n", QUERYMODE[querymode]);
 	printf("number of clients: %d\n", nclients);
 	printf("number of threads: %d\n", nthreads);
@@ -2208,6 +2241,7 @@ main(int argc, char **argv)
 		{"define", required_argument, NULL, 'D'},
 		{"file", required_argument, NULL, 'f'},
 		{"fillfactor", required_argument, NULL, 'F'},
+		{"gaussian", required_argument, NULL, 'g'},
 		{"host", required_argument, NULL, 'h'},
 		{"initialize", no_argument, NULL, 'i'},
 		{"jobs", required_argument, NULL, 'j'},
@@ -2301,7 +2335,7 @@ main(int argc, char **argv)
 	state = (CState *) pg_malloc(sizeof(CState));
 	memset(state, 0, sizeof(CState));
 
-	while ((c = getopt_long(argc, argv, "ih:nvp:dqSNc:j:Crs:t:T:U:lf:D:F:M:P:R:", long_options, &optindex)) != -1)
+	while ((c = getopt_long(argc, argv, "ih:nvp:dqSNc:j:Crs:t:T:U:lf:g:D:F:M:P:R:", long_options, &optindex)) != -1)
 	{
 		switch (c)
 		{
@@ -2418,6 +2452,15 @@ main(int argc, char **argv)
 				if (process_file(filename) == false || *sql_files[num_files - 1] == NULL)
 					exit(1);
 				break;
+			case 'g':
+				use_gaussian = true;
+				stdev_threshold = atoi(optarg);
+				if(stdev_threshold < 2)
+				{
+					fprintf(stderr, "gaussian option (-g) must be more than 2: %f\n", stdev_threshold);
+					exit(1);
+				}
+				break;
 			case 'D':
 				{
 					char	   *p;
diff --git a/doc/src/sgml/pgbench.sgml b/doc/src/sgml/pgbench.sgml
index 49a79b1..5f7cb1e 100644
--- a/doc/src/sgml/pgbench.sgml
+++ b/doc/src/sgml/pgbench.sgml
@@ -320,6 +320,18 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
      </varlistentry>
 
      <varlistentry>
+      <term><option>-g</option> <replaceable>standard deviation</></term>
+      <term><option>--gaussian</option><replaceable>standard deviation</></term>
+      <listitem>
+       <para>
+        Gaussian distribution pgbench option. Need the standard deviation threshold.
+        If we set larger standard deviation threshold, pgbench access patern limited
+        more specific records. Min standard deviation threshold is 2.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
       <term><option>-j</option> <replaceable>threads</></term>
       <term><option>--jobs=</option><replaceable>threads</></term>
       <listitem>
gaussian_2.pngimage/png; name=gaussian_2.pngDownload
gaussian_10.pngimage/png; name=gaussian_10.pngDownload
no_gaussian.pngimage/png; name=no_gaussian.pngDownload
#2Kevin Grittner
kgrittn@ymail.com
In reply to: KONDO Mitsumasa (#1)
Re: gaussian distribution pgbench

KONDO Mitsumasa <kondo.mitsumasa@lab.ntt.co.jp> wrote:

I create gaussinan distribution pgbench patch that can access
records with gaussian frequency. And I submit this commit fest.

Thanks!

I have moved this to the Open CommitFest, though.

https://commitfest.postgresql.org/action/commitfest_view/open

You had accidentally added to the CF In Progress.

--
Kevin Grittner
EDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#3Fabien COELHO
coelho@cri.ensmp.fr
In reply to: KONDO Mitsumasa (#1)
Re: gaussian distribution pgbench

Hello Mitsumasa,

In the general transaction situation, clients access for all records equally is
hard to happen. I think gaussian distribution access patterns are most of
transaction petterns in general. My patch realizes neary this access pattern.

That is great! I was just looking for something like that!

I have not looked at the patch yet, but from the plots you sent, it seems
that it is a gaussian distribution over the keys. However this pattern
induces stronger cache effects which are maybe not too realistic, because
neighboring keys in the middle are more likely to be chosen.

It seems to me that this is not desirable.

Have you considered adding a "randomization" layer, that is once you have
a key in [1 .. n] centered around n/2, then you perform a pseudo-random
transformation into the same domain so that key values are scattered over
the whole domain?

--
Fabien.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#4Mitsumasa KONDO
kondo.mitsumasa@gmail.com
In reply to: Fabien COELHO (#3)
Re: gaussian distribution pgbench

You had accidentally added to the CF In Progress.

Oh, I had completely mistook this CF schedule :-)

Maybe, Horiguchi-san is same situation...

However, because of your moving, I become first submitter in next CF.

Thank you for moving :-)

--

Mitsumasa KONDO

#5Mitsumasa KONDO
kondo.mitsumasa@gmail.com
In reply to: Mitsumasa KONDO (#4)
Re: gaussian distribution pgbench

However this pattern induces stronger cache effects which are maybe not

too realistic,

because neighboring keys in the middle are more likely to be chosen.

I think that your opinion is right. However, in effect, it is a
paseudo-benchmark, so that I think that such a simple mechanism is also
necessary.

Have you considered adding a "randomization" layer, that is once you have

a key in [1 .. > n] centered around n/2, then you perform a pseudo-random
transformation into the same > domain so that key values are scattered over
the whole domain?

Yes. I also consider this patch. It can realize by adding linear mapping
array which is created by random generator. However, current erand48
algorithm is not high accuracy and fossil algorithm, I do not know whether
it works well. If we realize it, we may need more accurate random generator
algorithm which is like Mersenne Twister*.*

Regards,

--

Mitsumasa KONDO

#6Peter Eisentraut
peter_e@gmx.net
In reply to: KONDO Mitsumasa (#1)
Re: gaussian distribution pgbench

On 9/20/13 2:42 AM, KONDO Mitsumasa wrote:

I create gaussinan distribution pgbench patch that can access records with
gaussian frequency. And I submit this commit fest.

This patch no longer applies.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#7KONDO Mitsumasa
kondo.mitsumasa@lab.ntt.co.jp
In reply to: Kevin Grittner (#2)
Re: gaussian distribution pgbench

Sorry for my delay reply.
Since I have had vacation last week, I replyed from gmail.
However, it was stalled post to pgsql-hackers:-(

(2013/09/21 6:05), Kevin Grittner wrote:

You had accidentally added to the CF In Progress.

Oh, I had completely mistook this CF schedule :-)
Maybe, Horiguchi-san is same situation...

However, because of your moving, I become first submitter in next CF.
Thank you for moving !
--
Mitsumasa KONDO
NTT Open Source Software Center

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#8KONDO Mitsumasa
kondo.mitsumasa@lab.ntt.co.jp
In reply to: Fabien COELHO (#3)
Re: gaussian distribution pgbench

Sorry for my delay reply.
Since I have had vacation last week, I replied from gmail.
However, it was stalled post to pgsql-hackers:-(

(2013/09/21 7:54), Fabien COELHO wrote:

However this pattern induces stronger cache effects which are maybe not too realistic,
because neighboring keys in the middle are more likely to be chosen.

I think that your opinion is right. However, in effect, it is a
paseudo-benchmark, so that I think that such a simple mechanism is also necessary.

Have you considered adding a "randomization" layer, that is once you have a key in [1 .. > n] centered around n/2, then you perform a pseudo-random transformation into the same > domain so that key values are scattered over the whole domain?

Yes. I also consider this patch. It can realize by adding linear mapping array
which is created by random generator. However, current erand48 algorithm is not
high accuracy and fossil algorithm, I do not know whether it works well. If we
realize it, we may need more accurate random generator algorithm which is like
Mersenne Twister.

Regards,
--
Mitsumasa KONDO
NTT Open Source Software Center

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#9KONDO Mitsumasa
kondo.mitsumasa@lab.ntt.co.jp
In reply to: Peter Eisentraut (#6)
Re: gaussian distribution pgbench

(2013/09/27 5:29), Peter Eisentraut wrote:

This patch no longer applies.

I will try to create this patch in next commit fest.
If you have nice idea, please send me!

Regards,
--
Mitsumasa KONDO
NTT Open Source Software Center

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#10Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: KONDO Mitsumasa (#9)
Re: gaussian distribution pgbench

On 30.09.2013 07:12, KONDO Mitsumasa wrote:

(2013/09/27 5:29), Peter Eisentraut wrote:

This patch no longer applies.

I will try to create this patch in next commit fest.
If you have nice idea, please send me!

A few thoughts on this:

1. DBT-2 uses a non-uniform distribution. You can use that instead of
pgbench.

2. Do we really want to add everything and the kitchen sink to pgbench?
Every addition is small when considered alone, but we'll soon end with a
monster. So I'm inclined to reject this patch on those grounds.

3. That said, this could be handy. But it would be even more handy if
you could get Gaussian random numbers with \setrandom, so that you could
use this with custom scripts. And once you implement that, do we
actually need the -g flag anymore? If you want TPC-B transactions with
gaussian distribution, you can write a custom script to do that. The
documentation includes a full script that corresponds to the built-in
TPC-B script.

So what I'd actually like to see is \setgaussian, for use in custom scripts.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#11Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Heikki Linnakangas (#10)
Re: gaussian distribution pgbench

3. That said, this could be handy. But it would be even more handy if you
could get Gaussian random numbers with \setrandom, so that you could use this
with custom scripts. And once you implement that, do we actually need the -g
flag anymore? If you want TPC-B transactions with gaussian distribution, you
can write a custom script to do that. The documentation includes a full
script that corresponds to the built-in TPC-B script.

So what I'd actually like to see is \setgaussian, for use in custom scripts.

Indeed, great idea! That looks pretty elegant! It would be something like:

\setgauss var min max sigma

I'm not sure whether sigma should be relative to max-min, or absolute.
I would say relative is better...

A concerned I raised is that what one should really want is a "pseudo
randomized" (discretized) gaussian, i.e. you want the probability of each
value along a gaussian distribution, *but* no direct frequency correlation
between neighbors. Otherwise, you may have unwanted/unrealistic positive
cache effects. Maybe this could be achieved by an independent built-in,
say either:

\randomize var min max [parameter ?]
\randomize var min max val [parameter]

Which would mean take variable var which must be in [min,max], and apply a
pseudo-random transformation which results is also in [min,max].

From a probabilistic point of view, it seems to me that a randomized
(discretized) exponential would be more significant to model a server
load.

\setexp var min max lambda...

--
Fabien.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#12Peter Geoghegan
pg@heroku.com
In reply to: Heikki Linnakangas (#10)
Re: gaussian distribution pgbench

On Thu, Nov 21, 2013 at 9:13 AM, Heikki Linnakangas
<hlinnakangas@vmware.com> wrote:

So what I'd actually like to see is \setgaussian, for use in custom scripts.

+1. I'd really like to be able to run a benchmark with a Gaussian and
uniform distribution side-by-side for comparative purposes - we need
to know that we're not optimizing one at the expense of the other.
Sure, DBT-2 gets you a non-uniform distribution, but it has serious
baggage from it being a tool primarily intended for measuring the
relative performance of different database systems. pgbench would be
pretty worthless for measuring the relative strengths and weaknesses
of different database systems, but it is not bad at informing the
optimization efforts of hackers. pgbench is a defacto standard for
that kind of thing, so we should make it incrementally better for that
kind of thing. No standard industry benchmark is likely to replace it
for this purpose, because such optimizations require relatively narrow
focus.

Sometimes I want to maximally pessimize the number of FPIs generated.
Other times I do not. Getting a sense of how something affects a
variety of distributions would be very valuable, not least since
normal distributions abound in nature.

--
Peter Geoghegan

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#13Gavin Flower
GavinFlower@archidevsys.co.nz
In reply to: Peter Geoghegan (#12)
Re: gaussian distribution pgbench

On 20/12/13 09:36, Peter Geoghegan wrote:

On Thu, Nov 21, 2013 at 9:13 AM, Heikki Linnakangas
<hlinnakangas@vmware.com> wrote:

So what I'd actually like to see is \setgaussian, for use in custom scripts.

+1. I'd really like to be able to run a benchmark with a Gaussian and
uniform distribution side-by-side for comparative purposes - we need
to know that we're not optimizing one at the expense of the other.
Sure, DBT-2 gets you a non-uniform distribution, but it has serious
baggage from it being a tool primarily intended for measuring the
relative performance of different database systems. pgbench would be
pretty worthless for measuring the relative strengths and weaknesses
of different database systems, but it is not bad at informing the
optimization efforts of hackers. pgbench is a defacto standard for
that kind of thing, so we should make it incrementally better for that
kind of thing. No standard industry benchmark is likely to replace it
for this purpose, because such optimizations require relatively narrow
focus.

Sometimes I want to maximally pessimize the number of FPIs generated.
Other times I do not. Getting a sense of how something affects a
variety of distributions would be very valuable, not least since
normal distributions abound in nature.

Curious, wouldn't the common usage pattern tend to favour a skewed
distribution, such as the Poisson Distribution (it has been over 40
years since I studied this area, so there may be better candidates).

Just that gut feeling & experience tends to make me think that the
"Normal" distribution may often not be the best for database access
simulation.

Cheers,
Gavin

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#14Gregory Smith
gregsmithpgsql@gmail.com
In reply to: Gavin Flower (#13)
Re: gaussian distribution pgbench

On 12/19/13 5:52 PM, Gavin Flower wrote:

Curious, wouldn't the common usage pattern tend to favour a skewed
distribution, such as the Poisson Distribution (it has been over 40
years since I studied this area, so there may be better candidates).

Some people like database load testing with a "Pareto principle"
distribution, where 80% of the activity hammers 20% of the rows such
that locking becomes important. (That's one specific form of Pareto
distribution) The standard pgbench load indirectly gets you quite a bit
of that due to all the contention on the branches table. Targeting all
of that at a single table can be more realistic.

My last round of reviewing a pgbench change left me pretty worn out with
wanting to extend that code much further. Adding in some new
probability distributions would be fine though, that's a narrow change.
We shouldn't get too excited about pgbench remaining a great tool for
too much longer though. pgbench is fast approaching a wall nowadays,
where it's hard for any single client server to fully overload today's
larger server. You basically need a second large server to generate
load, whereas what people really want is a bunch of coordinated small
clients. (That sort of wall was in early versions too, it just got
pushed upward a lot by the multi-worker changes in 9.0 coming around the
same time desktop core counts really skyrocketed)

pgbench started as a clone of a now abandoned Java project called
JDBCBench. I've been seriously considering a move back toward that
direction lately. Nowadays spinning up ten machines to run load
generation is trivial. The idea of extending pgbench's C code to
support multiple clients running at the same time and collating all of
their results is not a project I'd be excited about. It should remain a
perfectly fine tool for PostgreSQL developers to find code hotspots, but
that's only so useful.

(At this point someone normally points out Tsung solved all of those
problems years ago if you'd only give it a chance. I think it's kind of
telling that work on sysbench is rewriting the whole thing so you can
use Lua for your test scripts.)

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#15KONDO Mitsumasa
kondo.mitsumasa@lab.ntt.co.jp
In reply to: Gregory Smith (#14)
4 attachment(s)
Re: gaussian distribution pgbench

Hi,

I revise my gaussian pgbench patch which wss requested from community.

* Changes
- Support custom script.
- "\setgaussian" is generating gaussian distribute random number.
- ex) \setgaussian [var] [min] [max] [stddev_threshold]
- We can use mixture model in multiple custom scripts.
- Delete short option "-g", and add long options ”--gaussian"
- Refactoring getrand() interface

- getrand(TState *thread, int64 min, int64 max)
+ getrand(TState *thread, int64 min, int64 max, DistType dist_type, double value1)

- We can easy to add other random distribution algorithms. Please see detail
design in attached patch.

Febien COELHO wrote:

From a probabilistic point of view, it seems to me that a randomized

(discretized) exponential would be more significant to model a server load.

\setexp var min max lambda...

I can create randomized exponential distribution under following. It is very easy.
double rand_exp( double lambda ){
return -log(Uniform(0,1))/lambda;
}
If community wants this, I will add this function in my patch.

Gavin Flower wrote:

Curious, wouldn't the common usage pattern tend to favour a skewed distribution,
such as the Poisson Distribution (it has been over 40 years since I studied
this area, so there may be better candidates).

The difference between Poisson distribution and Gaussian distribution is discrete
or not.
In my gaussian algorithm, first generating continuos gaussian distribution, next
projection to integer values which are each record, it will be discrete value.
Therefore, it will be almost simular with Poisson distribution. And when we set
larger standard deviations(higher 10), it will be created better approximation of
Poisson distribution.

Attached sql files are for custom scripts which are different distribution. It
realize mixture distribuion benchmark. And attached graph is the result.
[example command]
$pgbench -f file1.sql file2.sql

If you have more some comment, please send me.
Regards,
--
Mitsumasa KONDO
NTT Open Source Software Center

Attachments:

gaussian_pgbench_v3.patchtext/x-diff; name=gaussian_pgbench_v3.patchDownload
*** a/contrib/pgbench/pgbench.c
--- b/contrib/pgbench/pgbench.c
***************
*** 40,45 ****
--- 40,46 ----
  #include <ctype.h>
  #include <math.h>
  #include <signal.h>
+ #include <limits.h>
  
  #ifndef WIN32
  #include <sys/time.h>
***************
*** 176,181 **** int			progress_nthreads = 0; /* number of threads for progress report */
--- 177,183 ----
  bool		is_connect;			/* establish connection for each transaction */
  bool		is_latencies;		/* report per-command latencies */
  int			main_pid;			/* main process id used in log filename */
+ double		stdev_threshold = 5;		/* standard deviation threshold */
  
  char	   *pghost = "";
  char	   *pgport = "";
***************
*** 267,272 **** typedef enum QueryMode
--- 269,280 ----
  	NUM_QUERYMODE
  } QueryMode;
  
+ typedef enum DistType
+ {
+ 	DIST_UNIFORM,				/* normal random distribution */
+ 	DIST_GAUSSIAN				/* gaussian random distribution */
+ } DistType;
+ 
  static QueryMode querymode = QUERY_SIMPLE;
  static const char *QUERYMODE[] = {"simple", "extended", "prepared"};
  
***************
*** 338,343 **** static char *select_only = {
--- 346,392 ----
  	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
  };
  
+ /* --gaussian case */
+ static char *gaussian_tpc_b = {
+ 	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+ 	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setgaussian aid 1 :naccounts :stdev_threshold\n"
+ 	"\\setrandom bid 1 :nbranches\n"
+ 	"\\setrandom tid 1 :ntellers\n"
+ 	"\\setrandom delta -5000 5000\n"
+ 	"BEGIN;\n"
+ 	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ 	"UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;\n"
+ 	"UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;\n"
+ 	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+ 	"END;\n"
+ };
+ 
+ /* --gaussian-n case */
+ static char *gaussian_simple_update = {
+ 	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+ 	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setgaussian aid 1 :naccounts :stdev_threshold\n"
+ 	"\\setrandom bid 1 :nbranches\n"
+ 	"\\setrandom tid 1 :ntellers\n"
+ 	"\\setrandom delta -5000 5000\n"
+ 	"BEGIN;\n"
+ 	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ 	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+ 	"END;\n"
+ };
+ 
+ /* --gaussian-s case */
+ static char *gaussian_select_only = {
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setgaussian aid 1 :naccounts :stdev_threshold\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ };
+ 
  /* Function prototypes */
  static void setalarm(int seconds);
  static void *threadRun(void *arg);
***************
*** 381,386 **** usage(void)
--- 430,438 ----
  		   "  -v, --vacuum-all         vacuum all four standard tables before tests\n"
  		   "  --aggregate-interval=NUM aggregate data over NUM seconds\n"
  		   "  --sampling-rate=NUM      fraction of transactions to log (e.g. 0.01 for 1%%)\n"
+ 		   "  --gaussian=NUM           gaussian tpc-b with NUM standard deviation threshold\n"
+ 		   "  --gaussian-n=NUM         gaussian -N option benchmark with NUM standard deviation threshold\n"
+ 		   "  --gaussian-s=NUM         gaussian -S option benchmark with NUM standard deviation threshold\n"
  		   "\nCommon options:\n"
  		   "  -d, --debug              print debugging output\n"
  		   "  -h, --host=HOSTNAME      database server host or socket directory\n"
***************
*** 461,469 **** gotdigits:
  	return ((sign < 0) ? -result : result);
  }
  
! /* random number generator: uniform distribution from min to max inclusive */
  static int64
! getrand(TState *thread, int64 min, int64 max)
  {
  	/*
  	 * Odd coding is so that min and max have approximately the same chance of
--- 513,521 ----
  	return ((sign < 0) ? -result : result);
  }
  
! /* random number generator: uniform or gaussian distribution from min to max inclusive */
  static int64
! getrand(TState *thread, int64 min, int64 max, DistType dist_type, double value1)
  {
  	/*
  	 * Odd coding is so that min and max have approximately the same chance of
***************
*** 474,480 **** getrand(TState *thread, int64 min, int64 max)
  	 * protected by a mutex, and therefore a bottleneck on machines with many
  	 * CPUs.
  	 */
! 	return min + (int64) ((max - min + 1) * pg_erand48(thread->random_state));
  }
  
  /* call PQexec() and exit() on failure */
--- 526,567 ----
  	 * protected by a mutex, and therefore a bottleneck on machines with many
  	 * CPUs.
  	 */
! 	double rand = pg_erand48(thread->random_state);
! 
! 	switch(dist_type)
! 	{
! 		 /* Generate uniform distribution. */
! 		case DIST_UNIFORM :
! 			break;
! 
! 		/* Generate gaussian distribution. */
! 		case DIST_GAUSSIAN :
! 		{
! 			double rand1 = (rand * (LONG_MAX - 1.0) + 0.5) / LONG_MAX;
! 			double rand2;
! 			double stdev;
! 			double stdev_threshold = value1;
! 
! 			/* get user specified random number until appeared ranged number in this loop */
! 			do
! 			{
! 				rand2 = pg_erand48(thread->random_state);
! 				/* Box-Muller transform */
! 				stdev = sqrt(-2.0 * log(rand1)) * sin(2.0 * M_PI * rand2);
! 			} while ( stdev < (-1.0 * stdev_threshold) || stdev > stdev_threshold);
! 
! 			/* normalization */
! 			rand = (stdev + value1) / (value1 * 2.0);
! 			break;
! 		}
! 
! 		/* maybe bug.. */
! 		default :
! 			return 0;
! 	}
! 
! 	/* return int64 random number within between min and max */
! 	return min + (int64) (max - min + 1) * rand ;
  }
  
  /* call PQexec() and exit() on failure */
***************
*** 942,948 **** top:
  		 * a transaction, the next transaction will start right away.
  		 */
  		int64 wait = (int64) (throttle_delay *
! 			1.00055271703 * -log(getrand(thread, 1, 10000)/10000.0));
  
  		thread->throttle_trigger += wait;
  
--- 1029,1035 ----
  		 * a transaction, the next transaction will start right away.
  		 */
  		int64 wait = (int64) (throttle_delay *
! 			1.00055271703 * - log(getrand(thread, 1, 10000, DIST_UNIFORM, 0)/10000.0));
  
  		thread->throttle_trigger += wait;
  
***************
*** 1179,1185 **** top:
  		if (commands[st->state] == NULL)
  		{
  			st->state = 0;
! 			st->use_file = (int) getrand(thread, 0, num_files - 1);
  			commands = sql_files[st->use_file];
  			st->is_throttled = false;
  			/*
--- 1266,1273 ----
  		if (commands[st->state] == NULL)
  		{
  			st->state = 0;
! 			st->use_file = (int) getrand(thread, 0, num_files - 1,
! 								DIST_UNIFORM, 0);
  			commands = sql_files[st->use_file];
  			st->is_throttled = false;
  			/*
***************
*** 1379,1387 **** top:
  			}
  
  #ifdef DEBUG
! 			printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getrand(thread, min, max));
  #endif
! 			snprintf(res, sizeof(res), INT64_FORMAT, getrand(thread, min, max));
  
  			if (!putVariable(st, argv[0], argv[1], res))
  			{
--- 1467,1475 ----
  			}
  
  #ifdef DEBUG
! 			printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getrand(thread, min, max, DIST_UNIFORM, 0));
  #endif
! 			snprintf(res, sizeof(res), INT64_FORMAT, getrand(thread, min, max, DIST_UNIFORM, 0));
  
  			if (!putVariable(st, argv[0], argv[1], res))
  			{
***************
*** 1391,1396 **** top:
--- 1479,1574 ----
  
  			st->listen = 1;
  		}
+ 		else if (pg_strcasecmp(argv[0], "setgaussian") == 0)
+ 		{
+ 			char	*var;
+ 			char	*endptr;
+ 			int64	min;
+ 			int64	max;
+ 			double	stdev_threshold;
+ 			char	res[64];
+ 
+ 			if (*argv[2] == ':')
+ 			{
+ 				if((var = getVariable(st, argv[2] + 1)) == NULL)
+ 				{
+ 					fprintf(stderr, "%s: undefined variable %s\n", argv[0], argv[2]);
+ 					st->ecnt++;
+ 					return true;
+ 				}
+ 				min = strtoint64(var);
+ 			}
+ 			else
+ 				min = strtoint64(argv[2]);
+ #ifdef NOT_USED
+ 			if (min < 0)
+ 			{
+ 				fprintf(stderr, "%s: invalid minimum number %d\n", argv[0], min);
+ 				st->ecnt++;
+ 				return;
+ 			}
+ #endif
+ 			if (*argv[3] == ':')
+ 			{
+ 				if((var = getVariable(st, argv[3] + 1)) == NULL)
+ 				{
+ 					fprintf(stderr, "%s: invalid maximum number %s\n", argv[0], argv[3]);
+ 					st->ecnt++;
+ 					return true;
+ 				}
+ 				max = strtoint64(var);
+ 			}
+ 			else
+ 				max = strtoint64(argv[3]);
+ 
+ 			/* check if min and max are appropriate value */
+ 			if(max < min)
+ 			{
+ 				fprintf(stderr, "%s: maximum is less than minimum\n", argv[0]);
+ 				st->ecnt++;
+ 				return true;
+ 			}
+ 
+ 			/* for not overflowing when generating random number */
+ 			if(max - min < 0 || (max - min) + 1 < 0)
+ 			{
+ 				fprintf(stderr, "%s: range too large\n", argv[0]);
+ 				st->ecnt++;
+ 				return true;
+ 			}
+ 
+ 			if(*argv[4] == ':')
+ 			{
+ 				if((var = getVariable(st, argv[4] + 1)) == NULL)
+ 				{
+ 					fprintf(stderr, "%s: invalid gausian threshold number %s\n", argv[0], argv[4]);
+ 					st->ecnt++;
+ 					return true;
+ 				}
+ 				stdev_threshold = strtod(var, NULL);
+ 			}
+ 			else
+ 				stdev_threshold = strtod(argv[4], &endptr);
+ 
+ 			if ( stdev_threshold < 2)
+ 			{
+ 				fprintf(stderr, "%s: gaussian threshold must be more than 2\n,", argv[4]);
+ 				st->ecnt++;
+ 				return true;
+ 			}
+ #ifdef DEBUG
+ 			printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getrand(thread, min, max, DIST_GAUSSIAN, stdev_threshold));
+ #endif
+ 			snprintf(res, sizeof(res), INT64_FORMAT, getrand(thread, min, max, DIST_GAUSSIAN, stdev_threshold));
+ 
+ 			if(!putVariable(st, argv[0], argv[1], res))
+ 			{
+ 				st->ecnt++;
+ 				return true;
+ 			}
+ 
+ 			st->listen = 1;
+ 		}
  		else if (pg_strcasecmp(argv[0], "set") == 0)
  		{
  			char	   *var;
***************
*** 1915,1920 **** process_commands(char *buf)
--- 2093,2110 ----
  				fprintf(stderr, "%s: extra argument \"%s\" ignored\n",
  						my_commands->argv[0], my_commands->argv[j]);
  		}
+ 		else if (pg_strcasecmp(my_commands->argv[0], "setgaussian") == 0)
+ 		{
+ 			if (my_commands->argc < 5)
+ 			{
+ 				fprintf(stderr, "%s: missing argument\n", my_commands->argv[0]);
+ 				exit(1);
+ 			}
+ 
+ 			for (j = 5; j < my_commands->argc; j++)
+ 				fprintf(stderr, "%s: extra argument \"%s\" ignored\n",
+ 						my_commands->argv[0], my_commands->argv[j]);
+ 		}
  		else if (pg_strcasecmp(my_commands->argv[0], "set") == 0)
  		{
  			if (my_commands->argc < 3)
***************
*** 2193,2203 **** printResults(int ttype, int normal_xacts, int nclients,
--- 2383,2411 ----
  		s = "Update only pgbench_accounts";
  	else if (ttype == 1)
  		s = "SELECT only";
+ 	else if (ttype == 4)
+ 		s = "Gaussian distributed TPC-B (sort of)";
+ 	else if (ttype == 5)
+ 		s = "Gaussian distributed update only pgbench_accounts";
+ 	else if (ttype == 6)
+ 		s = "Gaussian distributed SELECT only";
  	else
  		s = "Custom query";
  
  	printf("transaction type: %s\n", s);
  	printf("scaling factor: %d\n", scale);
+ 
+ 	/* gaussian distributed */
+ 	if(ttype == 4 || ttype == 5 || ttype == 6)
+ 	{
+ 		printf("standard deviation threshold: %.5f\n", stdev_threshold);
+ 		printf("access probability of top 20%%, 10%% and 5%% records: %.5f %.5f %.5f\n",
+ 			(double) ((erf (stdev_threshold * 0.2 / sqrt(2.0))) / (erf (stdev_threshold / sqrt(2.0)))),
+ 			(double) ((erf (stdev_threshold * 0.1 / sqrt(2.0))) / (erf (stdev_threshold / sqrt(2.0)))),
+ 			(double) ((erf (stdev_threshold * 0.05 / sqrt(2.0))) / (erf (stdev_threshold / sqrt(2.0))))
+ 			);
+ 	}
+ 
  	printf("query mode: %s\n", QUERYMODE[querymode]);
  	printf("number of clients: %d\n", nclients);
  	printf("number of threads: %d\n", nthreads);
***************
*** 2327,2332 **** main(int argc, char **argv)
--- 2535,2543 ----
  		{"unlogged-tables", no_argument, &unlogged_tables, 1},
  		{"sampling-rate", required_argument, NULL, 4},
  		{"aggregate-interval", required_argument, NULL, 5},
+ 		{"gaussian", required_argument, NULL, 6},
+ 		{"gaussian-n", required_argument, NULL, 7},
+ 		{"gaussian-s", required_argument, NULL, 8},
  		{"rate", required_argument, NULL, 'R'},
  		{NULL, 0, NULL, 0}
  	};
***************
*** 2606,2611 **** main(int argc, char **argv)
--- 2817,2849 ----
  				}
  #endif
  				break;
+ 			case 6:
+ 				ttype = 4;
+ 				stdev_threshold = atof(optarg);
+ 				if(stdev_threshold < 2)
+ 				{
+ 					fprintf(stderr, "--gaussian=NUM must be more than 2: %f\n", stdev_threshold);
+ 					exit(1);
+ 				}
+ 				break;
+ 			case 7:
+ 				ttype = 5;
+ 				stdev_threshold = atof(optarg);
+ 				if(stdev_threshold < 2)
+ 				{
+ 					fprintf(stderr, "--gaussian-n=NUM must be more than 2: %f\n", stdev_threshold);
+ 					exit(1);
+ 				}
+ 				break;
+ 			case 8:
+ 				ttype = 6;
+ 				stdev_threshold = atof(optarg);
+ 				if(stdev_threshold < 2)
+ 				{
+ 					fprintf(stderr, "--gaussian-s=NUM must be more than 2: %f\n", stdev_threshold);
+ 					exit(1);
+ 				}
+ 				break;
  			default:
  				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
  				exit(1);
***************
*** 2803,2808 **** main(int argc, char **argv)
--- 3041,3057 ----
  		}
  	}
  
+ 	/* set :stdev_threshold variable */
+ 	if(getVariable(&state[0], "stdev_threshold") == NULL)
+ 	{
+ 		snprintf(val, sizeof(val), "%lf", stdev_threshold);
+ 		for (i = 0; i < nclients; i++)
+ 		{
+ 			if (!putVariable(&state[i], "startup", "stdev_threshold", val))
+ 				exit(1);
+ 		}
+ 	}
+ 
  	if (!is_no_vacuum)
  	{
  		fprintf(stderr, "starting vacuum...");
***************
*** 2841,2847 **** main(int argc, char **argv)
  			sql_files[0] = process_builtin(simple_update);
  			num_files = 1;
  			break;
! 
  		default:
  			break;
  	}
--- 3090,3107 ----
  			sql_files[0] = process_builtin(simple_update);
  			num_files = 1;
  			break;
! 		case 4:
! 			sql_files[0] = process_builtin(gaussian_tpc_b);
! 			num_files = 1;
! 			break;
! 		case 5:
! 			sql_files[0] = process_builtin(gaussian_simple_update);
! 			num_files = 1;
! 			break;
! 		case 6:
! 			sql_files[0] = process_builtin(gaussian_select_only);
! 			num_files = 1;
! 			break;
  		default:
  			break;
  	}
***************
*** 3035,3041 **** threadRun(void *arg)
  		Command   **commands = sql_files[st->use_file];
  		int			prev_ecnt = st->ecnt;
  
! 		st->use_file = getrand(thread, 0, num_files - 1);
  		if (!doCustom(thread, st, &result->conn_time, logfile, &aggs))
  			remains--;			/* I've aborted */
  
--- 3295,3302 ----
  		Command   **commands = sql_files[st->use_file];
  		int			prev_ecnt = st->ecnt;
  
! 		st->use_file = getrand(thread, 0, num_files - 1, DIST_UNIFORM, 0);
! 
  		if (!doCustom(thread, st, &result->conn_time, logfile, &aggs))
  			remains--;			/* I've aborted */
  
*** a/doc/src/sgml/pgbench.sgml
--- b/doc/src/sgml/pgbench.sgml
***************
*** 320,325 **** pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
--- 320,340 ----
       </varlistentry>
  
       <varlistentry>
+       <term><option>--gaussian</option><replaceable>standard deviation</></term>
+       <term><option>--gaussian-n</option><replaceable>standard deviation</></term>
+       <term><option>--gaussian-s</option><replaceable>standard deviation</></term>
+       <listitem>
+        <para>
+         Gaussian distribution pgbench option. Need the standard deviation threshold.
+         If we set larger standard deviation threshold, pgbench access patern limited
+         more specific records. Min standard deviation threshold is 2. If you add '-n'
+         or '-s' options at tail, you can execute gaussian distribution pgbench which
+         is like '-N' or '-S' option.
+        </para>
+       </listitem>
+      </varlistentry>
+ 
+      <varlistentry>
        <term><option>-j</option> <replaceable>threads</></term>
        <term><option>--jobs=</option><replaceable>threads</></term>
        <listitem>
***************
*** 770,775 **** pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
--- 785,812 ----
  
     <varlistentry>
      <term>
+      <literal>\setgaussian <replaceable>varname</> <replaceable>min</> <replaceable>max</> <replaceable>
+      standard deviation threshold</literal>
+     </term>
+ 
+     <listitem>
+      <para>
+       Sets variable <replaceable>varname</> to a gaussian random integer value
+       between the limits <replaceable>min</> and <replaceable>max</> inclusive.
+       Each limit can be either an integer constant or a
+       <literal>:</><replaceable>variablename</> reference to a variable
+       having an integer value. Min standard deviation threshold is 2.
+      </para>
+ 
+      <para>
+       Example:
+ <programlisting>
+ \setgaussian aid 1 :naccounts 5
+ </programlisting></para>
+     </listitem>
+    </varlistentry>
+  <varlistentry>
+     <term>
       <literal>\sleep <replaceable>number</> [ us | ms | s ]</literal>
      </term>
  
file1.sqltext/plain; charset=Shift_JIS; name=file1.sqlDownload
file2.sqltext/plain; charset=Shift_JIS; name=file2.sqlDownload
graph.pngimage/png; name=graph.pngDownload
�PNG


IHDR���*�)PLTE���������������@��  ��� �@����@����`��`��`��@��0`��`@@@@���`�``�`���`���@��`��`�`�����` �```    @@ @�`� `�``����@ � ����������  ���`����`�����`�@�@@�����`���������������������``������������� ����  � �� �  �@ �@��`��`�������@��@��`��p�����������T&�s�IDATx����� Eq��w���q$HH���vz��g�spg���f������/`: ���\�0���e��EnU�#���.���o�pm������
��W��G��`����FP�T��q���}����	�n��W��^�I-����p�\ �M����������>%�&�-����o�������>��/��`���]�s��;|���|��]��t�������=���%@����Q�����=~8?��������3����t}�����$�>�I��Ff�@�;@�Ky2S2A��Jo^�qW���{n.������O�6���������=�����\�L�reU�T����>�j=�&L��0{$�#`r ��|G����C�9�ed�@�����������#�m`��09k.`=>��>09`r� �H��qw������UP
��U��~H��_���C�F�P�������l��
p����������}�Q��p �(L�7�>`1�;��.`r ��|@���`8��l�1�&����E�����0>`r ���`}_|@�,?`&�������b[�t�a�	�09��6����>f`r ����y[l��pM�09�����������F��}+�vk��A�Xo�b������j}R�b���� l�P)��?��yB��H�C�9h	��1@��1�+0������m�/G�9@���-@t��T�s�Y���&���
(@��3�`r ��X
X��L�09��*@�i\���!�L��`@�m� L��$��
�����0
��`% ��F��@����cV���
�������`T_�S�:>$
(� �������s�<$
�b
u�~�G���r,o@�
�]�
�w�3�O����? �09��o��<�p��AP}`r ���X��+&����
P�*��;�&�D|� ���;�^|� aV��7�@�������7P}�hP1@ [�10�Q\���D���C.���o#��`8@�+�� �P}
�b-���`rx��q�W�`	���T_���@z���?�O@�*�z��[����7���?���'@ ���#b�/��G�@�P����<���@D�g�]�A^���;=#h����o�?��B��}D ���p�&����z=#�T�F���N	�^�zScT������[���P����7��3@*C�~;r�u����������3������u6��Y���,�����1po
i0���y:��!��M�=���j��|S��� �
g}B�~C�X�0���n�P��"��r�Y������#O_@�&�V�������F�p�k(O�����l������ct��#� ���CU�,�Z���B��d��������_�A�����>!�t����C�n�@��6qp�)@4��?��Gtyn���2�U!���Z����YFn�m��A�at70��~�?.�_�� ��	
g���B!�_�(b���p���M#)�(��!Q�����v�'<��[�3@l?pi�|�����L"@d���C�"�NP-���r����p=��qAf������_G�:��
���������B�Lo�����0:��
�����������(��W7�6�/
��@q4�����t~�iM��a�q����:�	�B����|z��	
�9�-�}�������lP��6��A�F��g|%��J<<�I*�,hbm70��������`�	���q��yD��'�~��+�1��` @�>/@M�	Lh�
�b�[0�p����@�4����yh���o�3B�8
���+@{D2�l�(�
#`Q��'�E��OrQ��'�v�Z��Mi�Q�Px*t���V���������0@���T���bL�HF�m���S�?�p��@�	�R�S���)?�����"@��J�f7��S�"���%��X�"����`�@Z�1�	)�,�6��zFP$��fM)4�J�j�� b�j��_~�r�3���
�4S��@�m����f����/�v70�HJ�j��=���A�O^�#��N\3��E����� ?��4�shcI�pI�T2��P��f`,	�3@�U�)�@m��8����}��4�U8|Mad2l	�����i����<
@����!@�> �.������b*"t������
v����\����shx�C�N}�M �e��u�FW�%�����M��j���J����������_��nX�3T8T�D�},���!@�>7 �:�H%������HB,��.�������@@t�s�cH�`�8����Fa���x,����U�A^�[������^�U�+�����@7�
�oq��� @
 ut�bi���p�tZ�]��P)�����b��yE��s��7�W0�{�!���'E��
���@az�]+�B�����wf�s[p3@�6�f�f}�*`F�p�x��1��Ai~J�#�b�m�5pa����dW��u���!@�>S)c-�<z
�T�
�{p�O�s��v�f���}�_[~�_�A����0"@a�$	f�MA��X���,I����o��V#�;~?`�!@fT0B��y��U��
{h��R����5t����������E��C�1���0!@<K���0Q��,����rJ������������C������Z�g$G�K����.!@Io�� ���)M*k�n����B������@��r`�>!@-,Lx�����v����X@�)�\�o�����^.P��}����zl;���G������Px>���������o]�
&Wa��PxH��~{�(+�j%���^�I���ib��C���;p[4�p���$�KAr��y�D���
(=#�U}F�8%��&��T��@���J ���*@������'F�{Hy$e��Cd�A�r~l�?[�6�K��-OI���$����'�Y�T�zyuE�Hq,�qjm�,�S����B'8�J�����Z�!���O�\���+���H���j`N�~~��3M�u������r}H4����3��VGgo��EVm��7�N��[���2~0������@u @&Bd��(IQw(}g����b`�@�������Q���%�`���S���e9:��
e�^Q�0��-��O^�����ko�	�F���u�����$�
k�Y�uM��4�����r���s�~��8�����x89�5�����wjG\��6�H�+�Ah���W��z�mKYX�LVe��A��_!N�������F2E�6�^!��w��y�q����mXx��f�})�8�E�!�/�#J��	�����g���@��{/����!@U��FP���(0�U�0;j+�n�qH���<�0���������=����aV���`?*xykh�l�b��� V���aV���?Q���.3�Y���g&��}U��"����~	-An����=�{V87����9���s+Eb�6��=��'C���=�=�{^v��/�|G��������K�9�=w%�[0�O�w���n������EF���0��+����n8UX����}�E�>������?7�����p33q$���9�`��y���f�8�.9c��S��r��	������J*>�?+("�5u?IEND�B`�
#16Fabien COELHO
coelho@cri.ensmp.fr
In reply to: KONDO Mitsumasa (#15)
Re: gaussian distribution pgbench

Hello,

I revise my gaussian pgbench patch which wss requested from community.

With a lot of delay for which I apologise, please find hereafter the
review.

Gaussian Pgbench v3 patch by Mitsumasa KONDO review

* The purpose of the patch is to allow a pgbench script to draw from normally
distributed integer values instead of uniformly distributed.

This is a valuable contribution to enable pgbench to generate more realistic
loads, which is seldom uniform in practice.

However, ISTM that other distributions such an exponantial one would make
more sense, and also the values should be further randomized so that
neighboring values are not more likely to be drawn. The latest point is non
trivial.

* Compilation

The patch applies and compiles against current head. It works as expected,
although there is few feedback from the script to show that.

* Mathematical soundness

We want to derive a discrete normal distribution from a uniform one.
Well, normal distributions are for continuous variables... Anyway, this is
done by computing a continuous normal distribution which is then projected
onto integers. I'm basically fine with that.

The system uses a Box-Muller transform (1958) to do this transformation.
The Ziggurat method seems to be prefered for this purpose, *but* it would
require precalculated tables which depends on the target values. So I'm
fine with the Box-Muller transform for pgbench.

The BM method uses 2 uniformly distributed numbers to derive 2 normally
distributed numbers. The implementation computes one of these, and loops
over till one match a threshold criterion.

More explanations, at least in comments, are needed about this threshold
and its meaning. It is required to be more than 2. I guess is that it allows
to limit the number of iterations of the while loop, but in what proportion
is unclear. The documentation does not also help the user to understand
this value and its meaning.

What I think it is: it is the deviation for the FURTHEST point around the
mean, that is the actual deviation associated to the "min" and "max" target
values. The 2 minimum value induces that there is a least 4 stddev lengths
between min & max, with the most likely mean in the middle.

If the threshold test fails, one of the 2 uniform number is redrawn, a new
candidate value is tested. I'm not at ease about why only 1 value is redrawn
and not both, some explanations would be welcome. Also, on the other hand,
why not test the other possible value (with cos) if the first one fails?

Also, as suggested above, I would like some explanations about how much this
while loop may iterate without success, say with the expected average number
of iterations with its explanation in a comment.

* Implementation

Random values :
double rand1 = 1.0 - rand; // instead of the LONG_MAX computation & limits.h
rand2 should be in (0, 1], but it is in [0, 1), use "1.0 - ..." as well?!

What is called "stdev*" in getrand() is really the chosen deviation from
the target mean, so it would make more sense to name it "dev".

I do not think that the getrand refactoring was such a good idea. I'm sorry
if I may have suggested that in a previous comment.
The new getrand possibly ignores its parameters, hmmmm. ISTM that it would
be much simpler in the code to have a separate and clean "getrand_normal"
or "getrand_gauss" called for "\setgaussian", and that's it. This would
allow to get rid of DistType and all of getrand changes in the code.

There are heavy constants computations (sqrt(log()) within the while
loop which would be moved out of the loop.

ISTM that the while condition would be easier to read as:

while ( dev < - threshold || threshold < dev )

Maybe the \\setgaussian argument handling may be transformed into a function,
so that it could be used easily later for some other distribution (say some
setexp:-)

* Options

ISTM that the test options would be better if made orthogonal, i.e. not to
have three --gaussian* options. I would suggest to have only one
--gaussian=NUM which would trigger gaussian tests with this threshold,
and --gaussian=3.5 --select-only would use the select-only variant,
and so on.

* Typos

gausian -> gaussian
patern -> pattern

* Conclusion :

- this is a valuable patch to help create more realistic load and make pgbench
a more useful tool. I'm greatly in favor of having such a functionality.

- it seems to me that the patch should be further improved before being
committed, in particular I would suggest:

(1) improve the explanations in the code and in the documentation, especially
about what is the "deviation threshold" and its precise link to generated
values.

(2) simplify the code with a separate gaussian getrand, and simpler or
more efficient code here and there, see comments above.

(3) use only one option to trigger gaussian tests.

(bonus) \setexp would be a nice:-)

--
Fabien.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#17KONDO Mitsumasa
kondo.mitsumasa@lab.ntt.co.jp
In reply to: Fabien COELHO (#16)
1 attachment(s)
Re: gaussian distribution pgbench

Hi Febien,

Thank you very much for your very detail and useful comments!
I read your comment, I agree most of your advice:)

Attached patch is fixed for your comment. That are...
- Remove redundant long-option.
- We can use "--gaussian=NUM -S" or "--gaussian=NUMN -N" options.
- Add sentence in document
- Separate two random generate function which are uniform and gaussian.
- getGaussianrand() is created.
- Fix ranged random number more strictly, ex. (0,1) or [0,1).
- Please see comment of source code in detail:).
- Fix typo.
- Use cos() and sin() function when we generate gaussian random number.
- Add fast sqrt calculation algorithm.
- Reuse sqrt result and pre generate random number for reducing calculation cost.
- Experience of this method is under following. It will be little-bit faster
than non-reuse method. And distribution of gaussian is still good.

* Settings
shared_buffers = 1024MB

* Test script
pgbench -i -s 1
pgbench --gaussian=2 -T 30 -S -c8 -j4 -n
pgbench --gaussian=2 -T 30 -S -c8 -j4 -n
pgbench --gaussian=2 -T 30 -S -c8 -j4 -n

* Result
method | try1 | try2 | try3 |
--------------------------------------------|
reuse method | 44189 | 44453 | 44013 |
non-reuse method | 43567 | 43635 | 43508 |

(2014/02/09 21:32), Fabien COELHO wrote:

This is a valuable contribution to enable pgbench to generate more realistic
loads, which is seldom uniform in practice.

Thanks!

However, ISTM that other distributions such an exponantial one would make
more sense,

I can easy to create exponential distribution. Here, I assume exponential
distribution that is f(x) = lambda * exp^(-lambda * x) in general.
What do you think under following interface?

custom script: \setexp [varname] min max threshold
command : --exponential=NUM(threshold)

I don't want to use lambda variable for simple implementation. So lambda is
always 1. Because it can enough to control distribution by threshold. Threshold
parameter is f(x) value. And using created distribution projects to 'aid' by same
method. If you think OK, I will impliment under followings tomorrow, and also
create parseing part of this function...

do
{
rand = 1.0 - pg_erand48(thread->random_state);
rand = -log(rand);
}while( rand > exp_threshold)

return rand / exp_threshold;

and also the values should be further randomized so that
neighboring values are not more likely to be drawn. The latest point is non
trivial.

That's right, but I worry about gaussian randomness and benchmark reproducibility
might be disappeared when we re-randomized access pattern, because Postgres
storage method manages records by each pages and it is difficult to realize
access randomness in whole pages, not record. If we solve this problem, we have
to need algorithm for smart shuffule projection function that is still having
gaussian randomized. I think it will be difficult, and it have to impement in
another patch in the future.

* Mathematical soundness

We want to derive a discrete normal distribution from a uniform one.
Well, normal distributions are for continuous variables... Anyway, this is
done by computing a continuous normal distribution which is then projected
onto integers. I'm basically fine with that.

The system uses a Box-Muller transform (1958) to do this transformation.
The Ziggurat method seems to be prefered for this purpose, *but* it would
require precalculated tables which depends on the target values. So I'm
fine with the Box-Muller transform for pgbench.

Yes, that's right. I selected simple and relatively faster algorithm, that is
Box-Muller transform.

The BM method uses 2 uniformly distributed numbers to derive 2 normally
distributed numbers. The implementation computes one of these, and loops
over till one match a threshold criterion.

More explanations, at least in comments, are needed about this threshold
and its meaning. It is required to be more than 2. I guess is that it allows
to limit the number of iterations of the while loop,

Yes. This loop could not almost go on, because min stdev_threshold is 2.
The possibility of retry-loop is under 4 percent. It might not be problem.

but in what proportion
is unclear. The documentation does not also help the user to understand
this value and its meaning.

Yes, it is huristic method. So I added the comments in document.

What I think it is: it is the deviation for the FURTHEST point around the
mean, that is the actual deviation associated to the "min" and "max" target
values. The 2 minimum value induces that there is a least 4 stddev lengths
between min & max, with the most likely mean in the middle.

Correct!

If the threshold test fails, one of the 2 uniform number is redrawn, a new
candidate value is tested. I'm not at ease about why only 1 value is redrawn
and not both, some explanations would be welcome. Also, on the other hand,
why not test the other possible value (with cos) if the first one fails?

Yes, I think so too. So I fixed this partan and it will be better. Past
implementations are not good:(

Also, as suggested above, I would like some explanations about how much this
while loop may iterate without success, say with the expected average number
of iterations with its explanation in a comment.

I add my comments in source code.

* Implementation

Random values :
double rand1 = 1.0 - rand; // instead of the LONG_MAX computation & limits.h
rand2 should be in (0, 1], but it is in [0, 1), use "1.0 - ..." as well?!

It's more smart method. I change to this method.

What is called "stdev*" in getrand() is really the chosen deviation from
the target mean, so it would make more sense to name it "dev".

Hmm, I like stdev*. Short variable makes us more confuse:( And it's not big problem.

I do not think that the getrand refactoring was such a good idea. I'm sorry
if I may have suggested that in a previous comment.
The new getrand possibly ignores its parameters, hmmmm. ISTM that it would
be much simpler in the code to have a separate and clean "getrand_normal"
or "getrand_gauss" called for "\setgaussian", and that's it. This would
allow to get rid of DistType and all of getrand changes in the code.

I separate two function that are getrand() and getGaussianrand(), it becomes more
clear I think.

There are heavy constants computations (sqrt(log()) within the while
loop which would be moved out of the loop.

ISTM that the while condition would be easier to read as:

while ( dev < - threshold || threshold < dev )

OK, fixed.

Maybe the \\setgaussian argument handling may be transformed into a function,
so that it could be used easily later for some other distribution (say some
setexp:-)

* Options

ISTM that the test options would be better if made orthogonal, i.e. not to
have three --gaussian* options. I would suggest to have only one
--gaussian=NUM which would trigger gaussian tests with this threshold,
and --gaussian=3.5 --select-only would use the select-only variant,
and so on.

Agreed. Fixed.

* Typos

gausian -> gaussian
patern -> pattern

Oh, fixed.

* Conclusion :

- this is a valuable patch to help create more realistic load and make pgbench
a more useful tool. I'm greatly in favor of having such a functionality.

- it seems to me that the patch should be further improved before being
committed, in particular I would suggest:

(1) improve the explanations in the code and in the documentation, especially
about what is the "deviation threshold" and its precise link to generated
values.

(2) simplify the code with a separate gaussian getrand, and simpler or
more efficient code here and there, see comments above.

(3) use only one option to trigger gaussian tests.

(bonus) \setexp would be a nice:-)

Thank you for your comments. They make my patch more polished:)
I think my patch is fixed for supporting all your comments, but it might not be fixed
as you think. And if you notice other part, please send me.

Regards,
--
Mitsumasa KONDO
NTT Open Source Software Center

Attachments:

gaussian_pgbench_v5.patchtext/x-diff; name=gaussian_pgbench_v5.patchDownload
*** a/contrib/pgbench/pgbench.c
--- b/contrib/pgbench/pgbench.c
***************
*** 176,181 **** int			progress_nthreads = 0; /* number of threads for progress report */
--- 176,183 ----
  bool		is_connect;			/* establish connection for each transaction */
  bool		is_latencies;		/* report per-command latencies */
  int			main_pid;			/* main process id used in log filename */
+ double		stdev_threshold = 5;		/* standard deviation threshold */
+ bool		gaussian_option = false;	/* use gaussian distribution random generator */
  
  char	   *pghost = "";
  char	   *pgport = "";
***************
*** 338,346 **** static char *select_only = {
--- 340,390 ----
  	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
  };
  
+ /* --gaussian case */
+ static char *gaussian_tpc_b = {
+ 	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+ 	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setgaussian aid 1 :naccounts :stdev_threshold\n"
+ 	"\\setrandom bid 1 :nbranches\n"
+ 	"\\setrandom tid 1 :ntellers\n"
+ 	"\\setrandom delta -5000 5000\n"
+ 	"BEGIN;\n"
+ 	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ 	"UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;\n"
+ 	"UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;\n"
+ 	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+ 	"END;\n"
+ };
+ 
+ /* --gaussian with -N case */
+ static char *gaussian_simple_update = {
+ 	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+ 	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setgaussian aid 1 :naccounts :stdev_threshold\n"
+ 	"\\setrandom bid 1 :nbranches\n"
+ 	"\\setrandom tid 1 :ntellers\n"
+ 	"\\setrandom delta -5000 5000\n"
+ 	"BEGIN;\n"
+ 	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ 	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+ 	"END;\n"
+ };
+ 
+ /* --gaussian with -S case */
+ static char *gaussian_select_only = {
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setgaussian aid 1 :naccounts :stdev_threshold\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ };
+ 
  /* Function prototypes */
  static void setalarm(int seconds);
  static void *threadRun(void *arg);
+ static inline double sqrtd(const double x);
  
  static void
  usage(void)
***************
*** 381,386 **** usage(void)
--- 425,431 ----
  		   "  -v, --vacuum-all         vacuum all four standard tables before tests\n"
  		   "  --aggregate-interval=NUM aggregate data over NUM seconds\n"
  		   "  --sampling-rate=NUM      fraction of transactions to log (e.g. 0.01 for 1%%)\n"
+ 		   "  --gaussian=NUM           gaussian distribution with NUM standard deviation threshold\n"
  		   "\nCommon options:\n"
  		   "  -d, --debug              print debugging output\n"
  		   "  -h, --host=HOSTNAME      database server host or socket directory\n"
***************
*** 477,482 **** getrand(TState *thread, int64 min, int64 max)
--- 522,597 ----
  	return min + (int64) ((max - min + 1) * pg_erand48(thread->random_state));
  }
  
+ /* random number generator: gaussian distribution from min to max inclusive */
+ static int64
+ getGaussianrand(TState *thread, int64 min, int64 max, double stdev_threshold)
+ {
+ 	double		stdev;
+ 	double		rand;
+ 	static double	rand1;
+ 	static double	rand2;
+ 	static double	var_sqrt;
+ 	static bool	reuse = false;
+ 	
+ 	/*
+ 	 * Get user specified random number(-stdev_threshold < stdev <= stdev_threshold) 
+ 	 * in this loop. This loop is executed until appeared ranged number we want.
+ 	 * However, this loop could not almost go on, because min stdev_threshold is 2
+ 	 * then the possibility of retry-loop is under 4 percent. And possibility of
+ 	 * re-retry-loop is under 1.6 percent. And it doesn't happen frequentry even if
+ 	 * we also think about the cycle of the trigonometric function.
+  	 */
+ 	do
+ 	{
+ 		/* reuse pre calculation result as possible */
+ 		if(!reuse)
+ 		{
+ 			/* 
+  			 * pg_erand48 generates [0,1) random number. However rand1 
+  			 * needs (0,1) random number because log(0) cannot calculate.
+  			 * And rand2 also needs (0,1) random number in strictly. But
+  			 * normalization cost is high and we can substitute (0,1] at
+  			 * rand1 and [0,1) at rand2, so we use approximate calculation.
+  			 */
+ 			rand1 = 1.0 - pg_erand48(thread->random_state);
+ 			rand2 = pg_erand48(thread->random_state);
+ 		
+ 			 /* Box-Muller transform */
+ 			var_sqrt = sqrtd(-2.0 * log(rand1));
+ 			stdev = var_sqrt * sin(2.0 * M_PI * rand2);
+ 			reuse = true;
+ 		}
+ 		else
+ 		{
+ 			stdev = var_sqrt * cos(2.0 * M_PI * rand2);
+ 			reuse = false;
+ 		}		
+ 	} while (stdev < -stdev_threshold || stdev >= stdev_threshold);
+ 
+ 	/* normalization to [0,1) */
+ 	rand = (stdev + stdev_threshold) / (stdev_threshold * 2.0);
+ 
+ 	/* return int64 random number within between min and max */
+ 	return min + (int64) (max - min + 1) * rand;
+ }
+ 
+ /*
+  * fast sqrt algorithm: reference from Fast inverse square root algorithms.
+  */ 
+ static inline double
+ sqrtd(const double x)
+ {
+ 	double		x_half = 0.5 * x;
+ 	long long int	tmp = 0x5FE6EB50C7B537AAl - ( *(long long int*)&x >> 1);
+ 	double		x_result = *(double*)&tmp;
+ 
+ 	x_result *= (1.5 - (x_half * x_result * x_result));
+ 	/* retry this calculation, it becomes higher precision at sqrt */
+ 	x_result *= (1.5 - (x_half * x_result * x_result));
+ 
+ 	return x_result * x;
+ }
+ 
  /* call PQexec() and exit() on failure */
  static void
  executeStatement(PGconn *con, const char *sql)
***************
*** 1391,1396 **** top:
--- 1506,1601 ----
  
  			st->listen = 1;
  		}
+ 		else if (pg_strcasecmp(argv[0], "setgaussian") == 0)
+ 		{
+ 			char	*var;
+ 			char	*endptr;
+ 			int64	min;
+ 			int64	max;
+ 			double	stdev_threshold;
+ 			char	res[64];
+ 
+ 			if (*argv[2] == ':')
+ 			{
+ 				if((var = getVariable(st, argv[2] + 1)) == NULL)
+ 				{
+ 					fprintf(stderr, "%s: undefined variable %s\n", argv[0], argv[2]);
+ 					st->ecnt++;
+ 					return true;
+ 				}
+ 				min = strtoint64(var);
+ 			}
+ 			else
+ 				min = strtoint64(argv[2]);
+ #ifdef NOT_USED
+ 			if (min < 0)
+ 			{
+ 				fprintf(stderr, "%s: invalid minimum number %d\n", argv[0], min);
+ 				st->ecnt++;
+ 				return;
+ 			}
+ #endif
+ 			if (*argv[3] == ':')
+ 			{
+ 				if((var = getVariable(st, argv[3] + 1)) == NULL)
+ 				{
+ 					fprintf(stderr, "%s: invalid maximum number %s\n", argv[0], argv[3]);
+ 					st->ecnt++;
+ 					return true;
+ 				}
+ 				max = strtoint64(var);
+ 			}
+ 			else
+ 				max = strtoint64(argv[3]);
+ 
+ 			/* check if min and max are appropriate value */
+ 			if(max < min)
+ 			{
+ 				fprintf(stderr, "%s: maximum is less than minimum\n", argv[0]);
+ 				st->ecnt++;
+ 				return true;
+ 			}
+ 
+ 			/* for not overflowing when generating random number */
+ 			if(max - min < 0 || (max - min) + 1 < 0)
+ 			{
+ 				fprintf(stderr, "%s: range too large\n", argv[0]);
+ 				st->ecnt++;
+ 				return true;
+ 			}
+ 
+ 			if(*argv[4] == ':')
+ 			{
+ 				if((var = getVariable(st, argv[4] + 1)) == NULL)
+ 				{
+ 					fprintf(stderr, "%s: invalid gaussian threshold number %s\n", argv[0], argv[4]);
+ 					st->ecnt++;
+ 					return true;
+ 				}
+ 				stdev_threshold = strtod(var, NULL);
+ 			}
+ 			else
+ 				stdev_threshold = strtod(argv[4], &endptr);
+ 
+ 			if ( stdev_threshold < 2)
+ 			{
+ 				fprintf(stderr, "%s: gaussian threshold must be more than 2\n,", argv[4]);
+ 				st->ecnt++;
+ 				return true;
+ 			}
+ #ifdef DEBUG
+ 			printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getGaussianrand(thread, min, max, stdev_threshold));
+ #endif
+ 			snprintf(res, sizeof(res), INT64_FORMAT, getGaussianrand(thread, min, max, stdev_threshold));
+ 
+ 			if(!putVariable(st, argv[0], argv[1], res))
+ 			{
+ 				st->ecnt++;
+ 				return true;
+ 			}
+ 
+ 			st->listen = 1;
+ 		}
  		else if (pg_strcasecmp(argv[0], "set") == 0)
  		{
  			char	   *var;
***************
*** 1915,1920 **** process_commands(char *buf)
--- 2120,2137 ----
  				fprintf(stderr, "%s: extra argument \"%s\" ignored\n",
  						my_commands->argv[0], my_commands->argv[j]);
  		}
+ 		else if (pg_strcasecmp(my_commands->argv[0], "setgaussian") == 0)
+ 		{
+ 			if (my_commands->argc < 5)
+ 			{
+ 				fprintf(stderr, "%s: missing argument\n", my_commands->argv[0]);
+ 				exit(1);
+ 			}
+ 
+ 			for (j = 5; j < my_commands->argc; j++)
+ 				fprintf(stderr, "%s: extra argument \"%s\" ignored\n",
+ 						my_commands->argv[0], my_commands->argv[j]);
+ 		}
  		else if (pg_strcasecmp(my_commands->argv[0], "set") == 0)
  		{
  			if (my_commands->argc < 3)
***************
*** 2188,2203 **** printResults(int ttype, int normal_xacts, int nclients,
  						(INSTR_TIME_GET_DOUBLE(conn_total_time) / nthreads));
  
  	if (ttype == 0)
! 		s = "TPC-B (sort of)";
  	else if (ttype == 2)
! 		s = "Update only pgbench_accounts";
  	else if (ttype == 1)
! 		s = "SELECT only";
  	else
  		s = "Custom query";
  
  	printf("transaction type: %s\n", s);
  	printf("scaling factor: %d\n", scale);
  	printf("query mode: %s\n", QUERYMODE[querymode]);
  	printf("number of clients: %d\n", nclients);
  	printf("number of threads: %d\n", nthreads);
--- 2405,2447 ----
  						(INSTR_TIME_GET_DOUBLE(conn_total_time) / nthreads));
  
  	if (ttype == 0)
! 	{
! 		if(gaussian_option)
! 			s = "TPC-B (sort of)";
! 		else
! 			s = "Gaussian distributed TPC-B (sort of)";
! 	}
  	else if (ttype == 2)
! 	{
! 		if(gaussian_option)
! 			s = "Gaussian distributed update only pgbench_accounts";
! 		else
! 			s = "Update only pgbench_accounts";
! 	}
  	else if (ttype == 1)
! 	{
! 		if(gaussian_option)
! 			s = "Gaussian distributed SELECT only";
! 		else
! 			s = "SELECT only";
! 	}
  	else
  		s = "Custom query";
  
  	printf("transaction type: %s\n", s);
  	printf("scaling factor: %d\n", scale);
+ 
+ 	/* output in only gaussian distributed benchmark */
+ 	if(gaussian_option)
+ 	{
+ 		printf("standard deviation threshold: %.5f\n", stdev_threshold);
+ 		printf("access probability of top 20%%, 10%% and 5%% records: %.5f %.5f %.5f\n",
+ 			(double) ((erf (stdev_threshold * 0.2 / sqrt(2.0))) / (erf (stdev_threshold / sqrt(2.0)))),
+ 			(double) ((erf (stdev_threshold * 0.1 / sqrt(2.0))) / (erf (stdev_threshold / sqrt(2.0)))),
+ 			(double) ((erf (stdev_threshold * 0.05 / sqrt(2.0))) / (erf (stdev_threshold / sqrt(2.0))))
+ 			);
+ 	}
+ 
  	printf("query mode: %s\n", QUERYMODE[querymode]);
  	printf("number of clients: %d\n", nclients);
  	printf("number of threads: %d\n", nthreads);
***************
*** 2327,2332 **** main(int argc, char **argv)
--- 2571,2577 ----
  		{"unlogged-tables", no_argument, &unlogged_tables, 1},
  		{"sampling-rate", required_argument, NULL, 4},
  		{"aggregate-interval", required_argument, NULL, 5},
+ 		{"gaussian", required_argument, NULL, 6},
  		{"rate", required_argument, NULL, 'R'},
  		{NULL, 0, NULL, 0}
  	};
***************
*** 2606,2611 **** main(int argc, char **argv)
--- 2851,2865 ----
  				}
  #endif
  				break;
+ 			case 6:
+ 				gaussian_option = true;
+ 				stdev_threshold = atof(optarg);
+ 				if(stdev_threshold < 2)
+ 				{
+ 					fprintf(stderr, "--gaussian=NUM must be more than 2: %f\n", stdev_threshold);
+ 					exit(1);
+ 				}
+ 				break;
  			default:
  				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
  				exit(1);
***************
*** 2803,2808 **** main(int argc, char **argv)
--- 3057,3073 ----
  		}
  	}
  
+ 	/* set :stdev_threshold variable */
+ 	if(getVariable(&state[0], "stdev_threshold") == NULL)
+ 	{
+ 		snprintf(val, sizeof(val), "%lf", stdev_threshold);
+ 		for (i = 0; i < nclients; i++)
+ 		{
+ 			if (!putVariable(&state[i], "startup", "stdev_threshold", val))
+ 				exit(1);
+ 		}
+ 	}
+ 
  	if (!is_no_vacuum)
  	{
  		fprintf(stderr, "starting vacuum...");
***************
*** 2828,2844 **** main(int argc, char **argv)
  	switch (ttype)
  	{
  		case 0:
! 			sql_files[0] = process_builtin(tpc_b);
  			num_files = 1;
  			break;
  
  		case 1:
! 			sql_files[0] = process_builtin(select_only);
  			num_files = 1;
  			break;
  
  		case 2:
! 			sql_files[0] = process_builtin(simple_update);
  			num_files = 1;
  			break;
  
--- 3093,3118 ----
  	switch (ttype)
  	{
  		case 0:
! 			if(gaussian_option)
! 				sql_files[0] = process_builtin(gaussian_tpc_b);
! 			else
! 				sql_files[0] = process_builtin(tpc_b);
  			num_files = 1;
  			break;
  
  		case 1:
! 			if(gaussian_option)
! 				sql_files[0] = process_builtin(gaussian_select_only);
! 			else
! 				sql_files[0] = process_builtin(select_only);
  			num_files = 1;
  			break;
  
  		case 2:
! 			if(gaussian_option)
! 				sql_files[0] = process_builtin(simple_update);
! 			else
! 				sql_files[0] = process_builtin(gaussian_simple_update);
  			num_files = 1;
  			break;
  
*** a/doc/src/sgml/pgbench.sgml
--- b/doc/src/sgml/pgbench.sgml
***************
*** 320,325 **** pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
--- 320,342 ----
       </varlistentry>
  
       <varlistentry>
+       <term><option>--gaussian</option><replaceable>standard deviation</></term>
+       <listitem>
+        <para>
+         Gaussian distribution pgbench option. Need the standard deviation threshold.
+         Standard deviation threshold can control distribution of access patern that
+         is used by aid in pgbench_accounts table. If we set larger standard deviation
+         threshold, pgbench access patern limited more specific records. On the other
+         hands, if you set smaller standard deviation, pgbench access patern will be
+         more gently distribution. Standard deviation threshold must be higher than 2.
+         This rule is needed for realizing realistic calculation costs. If you add 
+         '-N' or '-S' options, you can execute gaussian distribution pgbench in these
+         benchmarks.
+        </para>
+       </listitem>
+      </varlistentry>
+ 
+      <varlistentry>
        <term><option>-j</option> <replaceable>threads</></term>
        <term><option>--jobs=</option><replaceable>threads</></term>
        <listitem>
***************
*** 770,775 **** pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
--- 787,818 ----
  
     <varlistentry>
      <term>
+      <literal>\setgaussian <replaceable>varname</> <replaceable>min</> <replaceable>max</> <replaceable>
+      standard deviation threshold</literal>
+     </term>
+ 
+     <listitem>
+      <para>
+       Sets variable <replaceable>varname</> to a gaussian random integer value
+       between the limits <replaceable>min</> and <replaceable>max</> inclusive.
+       Each limit can be either an integer constant or a
+       <literal>:</><replaceable>variablename</> reference to a variable
+       having an integer value. Standard deviation threshold controls
+       distribution of access patern. If we set larger value in standard
+       deviation threshold, more frequentry access patern will be more 
+       limited ranges. Min standard deviation threshold is 2. This rule 
+       needs for realizing realistic calculation costs.
+      </para>
+ 
+      <para>
+       Example:
+ <programlisting>
+ \setgaussian aid 1 :naccounts 5
+ </programlisting></para>
+     </listitem>
+    </varlistentry>
+  <varlistentry>
+     <term>
       <literal>\sleep <replaceable>number</> [ us | ms | s ]</literal>
      </term>
  
#18KONDO Mitsumasa
kondo.mitsumasa@lab.ntt.co.jp
In reply to: KONDO Mitsumasa (#17)
1 attachment(s)
Re: gaussian distribution pgbench

Sorry, previos attached patch has small bug.
Please use latest one.

134 - return min + (int64) (max - min + 1) * rand;
134 + return min + (int64)((max - min + 1) * rand);

Regards,
--
Mitsumasa KONDO
NTT Open Source Software Center

Attachments:

gaussian_pgbench_v5-1.patchtext/x-diff; name=gaussian_pgbench_v5-1.patchDownload
*** a/contrib/pgbench/pgbench.c
--- b/contrib/pgbench/pgbench.c
***************
*** 176,181 **** int			progress_nthreads = 0; /* number of threads for progress report */
--- 176,183 ----
  bool		is_connect;			/* establish connection for each transaction */
  bool		is_latencies;		/* report per-command latencies */
  int			main_pid;			/* main process id used in log filename */
+ double		stdev_threshold = 5;		/* standard deviation threshold */
+ bool		gaussian_option = false;	/* use gaussian distribution random generator */
  
  char	   *pghost = "";
  char	   *pgport = "";
***************
*** 338,346 **** static char *select_only = {
--- 340,390 ----
  	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
  };
  
+ /* --gaussian case */
+ static char *gaussian_tpc_b = {
+ 	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+ 	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setgaussian aid 1 :naccounts :stdev_threshold\n"
+ 	"\\setrandom bid 1 :nbranches\n"
+ 	"\\setrandom tid 1 :ntellers\n"
+ 	"\\setrandom delta -5000 5000\n"
+ 	"BEGIN;\n"
+ 	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ 	"UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;\n"
+ 	"UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;\n"
+ 	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+ 	"END;\n"
+ };
+ 
+ /* --gaussian with -N case */
+ static char *gaussian_simple_update = {
+ 	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+ 	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setgaussian aid 1 :naccounts :stdev_threshold\n"
+ 	"\\setrandom bid 1 :nbranches\n"
+ 	"\\setrandom tid 1 :ntellers\n"
+ 	"\\setrandom delta -5000 5000\n"
+ 	"BEGIN;\n"
+ 	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ 	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+ 	"END;\n"
+ };
+ 
+ /* --gaussian with -S case */
+ static char *gaussian_select_only = {
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setgaussian aid 1 :naccounts :stdev_threshold\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ };
+ 
  /* Function prototypes */
  static void setalarm(int seconds);
  static void *threadRun(void *arg);
+ static inline double sqrtd(const double x);
  
  static void
  usage(void)
***************
*** 381,386 **** usage(void)
--- 425,431 ----
  		   "  -v, --vacuum-all         vacuum all four standard tables before tests\n"
  		   "  --aggregate-interval=NUM aggregate data over NUM seconds\n"
  		   "  --sampling-rate=NUM      fraction of transactions to log (e.g. 0.01 for 1%%)\n"
+ 		   "  --gaussian=NUM           gaussian distribution with NUM standard deviation threshold\n"
  		   "\nCommon options:\n"
  		   "  -d, --debug              print debugging output\n"
  		   "  -h, --host=HOSTNAME      database server host or socket directory\n"
***************
*** 477,482 **** getrand(TState *thread, int64 min, int64 max)
--- 522,597 ----
  	return min + (int64) ((max - min + 1) * pg_erand48(thread->random_state));
  }
  
+ /* random number generator: gaussian distribution from min to max inclusive */
+ static int64
+ getGaussianrand(TState *thread, int64 min, int64 max, double stdev_threshold)
+ {
+ 	double		stdev;
+ 	double		rand;
+ 	static double	rand1;
+ 	static double	rand2;
+ 	static double	var_sqrt;
+ 	static bool	reuse = false;
+ 	
+ 	/*
+ 	 * Get user specified random number(-stdev_threshold < stdev <= stdev_threshold) 
+ 	 * in this loop. This loop is executed until appeared ranged number we want.
+ 	 * However, this loop could not almost go on, because min stdev_threshold is 2
+ 	 * then the possibility of retry-loop is under 4 percent. And possibility of
+ 	 * re-retry-loop is under 1.6 percent. And it doesn't happen frequentry even if
+ 	 * we also think about the cycle of the trigonometric function.
+  	 */
+ 	do
+ 	{
+ 		/* reuse pre calculation result as possible */
+ 		if(!reuse)
+ 		{
+ 			/* 
+  			 * pg_erand48 generates [0,1) random number. However rand1 
+  			 * needs (0,1) random number because log(0) cannot calculate.
+  			 * And rand2 also needs (0,1) random number in strictly. But
+  			 * normalization cost is high and we can substitute (0,1] at
+  			 * rand1 and [0,1) at rand2, so we use approximate calculation.
+  			 */
+ 			rand1 = 1.0 - pg_erand48(thread->random_state);
+ 			rand2 = pg_erand48(thread->random_state);
+ 		
+ 			 /* Box-Muller transform */
+ 			var_sqrt = sqrtd(-2.0 * log(rand1));
+ 			stdev = var_sqrt * sin(2.0 * M_PI * rand2);
+ 			reuse = true;
+ 		}
+ 		else
+ 		{
+ 			stdev = var_sqrt * cos(2.0 * M_PI * rand2);
+ 			reuse = false;
+ 		}		
+ 	} while (stdev < -stdev_threshold || stdev >= stdev_threshold);
+ 
+ 	/* normalization to [0,1) */
+ 	rand = (stdev + stdev_threshold) / (stdev_threshold * 2.0);
+ 
+ 	/* return int64 random number within between min and max */
+ 	return min + (int64)((max - min + 1) * rand);
+ }
+ 
+ /*
+  * fast sqrt algorithm: reference from Fast inverse square root algorithms.
+  */ 
+ static inline double
+ sqrtd(const double x)
+ {
+ 	double		x_half = 0.5 * x;
+ 	long long int	tmp = 0x5FE6EB50C7B537AAl - ( *(long long int*)&x >> 1);
+ 	double		x_result = *(double*)&tmp;
+ 
+ 	x_result *= (1.5 - (x_half * x_result * x_result));
+ 	/* retry this calculation, it becomes higher precision at sqrt */
+ 	x_result *= (1.5 - (x_half * x_result * x_result));
+ 
+ 	return x_result * x;
+ }
+ 
  /* call PQexec() and exit() on failure */
  static void
  executeStatement(PGconn *con, const char *sql)
***************
*** 1391,1396 **** top:
--- 1506,1601 ----
  
  			st->listen = 1;
  		}
+ 		else if (pg_strcasecmp(argv[0], "setgaussian") == 0)
+ 		{
+ 			char	*var;
+ 			char	*endptr;
+ 			int64	min;
+ 			int64	max;
+ 			double	stdev_threshold;
+ 			char	res[64];
+ 
+ 			if (*argv[2] == ':')
+ 			{
+ 				if((var = getVariable(st, argv[2] + 1)) == NULL)
+ 				{
+ 					fprintf(stderr, "%s: undefined variable %s\n", argv[0], argv[2]);
+ 					st->ecnt++;
+ 					return true;
+ 				}
+ 				min = strtoint64(var);
+ 			}
+ 			else
+ 				min = strtoint64(argv[2]);
+ #ifdef NOT_USED
+ 			if (min < 0)
+ 			{
+ 				fprintf(stderr, "%s: invalid minimum number %d\n", argv[0], min);
+ 				st->ecnt++;
+ 				return;
+ 			}
+ #endif
+ 			if (*argv[3] == ':')
+ 			{
+ 				if((var = getVariable(st, argv[3] + 1)) == NULL)
+ 				{
+ 					fprintf(stderr, "%s: invalid maximum number %s\n", argv[0], argv[3]);
+ 					st->ecnt++;
+ 					return true;
+ 				}
+ 				max = strtoint64(var);
+ 			}
+ 			else
+ 				max = strtoint64(argv[3]);
+ 
+ 			/* check if min and max are appropriate value */
+ 			if(max < min)
+ 			{
+ 				fprintf(stderr, "%s: maximum is less than minimum\n", argv[0]);
+ 				st->ecnt++;
+ 				return true;
+ 			}
+ 
+ 			/* for not overflowing when generating random number */
+ 			if(max - min < 0 || (max - min) + 1 < 0)
+ 			{
+ 				fprintf(stderr, "%s: range too large\n", argv[0]);
+ 				st->ecnt++;
+ 				return true;
+ 			}
+ 
+ 			if(*argv[4] == ':')
+ 			{
+ 				if((var = getVariable(st, argv[4] + 1)) == NULL)
+ 				{
+ 					fprintf(stderr, "%s: invalid gaussian threshold number %s\n", argv[0], argv[4]);
+ 					st->ecnt++;
+ 					return true;
+ 				}
+ 				stdev_threshold = strtod(var, NULL);
+ 			}
+ 			else
+ 				stdev_threshold = strtod(argv[4], &endptr);
+ 
+ 			if ( stdev_threshold < 2)
+ 			{
+ 				fprintf(stderr, "%s: gaussian threshold must be more than 2\n,", argv[4]);
+ 				st->ecnt++;
+ 				return true;
+ 			}
+ #ifdef DEBUG
+ 			printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getGaussianrand(thread, min, max, stdev_threshold));
+ #endif
+ 			snprintf(res, sizeof(res), INT64_FORMAT, getGaussianrand(thread, min, max, stdev_threshold));
+ 
+ 			if(!putVariable(st, argv[0], argv[1], res))
+ 			{
+ 				st->ecnt++;
+ 				return true;
+ 			}
+ 
+ 			st->listen = 1;
+ 		}
  		else if (pg_strcasecmp(argv[0], "set") == 0)
  		{
  			char	   *var;
***************
*** 1915,1920 **** process_commands(char *buf)
--- 2120,2137 ----
  				fprintf(stderr, "%s: extra argument \"%s\" ignored\n",
  						my_commands->argv[0], my_commands->argv[j]);
  		}
+ 		else if (pg_strcasecmp(my_commands->argv[0], "setgaussian") == 0)
+ 		{
+ 			if (my_commands->argc < 5)
+ 			{
+ 				fprintf(stderr, "%s: missing argument\n", my_commands->argv[0]);
+ 				exit(1);
+ 			}
+ 
+ 			for (j = 5; j < my_commands->argc; j++)
+ 				fprintf(stderr, "%s: extra argument \"%s\" ignored\n",
+ 						my_commands->argv[0], my_commands->argv[j]);
+ 		}
  		else if (pg_strcasecmp(my_commands->argv[0], "set") == 0)
  		{
  			if (my_commands->argc < 3)
***************
*** 2188,2203 **** printResults(int ttype, int normal_xacts, int nclients,
  						(INSTR_TIME_GET_DOUBLE(conn_total_time) / nthreads));
  
  	if (ttype == 0)
! 		s = "TPC-B (sort of)";
  	else if (ttype == 2)
! 		s = "Update only pgbench_accounts";
  	else if (ttype == 1)
! 		s = "SELECT only";
  	else
  		s = "Custom query";
  
  	printf("transaction type: %s\n", s);
  	printf("scaling factor: %d\n", scale);
  	printf("query mode: %s\n", QUERYMODE[querymode]);
  	printf("number of clients: %d\n", nclients);
  	printf("number of threads: %d\n", nthreads);
--- 2405,2447 ----
  						(INSTR_TIME_GET_DOUBLE(conn_total_time) / nthreads));
  
  	if (ttype == 0)
! 	{
! 		if(gaussian_option)
! 			s = "TPC-B (sort of)";
! 		else
! 			s = "Gaussian distributed TPC-B (sort of)";
! 	}
  	else if (ttype == 2)
! 	{
! 		if(gaussian_option)
! 			s = "Gaussian distributed update only pgbench_accounts";
! 		else
! 			s = "Update only pgbench_accounts";
! 	}
  	else if (ttype == 1)
! 	{
! 		if(gaussian_option)
! 			s = "Gaussian distributed SELECT only";
! 		else
! 			s = "SELECT only";
! 	}
  	else
  		s = "Custom query";
  
  	printf("transaction type: %s\n", s);
  	printf("scaling factor: %d\n", scale);
+ 
+ 	/* output in only gaussian distributed benchmark */
+ 	if(gaussian_option)
+ 	{
+ 		printf("standard deviation threshold: %.5f\n", stdev_threshold);
+ 		printf("access probability of top 20%%, 10%% and 5%% records: %.5f %.5f %.5f\n",
+ 			(double) ((erf (stdev_threshold * 0.2 / sqrt(2.0))) / (erf (stdev_threshold / sqrt(2.0)))),
+ 			(double) ((erf (stdev_threshold * 0.1 / sqrt(2.0))) / (erf (stdev_threshold / sqrt(2.0)))),
+ 			(double) ((erf (stdev_threshold * 0.05 / sqrt(2.0))) / (erf (stdev_threshold / sqrt(2.0))))
+ 			);
+ 	}
+ 
  	printf("query mode: %s\n", QUERYMODE[querymode]);
  	printf("number of clients: %d\n", nclients);
  	printf("number of threads: %d\n", nthreads);
***************
*** 2327,2332 **** main(int argc, char **argv)
--- 2571,2577 ----
  		{"unlogged-tables", no_argument, &unlogged_tables, 1},
  		{"sampling-rate", required_argument, NULL, 4},
  		{"aggregate-interval", required_argument, NULL, 5},
+ 		{"gaussian", required_argument, NULL, 6},
  		{"rate", required_argument, NULL, 'R'},
  		{NULL, 0, NULL, 0}
  	};
***************
*** 2606,2611 **** main(int argc, char **argv)
--- 2851,2865 ----
  				}
  #endif
  				break;
+ 			case 6:
+ 				gaussian_option = true;
+ 				stdev_threshold = atof(optarg);
+ 				if(stdev_threshold < 2)
+ 				{
+ 					fprintf(stderr, "--gaussian=NUM must be more than 2: %f\n", stdev_threshold);
+ 					exit(1);
+ 				}
+ 				break;
  			default:
  				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
  				exit(1);
***************
*** 2803,2808 **** main(int argc, char **argv)
--- 3057,3073 ----
  		}
  	}
  
+ 	/* set :stdev_threshold variable */
+ 	if(getVariable(&state[0], "stdev_threshold") == NULL)
+ 	{
+ 		snprintf(val, sizeof(val), "%lf", stdev_threshold);
+ 		for (i = 0; i < nclients; i++)
+ 		{
+ 			if (!putVariable(&state[i], "startup", "stdev_threshold", val))
+ 				exit(1);
+ 		}
+ 	}
+ 
  	if (!is_no_vacuum)
  	{
  		fprintf(stderr, "starting vacuum...");
***************
*** 2828,2844 **** main(int argc, char **argv)
  	switch (ttype)
  	{
  		case 0:
! 			sql_files[0] = process_builtin(tpc_b);
  			num_files = 1;
  			break;
  
  		case 1:
! 			sql_files[0] = process_builtin(select_only);
  			num_files = 1;
  			break;
  
  		case 2:
! 			sql_files[0] = process_builtin(simple_update);
  			num_files = 1;
  			break;
  
--- 3093,3118 ----
  	switch (ttype)
  	{
  		case 0:
! 			if(gaussian_option)
! 				sql_files[0] = process_builtin(gaussian_tpc_b);
! 			else
! 				sql_files[0] = process_builtin(tpc_b);
  			num_files = 1;
  			break;
  
  		case 1:
! 			if(gaussian_option)
! 				sql_files[0] = process_builtin(gaussian_select_only);
! 			else
! 				sql_files[0] = process_builtin(select_only);
  			num_files = 1;
  			break;
  
  		case 2:
! 			if(gaussian_option)
! 				sql_files[0] = process_builtin(simple_update);
! 			else
! 				sql_files[0] = process_builtin(gaussian_simple_update);
  			num_files = 1;
  			break;
  
*** a/doc/src/sgml/pgbench.sgml
--- b/doc/src/sgml/pgbench.sgml
***************
*** 320,325 **** pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
--- 320,342 ----
       </varlistentry>
  
       <varlistentry>
+       <term><option>--gaussian</option><replaceable>standard deviation</></term>
+       <listitem>
+        <para>
+         Gaussian distribution pgbench option. Need the standard deviation threshold.
+         Standard deviation threshold can control distribution of access patern that
+         is used by aid in pgbench_accounts table. If we set larger standard deviation
+         threshold, pgbench access patern limited more specific records. On the other
+         hands, if you set smaller standard deviation, pgbench access patern will be
+         more gently distribution. Standard deviation threshold must be higher than 2.
+         This rule is needed for realizing realistic calculation costs. If you add 
+         '-N' or '-S' options, you can execute gaussian distribution pgbench in these
+         benchmarks.
+        </para>
+       </listitem>
+      </varlistentry>
+ 
+      <varlistentry>
        <term><option>-j</option> <replaceable>threads</></term>
        <term><option>--jobs=</option><replaceable>threads</></term>
        <listitem>
***************
*** 770,775 **** pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
--- 787,818 ----
  
     <varlistentry>
      <term>
+      <literal>\setgaussian <replaceable>varname</> <replaceable>min</> <replaceable>max</> <replaceable>
+      standard deviation threshold</literal>
+     </term>
+ 
+     <listitem>
+      <para>
+       Sets variable <replaceable>varname</> to a gaussian random integer value
+       between the limits <replaceable>min</> and <replaceable>max</> inclusive.
+       Each limit can be either an integer constant or a
+       <literal>:</><replaceable>variablename</> reference to a variable
+       having an integer value. Standard deviation threshold controls
+       distribution of access patern. If we set larger value in standard
+       deviation threshold, more frequentry access patern will be more 
+       limited ranges. Min standard deviation threshold is 2. This rule 
+       needs for realizing realistic calculation costs.
+      </para>
+ 
+      <para>
+       Example:
+ <programlisting>
+ \setgaussian aid 1 :naccounts 5
+ </programlisting></para>
+     </listitem>
+    </varlistentry>
+  <varlistentry>
+     <term>
       <literal>\sleep <replaceable>number</> [ us | ms | s ]</literal>
      </term>
  
#19Mitsumasa KONDO
kondo.mitsumasa@gmail.com
In reply to: KONDO Mitsumasa (#18)
3 attachment(s)
Re: gaussian distribution pgbench

I add exponential distribution random generator (and little bit
refactoring:) ).
I use inverse transform method to create its distribution. It's very
simple method that is
created by - log (rand()). We can control slope of distribution using
threshold parameter.
It is same as gaussian threshold.

usage example
pgbench --exponential=NUM -S

Attached graph is created with exponential threshold = 5. We can see
exponential
distribution in the graphs. It supports -S, -N options and custom script.
So we set
"¥setexponential [var] [min] [max] [threshold]" in a transaction pattern
file,
it appear distribution we want.

We have no time to fix its very much... But I think almost part of patch
have been completed.

Regards,
--
Mitsumasa KONDO
NTT Open Source Software Center

Attachments:

gaussian_and_exponential_pgbench_v6.patchapplication/octet-stream; name=gaussian_and_exponential_pgbench_v6.patchDownload
*** a/contrib/pgbench/pgbench.c
--- b/contrib/pgbench/pgbench.c
***************
*** 106,111 **** extern int	optind;
--- 106,114 ----
  #define LOG_STEP_SECONDS	5	/* seconds between log messages */
  #define DEFAULT_NXACTS	10		/* default nxacts */
  
+ #define MIN_GAUSSIAN_THRESHOLD		2.0	/* use gaussian distributed random generator */
+ #define MIN_EXPONENTIAL_THRESHOLD	2.0	/* use exponential distributed random generator */
+ 
  int			nxacts = 0;			/* number of transactions per client */
  int			duration = 0;		/* duration in seconds */
  
***************
*** 176,181 **** int			progress_nthreads = 0; /* number of threads for progress report */
--- 179,188 ----
  bool		is_connect;			/* establish connection for each transaction */
  bool		is_latencies;		/* report per-command latencies */
  int			main_pid;			/* main process id used in log filename */
+ double		stdev_threshold = 5;		/* standard deviation threshold using gaussian */
+ double		exp_threshold = 5;		/* */
+ bool		gaussian_option = false;	/* use gaussian distribution random generator */
+ bool		exponential_option = false;	/* use exponential distribution random generator */
  
  char	   *pghost = "";
  char	   *pgport = "";
***************
*** 338,346 **** static char *select_only = {
--- 345,436 ----
  	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
  };
  
+ /* --exponential case */
+ static char *exponential_tpc_b = {
+ 	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+ 	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setexponential aid 1 :naccounts :exp_threshold\n"
+ 	"\\setrandom bid 1 :nbranches\n"
+ 	"\\setrandom tid 1 :ntellers\n"
+ 	"\\setrandom delta -5000 5000\n"
+ 	"BEGIN;\n"
+ 	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ 	"UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;\n"
+ 	"UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;\n"
+ 	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+ 	"END;\n"
+ };
+ 
+ /* --exponential with -N case */
+ static char *exponential_simple_update = {
+ 	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+ 	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setexponential aid 1 :naccounts :exp_threshold\n"
+ 	"\\setrandom bid 1 :nbranches\n"
+ 	"\\setrandom tid 1 :ntellers\n"
+ 	"\\setrandom delta -5000 5000\n"
+ 	"BEGIN;\n"
+ 	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ 	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+ 	"END;\n"
+ };
+ 
+ /* --exponential with -S case */
+ static char *exponential_select_only = {
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setexponential aid 1 :naccounts :exp_threshold\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ };
+ 
+ /* --gaussian case */
+ static char *gaussian_tpc_b = {
+ 	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+ 	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setgaussian aid 1 :naccounts :stdev_threshold\n"
+ 	"\\setrandom bid 1 :nbranches\n"
+ 	"\\setrandom tid 1 :ntellers\n"
+ 	"\\setrandom delta -5000 5000\n"
+ 	"BEGIN;\n"
+ 	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ 	"UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;\n"
+ 	"UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;\n"
+ 	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+ 	"END;\n"
+ };
+ 
+ /* --gaussian with -N case */
+ static char *gaussian_simple_update = {
+ 	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+ 	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setgaussian aid 1 :naccounts :stdev_threshold\n"
+ 	"\\setrandom bid 1 :nbranches\n"
+ 	"\\setrandom tid 1 :ntellers\n"
+ 	"\\setrandom delta -5000 5000\n"
+ 	"BEGIN;\n"
+ 	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ 	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+ 	"END;\n"
+ };
+ 
+ /* --gaussian with -S case */
+ static char *gaussian_select_only = {
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setgaussian aid 1 :naccounts :stdev_threshold\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ };
+ 
  /* Function prototypes */
  static void setalarm(int seconds);
  static void *threadRun(void *arg);
+ static inline double sqrtd(const double x);
  
  static void
  usage(void)
***************
*** 381,386 **** usage(void)
--- 471,478 ----
  		   "  -v, --vacuum-all         vacuum all four standard tables before tests\n"
  		   "  --aggregate-interval=NUM aggregate data over NUM seconds\n"
  		   "  --sampling-rate=NUM      fraction of transactions to log (e.g. 0.01 for 1%%)\n"
+ 		   "  --exponential=NUM	       exponential distribution with NUM threshold parameter\n "
+ 		   "  --gaussian=NUM           gaussian distribution with NUM standard deviation threshold\n"
  		   "\nCommon options:\n"
  		   "  -d, --debug              print debugging output\n"
  		   "  -h, --host=HOSTNAME      database server host or socket directory\n"
***************
*** 477,482 **** getrand(TState *thread, int64 min, int64 max)
--- 569,670 ----
  	return min + (int64) ((max - min + 1) * pg_erand48(thread->random_state));
  }
  
+ /* random number generator: exponential distribution from min to max inclusive */
+ static int64
+ getExponentialrand(TState *thread, int64 min, int64 max, double exp_threshold)
+ {
+ 	double		rand;
+ 
+ 	/*
+ 	 * Get user specified random number in this loop. This loop is executed until
+ 	 * appeared ranged number we want. However, this loop could not almost go on,
+ 	 * because min exp_threshold is 2, then the possibility of retry-loop is under
+ 	 * 1 percent. Because -log(0.01) = 2.
+ 	 */
+ 	do
+ 	{
+ 		/* normalization to (0,1] */
+ 		rand = 1.0 - pg_erand48(thread->random_state);
+ 		rand = -log(rand);
+ 	} while (rand > exp_threshold);
+ 
+ 	/* normalization to (0,1] */
+ 	rand = rand / exp_threshold;
+ 
+ 	/* return int64 random number within between min and max */
+ 	return min + (int64)((max - min + 1) * rand);
+ }
+ 
+ /* random number generator: gaussian distribution from min to max inclusive */
+ static int64
+ getGaussianrand(TState *thread, int64 min, int64 max, double stdev_threshold)
+ {
+ 	double		stdev;
+ 	double		rand;
+ 	double		rand1;
+ 	static double	rand2;
+ 	static double	var_sqrt;
+ 	static bool	reuse = false;
+ 
+ 	/*
+ 	 * Get user specified random number(-stdev_threshold < stdev <= stdev_threshold)
+ 	 * in this loop. This loop is executed until appeared ranged number we want.
+ 	 * However, this loop could not almost go on, because min stdev_threshold is 2
+ 	 * then the possibility of retry-loop is under 4 percent. And possibility of
+ 	 * re-retry-loop is under 1.6 percent. And it doesn't happen frequentry even if
+ 	 * we also think about the cycle of the trigonometric function.
+ 	 */
+ 	do
+ 	{
+ 		/* reuse pre calculation result as possible */
+ 		if(!reuse)
+ 		{
+ 			/*
+ 			 * pg_erand48 generates [0,1) random number. However rand1
+ 			 * needs (0,1) random number because log(0) cannot calculate.
+ 			 * And rand2 also needs (0,1) random number in strictly. But
+ 			 * normalization cost is high and we can substitute (0,1] at
+ 			 * rand1 and [0,1) at rand2, so we use approximate calculation.
+ 			 */
+ 			rand1 = 1.0 - pg_erand48(thread->random_state);
+ 			rand2 = pg_erand48(thread->random_state);
+ 
+ 			 /* Box-Muller transform */
+ 			var_sqrt = sqrtd(-2.0 * log(rand1));
+ 			stdev = var_sqrt * sin(2.0 * M_PI * rand2);
+ 			reuse = true;
+ 		}
+ 		else
+ 		{
+ 			stdev = var_sqrt * cos(2.0 * M_PI * rand2);
+ 			reuse = false;
+ 		}
+ 	} while (stdev < -stdev_threshold || stdev >= stdev_threshold);
+ 
+ 	/* normalization to [0,1) */
+ 	rand = (stdev + stdev_threshold) / (stdev_threshold * 2.0);
+ 
+ 	/* return int64 random number within between min and max */
+ 	return min + (int64)((max - min + 1) * rand);
+ }
+ 
+ /*
+  * fast sqrt algorithm: reference from Fast inverse square root algorithms.
+  */
+ static inline double
+ sqrtd(const double x)
+ {
+ 	double		x_half = 0.5 * x;
+ 	long long int	tmp = 0x5FE6EB50C7B537AAl - ( *(long long int*)&x >> 1);
+ 	double		x_result = *(double*)&tmp;
+ 
+ 	x_result *= (1.5 - (x_half * x_result * x_result));
+ 	/* retry this calculation, it becomes higher precision at sqrt */
+ 	x_result *= (1.5 - (x_half * x_result * x_result));
+ 
+ 	return x_result * x;
+ }
+ 
  /* call PQexec() and exit() on failure */
  static void
  executeStatement(PGconn *con, const char *sql)
***************
*** 1315,1325 **** top:
  			fprintf(stderr, "\n");
  		}
  
! 		if (pg_strcasecmp(argv[0], "setrandom") == 0)
  		{
  			char	   *var;
  			int64		min,
  						max;
  			char		res[64];
  
  			if (*argv[2] == ':')
--- 1503,1516 ----
  			fprintf(stderr, "\n");
  		}
  
! 		if ((pg_strcasecmp(argv[0], "setrandom") == 0) ||
! 			(pg_strcasecmp(argv[0], "setgaussian") == 0) ||
! 			(pg_strcasecmp(argv[0], "setexponential") == 0))
  		{
  			char	   *var;
  			int64		min,
  						max;
+ 			double		threshold = 0;
  			char		res[64];
  
  			if (*argv[2] == ':')
***************
*** 1365,1375 **** top:
  			}
  
  			/*
! 			 * getrand() needs to be able to subtract max from min and add one
! 			 * to the result without overflowing.  Since we know max > min, we
! 			 * can detect overflow just by checking for a negative result. But
! 			 * we must check both that the subtraction doesn't overflow, and
! 			 * that adding one to the result doesn't overflow either.
  			 */
  			if (max - min < 0 || (max - min) + 1 < 0)
  			{
--- 1556,1566 ----
  			}
  
  			/*
! 			 * Generate random number functions need to be able to subtract
! 			 * max from min and add one to the result without overflowing.
! 			 * Since we know max > min, we can detect overflow just by checking
! 			 * for a negative result. But we must check both that the subtraction
! 			 * doesn't overflow, and that adding one to the result doesn't overflow either.
  			 */
  			if (max - min < 0 || (max - min) + 1 < 0)
  			{
***************
*** 1378,1387 **** top:
  				return true;
  			}
  
  #ifdef DEBUG
! 			printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getrand(thread, min, max));
  #endif
! 			snprintf(res, sizeof(res), INT64_FORMAT, getrand(thread, min, max));
  
  			if (!putVariable(st, argv[0], argv[1], res))
  			{
--- 1569,1623 ----
  				return true;
  			}
  
+ 			if (pg_strcasecmp(argv[0], "setrandom") == 0)
+ 			{
+ #ifdef DEBUG
+ 				printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getrand(thread, min, max));
+ #endif
+ 				snprintf(res, sizeof(res), INT64_FORMAT, getrand(thread, min, max));
+ 			}
+ 			else if ((pg_strcasecmp(argv[0], "setgaussian") == 0) || (pg_strcasecmp(argv[0], "setexponential") == 0))
+ 			{
+ 				if(*argv[4] == ':')
+ 				{
+ 					if((var = getVariable(st, argv[4] + 1)) == NULL)
+ 					{
+ 						fprintf(stderr, "%s: invalid threshold number %s\n", argv[0], argv[4]);
+ 						st->ecnt++;
+ 						return true;
+ 					}
+ 					threshold = strtod(var, NULL);
+ 				}
+ 				else
+ 					threshold = strtod(argv[4], NULL);
+ 
+ 				if (pg_strcasecmp(argv[0], "setgaussian") == 0)
+ 				{
+ 					if (threshold < MIN_GAUSSIAN_THRESHOLD)
+ 					{
+ 						fprintf(stderr, "%s: gaussian threshold must be more than 2\n,", argv[4]);
+ 						st->ecnt++;
+ 						return true;
+ 					}
+ #ifdef DEBUG
+ 					printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getGaussianrand(thread, min, max, threshold));
+ #endif
+ 					snprintf(res, sizeof(res), INT64_FORMAT, getGaussianrand(thread, min, max, threshold));
+ 				}
+ 				else if (pg_strcasecmp(argv[0], "setexponential") == 0)
+ 				{
+ 					if (threshold < MIN_EXPONENTIAL_THRESHOLD)
+ 					{
+ 						fprintf(stderr, "%s: exponential threshold must be more than 2\n,", argv[4]);
+ 						st->ecnt++;
+ 						return true;
+ 					}
  #ifdef DEBUG
! 					printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getExponentialrand(thread, min, max, threshold));
  #endif
! 					snprintf(res, sizeof(res), INT64_FORMAT, getExponentialrand(thread, min, max, threshold));
! 				}
! 			}
  
  			if (!putVariable(st, argv[0], argv[1], res))
  			{
***************
*** 1915,1920 **** process_commands(char *buf)
--- 2151,2169 ----
  				fprintf(stderr, "%s: extra argument \"%s\" ignored\n",
  						my_commands->argv[0], my_commands->argv[j]);
  		}
+ 		else if ((pg_strcasecmp(my_commands->argv[0], "setgaussian") == 0) ||
+ 			 (pg_strcasecmp(my_commands->argv[0], "setexponential") == 0))
+ 		{
+ 			if (my_commands->argc < 5)
+ 			{
+ 				fprintf(stderr, "%s: missing argument\n", my_commands->argv[0]);
+ 				exit(1);
+ 			}
+ 
+ 			for (j = 5; j < my_commands->argc; j++)
+ 				fprintf(stderr, "%s: extra argument \"%s\" ignored\n",
+ 						my_commands->argv[0], my_commands->argv[j]);
+ 		}
  		else if (pg_strcasecmp(my_commands->argv[0], "set") == 0)
  		{
  			if (my_commands->argc < 3)
***************
*** 2188,2203 **** printResults(int ttype, int normal_xacts, int nclients,
  						(INSTR_TIME_GET_DOUBLE(conn_total_time) / nthreads));
  
  	if (ttype == 0)
! 		s = "TPC-B (sort of)";
  	else if (ttype == 2)
! 		s = "Update only pgbench_accounts";
  	else if (ttype == 1)
! 		s = "SELECT only";
  	else
  		s = "Custom query";
  
  	printf("transaction type: %s\n", s);
  	printf("scaling factor: %d\n", scale);
  	printf("query mode: %s\n", QUERYMODE[querymode]);
  	printf("number of clients: %d\n", nclients);
  	printf("number of threads: %d\n", nthreads);
--- 2437,2483 ----
  						(INSTR_TIME_GET_DOUBLE(conn_total_time) / nthreads));
  
  	if (ttype == 0)
! 	{
! 		if (gaussian_option)
! 			s = "Gaussian distributed TPC-B (sort of)";
! 		else if (exponential_option)
! 			s = "Exponential distributed TPC-B (sort of)";
! 		else
! 			s = "TPC-B (sort of)";
! 	}
  	else if (ttype == 2)
! 	{
! 		if (gaussian_option)
! 			s = "Gaussian distributed update only pgbench_accounts";
! 		else if (exponential_option)
! 			s = "Exponential distributed update only pgbench_accounts";
! 		else
! 			s = "Update only pgbench_accounts";
! 	}
  	else if (ttype == 1)
! 	{
! 		if (gaussian_option)
! 			s = "Gaussian distributed SELECT only";
! 		else if (exponential_option)
! 			s = "Exponential distributed SELECT only";
! 		else
! 			s = "SELECT only";
! 	}
  	else
  		s = "Custom query";
  
  	printf("transaction type: %s\n", s);
  	printf("scaling factor: %d\n", scale);
+ 
+ 	/* output in only gaussian distributed benchmark */
+ 	if (gaussian_option)
+ 	{
+ 		printf("standard deviation threshold: %.5f\n", stdev_threshold);
+ 		printf("access probability of top 20%%, 10%% and 5%% records: %.5f %.5f %.5f\n",
+ 			(double) ((erf (stdev_threshold * 0.2 / sqrt(2.0))) / (erf (stdev_threshold / sqrt(2.0)))),
+ 			(double) ((erf (stdev_threshold * 0.1 / sqrt(2.0))) / (erf (stdev_threshold / sqrt(2.0)))),
+ 			(double) ((erf (stdev_threshold * 0.05 / sqrt(2.0))) / (erf (stdev_threshold / sqrt(2.0)))));
+ 	}
  	printf("query mode: %s\n", QUERYMODE[querymode]);
  	printf("number of clients: %d\n", nclients);
  	printf("number of threads: %d\n", nthreads);
***************
*** 2327,2332 **** main(int argc, char **argv)
--- 2607,2614 ----
  		{"unlogged-tables", no_argument, &unlogged_tables, 1},
  		{"sampling-rate", required_argument, NULL, 4},
  		{"aggregate-interval", required_argument, NULL, 5},
+ 		{"gaussian", required_argument, NULL, 6},
+ 		{"exponential", required_argument, NULL, 7},
  		{"rate", required_argument, NULL, 'R'},
  		{NULL, 0, NULL, 0}
  	};
***************
*** 2606,2611 **** main(int argc, char **argv)
--- 2888,2911 ----
  				}
  #endif
  				break;
+ 			case 6:
+ 				gaussian_option = true;
+ 				stdev_threshold = atof(optarg);
+ 				if(stdev_threshold < MIN_GAUSSIAN_THRESHOLD)
+ 				{
+ 					fprintf(stderr, "--gaussian=NUM must be more than %f: %f\n", MIN_GAUSSIAN_THRESHOLD, stdev_threshold);
+ 					exit(1);
+ 				}
+ 				break;
+ 			case 7:
+ 				exponential_option = true;
+ 				exp_threshold = atof(optarg);
+ 				if(exp_threshold < MIN_EXPONENTIAL_THRESHOLD)
+ 				{
+ 					fprintf(stderr, "--exponential=NUM must be more than %f: %f\n", MIN_EXPONENTIAL_THRESHOLD, exp_threshold);
+ 					exit(1);
+ 				}
+ 				break;
  			default:
  				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
  				exit(1);
***************
*** 2803,2808 **** main(int argc, char **argv)
--- 3103,3130 ----
  		}
  	}
  
+ 	/* set :stdev_threshold variable */
+ 	if(getVariable(&state[0], "stdev_threshold") == NULL)
+ 	{
+ 		snprintf(val, sizeof(val), "%lf", stdev_threshold);
+ 		for (i = 0; i < nclients; i++)
+ 		{
+ 			if (!putVariable(&state[i], "startup", "stdev_threshold", val))
+ 				exit(1);
+ 		}
+ 	}
+ 
+ 	/* set :exp_threshold variable */
+ 	if(getVariable(&state[0], "exp_threshold") == NULL)
+ 	{
+ 		snprintf(val, sizeof(val), "%lf", exp_threshold);
+ 		for (i = 0; i < nclients; i++)
+ 		{
+ 			if (!putVariable(&state[i], "startup", "exp_threshold", val))
+ 				exit(1);
+ 		}
+ 	}
+ 
  	if (!is_no_vacuum)
  	{
  		fprintf(stderr, "starting vacuum...");
***************
*** 2828,2844 **** main(int argc, char **argv)
  	switch (ttype)
  	{
  		case 0:
! 			sql_files[0] = process_builtin(tpc_b);
  			num_files = 1;
  			break;
  
  		case 1:
! 			sql_files[0] = process_builtin(select_only);
  			num_files = 1;
  			break;
  
  		case 2:
! 			sql_files[0] = process_builtin(simple_update);
  			num_files = 1;
  			break;
  
--- 3150,3181 ----
  	switch (ttype)
  	{
  		case 0:
! 			if (gaussian_option)
! 				sql_files[0] = process_builtin(gaussian_tpc_b);
! 			else if (exponential_option)
! 				sql_files[0] = process_builtin(exponential_tpc_b);
! 			else
! 				sql_files[0] = process_builtin(tpc_b);
  			num_files = 1;
  			break;
  
  		case 1:
! 			if (gaussian_option)
! 				sql_files[0] = process_builtin(gaussian_select_only);
! 			else if (exponential_option)
! 				sql_files[0] = process_builtin(exponential_select_only);
! 			else
! 				sql_files[0] = process_builtin(select_only);
  			num_files = 1;
  			break;
  
  		case 2:
! 			if (gaussian_option)
! 				sql_files[0] = process_builtin(gaussian_simple_update);
! 			else if (exponential_option)
! 				sql_files[0] = process_builtin(exponential_simple_update);
! 			else
! 				sql_files[0] = process_builtin(simple_update);
  			num_files = 1;
  			break;
  
*** a/doc/src/sgml/pgbench.sgml
--- b/doc/src/sgml/pgbench.sgml
***************
*** 320,325 **** pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
--- 320,359 ----
       </varlistentry>
  
       <varlistentry>
+       <term><option>--exponential</option><replaceable>deviation threshold</></term>
+       <listitem>
+        <para>
+          Exponential distribution pgbench option. Need the deviation threshold.
+          Deviation threshold can control distribution of access patern that
+          is used by aid in pgbench_accounts table. If we set larger deviation threshold,
+          pgbench access patern limited more specific records. On the other
+          hands, if you set smaller deviation threshold, pgbench access patern will be
+          more gently distribution. Deviation threshold must be higher than 2.
+          This rule is needed for realizing realistic calculation costs. If you add
+          '-N' or '-S' options, you can execute gaussian distribution pgbench in these
+          benchmarks.
+        </para>
+       </listitem>
+      </varlistentry>
+ 
+      <varlistentry>
+       <term><option>--gaussian</option><replaceable>standard deviation threshold</></term>
+       <listitem>
+        <para>
+         Gaussian distribution pgbench option. Need the standard deviation threshold.
+         Standard deviation threshold can control distribution of access patern that
+         is used by aid in pgbench_accounts table. If we set larger standard deviation
+         threshold, pgbench access patern limited more specific records. On the other
+         hands, if you set smaller standard deviation threshold, pgbench access patern
+         will be more gently distribution. Standard deviation threshold must be higher
+         than 2. This rule is needed for realizing realistic calculation costs. If you
+         add '-N' or '-S' options, you can execute gaussian distribution pgbench in these
+         benchmarks.
+        </para>
+       </listitem>
+      </varlistentry>
+ 
+      <varlistentry>
        <term><option>-j</option> <replaceable>threads</></term>
        <term><option>--jobs=</option><replaceable>threads</></term>
        <listitem>
***************
*** 770,775 **** pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
--- 804,863 ----
  
     <varlistentry>
      <term>
+      <literal>\setgaussian <replaceable>varname</> <replaceable>min</> <replaceable>max</> <replaceable>
+      standard deviation threshold</literal>
+     </term>
+ 
+     <listitem>
+      <para>
+       Sets variable <replaceable>varname</> to a gaussian random integer value
+       between the limits <replaceable>min</> and <replaceable>max</> inclusive.
+       Each limit can be either an integer constant or a
+       <literal>:</><replaceable>variablename</> reference to a variable
+       having an integer value. Standard deviation threshold controls
+       distribution of access patern. If we set larger value at standard
+       deviation threshold, more frequentry access patern will be more 
+       limited ranges. Min standard deviation threshold is 2. This rule 
+       needs for realizing realistic calculation costs.
+      </para>
+ 
+      <para>
+       Example:
+ <programlisting>
+ \setgaussian aid 1 :naccounts 5
+ </programlisting></para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+        <term>
+         <literal>\setexponential <replaceable>varname</> <replaceable>min</> <replaceable>max</> <replaceable>
+         deviation threshold</literal>
+        </term>
+ 
+     <listitem>
+      <para>
+       Sets variable <replaceable>varname</> to a exponential random integer value
+       between the limits <replaceable>min</> and <replaceable>max</> inclusive.
+       Each limit can be either an integer constant or a
+       <literal>:</><replaceable>variablename</> reference to a variable
+       having an integer value. Deviation threshold controls distribution
+       of access patern. If we set larger value at deviation threshold,
+       more frequentry access patern will be more limited ranges. Min
+       deviation threshold is 2. This rule needs for realizing
+       realistic calculation costs.
+      </para>
+ 
+      <para>
+       Example:
+ <programlisting>
+ \setexponential aid 1 :naccounts 5
+ </programlisting></para>
+      </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term>
       <literal>\sleep <replaceable>number</> [ us | ms | s ]</literal>
      </term>
  
exponential=5.pngimage/png; name="exponential=5.png"Download
�PNG


IHDR���*�)PLTE���������������@��  ��� �@����@����`��`��`��@��0`��`@@@@���`�``�`���`���@��`��`�`�����` �```    @@ @�`� `�``����@ � ����������  ���`����`�����`�@�@@�����`���������������������``������������� ����  � �� �  �@ �@��`��`�������@��@��`��p�����������T&�s�IDATx�������Eq����]�Q������+�b�bf	�$8�,��e��	� �������,��0)[�o�������E���_��H��UK,i����l,��%`����C����D����������f��g���D���`�[�����Z����k����2�M��_W/�Vv]����,E��k1`]��?R��T�KnG��1�7���[�����[B8&����8M������}����C�-wR��l���k��)>��/��8�N�����Fp��TS4�u�e�e=y/����A��_�����H)V$� &L�09`r ���w���&�z�	�`����4p`r�+���;	09`r ���\#�\���� 8��@�;G!�����	��AL�BHmp���'����'j	|�-�>���#�S� �OzX��Z�o]�M\��ty�����t	I�|
�*��%|���K�a�x$��U��Q���
�[x8��	0���z>�_�@�����-:��A�h$���AL���@8���^�`�3">Q��?�+H����"����@C�:���A�
09��@T|a�0��	<���:��PL�	�09���|���{�H�����J�� &��'Ph��	H��A`r���+��N�z``�/��	096C��6
��$���t��1R��__pp��t�')���\�K=]�]%8�o�����c�7[8��j��	�K�#x��ooBg=����vO`N��������H���LT`�+�F�0����K�m
i���&�+�B�� &�:��1�,u�@c@S��#@�,��.�
X"%@@�� ���@��j$�X7!�1��*�> ���r�o�_�
i��4��  �����bRG��f*��v`{���0�����@��k�%bu$��Z������
)`�.��1�qP�����X	t�4i��/Y�� q70�:�B+�4 `�U�u�H�1p��WJY`}7��$���s	x6U��@)����
@���|c��$@������M����!�#��0DX�������@u<5!�r3�<�T1�"�W�MHcI=�BM�H�1�i|�4�@	��@���%Z��z� ��%��7(4YTp2(�4`�bc�*�{Y�
EX����:

�|U�!=-��ln��<L�i�P���A 
i�P������!�C�5=-�q*@�f`��_*���]	Tp�\!�
�<���th+Yz0 ��Q��%�?���D��5����@��:�S����H���	<*D�m��Y�"��<�~ `r���( �Bt�B�2�xND�1*�+�	09���'s�q%����2(����5�1����������?A�����O��O�B0=~:O`�H��<H�J��L�����p�����[���@@�������_&X"pB�s�<�.�^����"�4o���)#d�i6�;<	 �b30�����n��lt}�&N��&��oO����L��clC�I;n`�4�	���A���?|���t'`Fv���qqU�J%��kF�K�t�����L	�S`�	P����������P��5�.��
�m��Y�����Z���}8�k�8x�f3���������������Tk%��h6��>�$���?X�d���F���������a @X�.iu�9���?V�����Q��j30������n���K�C�^h	�p	He�P���16u���%��EsL`"
�1mLzb<�P @'�.���J�r57X���,��H�C�����
����� ��!(�#8���@�5	�b2�Gx�@��HC�`*�S@;v	P��!�B#wH/�O�m���y��`�S���	����
S����`y �9�N+f:�w�$�V����������!�-�A^k
�����0�K����q�
��V�CB�3�e�h1������-@1����#h#
 h���qQ��E���#|n�C����6�����~�B� �����8@$p�������x����`U��a�c�O��% m���O(Tv��/��	p����N����~�)����B1�s,-��������
�P�������bj
x��*��"D���;�`JJ �O�Xa�%~(�3f��A.�W<�:0
P��6�
S�B�|^��������[���58l~|��� �����0#����/�^�����640_u*�����9�q)@PO�������!�,��`�j$�^H�I#��x!���R1�9��}�ls&|2O�-�5`{�\�%��`��[,�HP���@�
��/����p�L�.����������Z�T�H�������/����#!�D	�:@P ����~-��k�W�f`��;w��A���:�j,�"���p���F�]�{�c�C�c8�J���^�}9dQ�  XV7�E���U���!7U!�C%�C�cD"�-��:��&��,���B��X�y�q��5T\�N�Ucx�V���dh�9w�r����� ��x{��	)BT��c-_9�$�\���u_��D;?^$�{��~���;1��f����������Ox��5���(������Y�1$����a�������V#P��Y����#��7�����`9	��EN!��k�2��\�Y�^FK�_��K� �����p�;����x&����|�s����T<���P����t����\C�x�i�l�]s>|^�����i�+s @�	0��Y�#�?�e��:~��1�q�1�{
y��oX.�K��^�`���O�?����1�32U�Q	`}��?�"``�8����q��q�(�"$�l]��2��g���?�'�R��dnV�!�����U��3�ZP���
�B���0��W��%��e�����;�����
PN?m	���M�����50�m��Es�w�?��4,IY4�Y��L�e�;JX'��Z�l>��\�
�-��+g����b������
�g�� pH�K6��~������k��K�K��*��Q�����T��N�����8��a�\X�����.����%��^�����w�r��H��_����������������������	+]b��-�~�{Y.%����Q-��d\�a�%�������r"<��6o��+��EX'a�K���� \�*�|���s�����2_;����uR�����SN'�H����
0����_`6�]t����=K��&$9�0;y�mM�����%��q%eIEND�B`�
gnuplot.shapplication/x-sh; name=gnuplot.shDownload
#20Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Mitsumasa KONDO (#19)
1 attachment(s)
Re: gaussian distribution pgbench

Gaussian Pgbench v6 patch by Mitsumasa KONDO review & patch v7.

* The purpose of the patch is to allow a pgbench script to draw from normally
distributed or exponentially distributed integer values instead of uniformly
distributed.

This is a valuable contribution to enable pgbench to generate more realistic
loads, which is seldom uniform in practice.

* Changes

I have updated the patch (v7) based on Mitsumasa latest v6:
- some code simplifications & formula changes.
- I've added explicit looping probability computations in comments
to show the (low) looping probability of the iterative search.
- I've tried to clarify the sgml documentation.
- I've removed the 5.0 default value as it was not used anymore.
- I've renamed some variables to match the naming style around.

* Compilation

The patch applies and compiles against current head. It works as expected,
although there is few feedback from the script to show that. By looking
at the "aid" distribution in the "pgbench_history" table after a run, I
could check that the aid values are indeed skewed, depending on the parameters.

* Mathematical soundness

I've checked again the mathematical soundness for the methods involved.

After further thoughts, I'm not that sure that there is not a bias induced
by taking the second value based on "cos" when the first based on "sin"
as failed the test. So I removed the cos computation for the gaussian version,
and simplified the code accordingly. This mean that it may be a little
less efficient, but I'm more confident that there is no bias.

* Conclusion

If Mitsumasa-san is okay with the changes I have made, I would suggest
to accept this patch.

--
Fabien.

Attachments:

gaussian_and_exponential_pgbench_v7.patchtext/x-diff; name=gaussian_and_exponential_pgbench_v7.patchDownload
diff --git a/contrib/pgbench/pgbench.c b/contrib/pgbench/pgbench.c
index 16b7ab5..afe4a32 100644
--- a/contrib/pgbench/pgbench.c
+++ b/contrib/pgbench/pgbench.c
@@ -106,6 +106,9 @@ extern int	optind;
 #define LOG_STEP_SECONDS	5	/* seconds between log messages */
 #define DEFAULT_NXACTS	10		/* default nxacts */
 
+#define MIN_GAUSSIAN_THRESHOLD		2.0	/* minimum threshold for gauss */
+#define MIN_EXPONENTIAL_THRESHOLD	2.0	/* minimum threshold for exp */
+
 int			nxacts = 0;			/* number of transactions per client */
 int			duration = 0;		/* duration in seconds */
 
@@ -177,6 +180,14 @@ bool		is_connect;			/* establish connection for each transaction */
 bool		is_latencies;		/* report per-command latencies */
 int			main_pid;			/* main process id used in log filename */
 
+/* gaussian distribution tests: */
+double		stdev_threshold;   /* standard deviation threshold */
+bool        use_gaussian = false;
+
+/* exponential distribution tests: */
+double		exp_threshold;   /* threshold for exponential */
+bool		use_exponential = false;
+
 char	   *pghost = "";
 char	   *pgport = "";
 char	   *login = NULL;
@@ -338,6 +349,88 @@ static char *select_only = {
 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
 };
 
+/* --exponential case */
+static char *exponential_tpc_b = {
+	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+	"\\setexponential aid 1 :naccounts :exp_threshold\n"
+	"\\setrandom bid 1 :nbranches\n"
+	"\\setrandom tid 1 :ntellers\n"
+	"\\setrandom delta -5000 5000\n"
+	"BEGIN;\n"
+	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+	"UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;\n"
+	"UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;\n"
+	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+	"END;\n"
+};
+
+/* --exponential with -N case */
+static char *exponential_simple_update = {
+	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+	"\\setexponential aid 1 :naccounts :exp_threshold\n"
+	"\\setrandom bid 1 :nbranches\n"
+	"\\setrandom tid 1 :ntellers\n"
+	"\\setrandom delta -5000 5000\n"
+	"BEGIN;\n"
+	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+	"END;\n"
+};
+
+/* --exponential with -S case */
+static char *exponential_select_only = {
+	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+	"\\setexponential aid 1 :naccounts :exp_threshold\n"
+	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+};
+
+/* --gaussian case */
+static char *gaussian_tpc_b = {
+	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+	"\\setgaussian aid 1 :naccounts :stdev_threshold\n"
+	"\\setrandom bid 1 :nbranches\n"
+	"\\setrandom tid 1 :ntellers\n"
+	"\\setrandom delta -5000 5000\n"
+	"BEGIN;\n"
+	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+	"UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;\n"
+	"UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;\n"
+	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+	"END;\n"
+};
+
+/* --gaussian with -N case */
+static char *gaussian_simple_update = {
+	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+	"\\setgaussian aid 1 :naccounts :stdev_threshold\n"
+	"\\setrandom bid 1 :nbranches\n"
+	"\\setrandom tid 1 :ntellers\n"
+	"\\setrandom delta -5000 5000\n"
+	"BEGIN;\n"
+	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+	"END;\n"
+};
+
+/* --gaussian with -S case */
+static char *gaussian_select_only = {
+	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+	"\\setgaussian aid 1 :naccounts :stdev_threshold\n"
+	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+};
+
 /* Function prototypes */
 static void setalarm(int seconds);
 static void *threadRun(void *arg);
@@ -381,6 +474,8 @@ usage(void)
 		   "  -v, --vacuum-all         vacuum all four standard tables before tests\n"
 		   "  --aggregate-interval=NUM aggregate data over NUM seconds\n"
 		   "  --sampling-rate=NUM      fraction of transactions to log (e.g. 0.01 for 1%%)\n"
+		   "  --exponential=NUM	       exponential distribution with NUM threshold parameter\n "
+		   "  --gaussian=NUM           gaussian distribution with NUM standard deviation threshold\n"
 		   "\nCommon options:\n"
 		   "  -d, --debug              print debugging output\n"
 		   "  -h, --host=HOSTNAME      database server host or socket directory\n"
@@ -477,6 +572,79 @@ getrand(TState *thread, int64 min, int64 max)
 	return min + (int64) ((max - min + 1) * pg_erand48(thread->random_state));
 }
 
+/* random number generator: exponential distribution from min to max inclusive */
+static int64
+getExponentialrand(TState *thread, int64 min, int64 max, double exp_threshold)
+{
+	double		rand;
+
+	/*
+	 * Get user specified random number in this loop. This loop is executed until
+	 * the number in the expected range. As the minimum threshold is 2.0, the
+	 * probability of a retry is at worst 13.5%  as - ln(0.135) ~ 2.0 ;
+	 * For a 5.0 threshold, it is about e^{-5} ~ 0.7%.
+	 */
+	do
+	{
+		/* as pg_erand48 is in [0, 1), uniform is in (0, 1] */
+		double uniform = 1.0 - pg_erand48(thread->random_state);
+		/* rand is in [0 LARGE) */
+		rand = - log(uniform);
+	} while (rand >= exp_threshold);
+
+	/* rand in [0, exp_threshold), normalized to [0,1) */
+	rand /= exp_threshold;
+
+	/* return int64 random number within between min and max */
+	return min + (int64)((max - min + 1) * rand);
+}
+
+/* random number generator: gaussian distribution from min to max inclusive */
+static int64
+getGaussianrand(TState *thread, int64 min, int64 max, double stdev_threshold)
+{
+	double		stdev;
+	double		rand;
+
+	/*
+	 * Get user specified random number from this loop, with
+	 *    -stdev_threshold < stdev <= stdev_threshold
+	 *
+	 * This loop is executed until the number is in the expected range.
+	 *
+	 * As the minimum threshold is 2.0, the probability of looping is low:
+	 * sqrt(-2 ln(r)) <= 2 => r >= e^{-2} ~ 0.135, then when taking the average
+	 * sinus multiplier as 2/pi, we have a 8.6% looping probability in the
+	 * worst case. For a 5.0 threshold value, the looping proability
+	 * is about e^{-5} * 2 / pi ~ 0.43%.
+	 */
+	do
+	{
+		/*
+		 * pg_erand48 generates [0,1), but for the basic version of the
+		 * Box-Muller transform the two uniformly distributed random numbers
+		 * are expected in (0, 1] (see http://en.wikipedia.org/wiki/Box_muller)
+		 */
+		double rand1 = 1.0 - pg_erand48(thread->random_state);
+		double rand2 = 1.0 - pg_erand48(thread->random_state);
+
+		/* Box-Muller basic form transform */
+		double var_sqrt = sqrt(-2.0 * log(rand1));
+		stdev = var_sqrt * sin(2.0 * M_PI * rand2);
+
+		/* we may try with cos, but there may be a bias induced if the previous
+		 * value fails the test? To be on the safe side, let us try over.
+		 */
+	}
+	while (stdev < -stdev_threshold || stdev >= stdev_threshold);
+
+	/* stdev is in [-threshold, threshold), normalization to [0,1) */
+	rand = (stdev + stdev_threshold) / (stdev_threshold * 2.0);
+
+	/* return int64 random number within between min and max */
+	return min + (int64)((max - min + 1) * rand);
+}
+
 /* call PQexec() and exit() on failure */
 static void
 executeStatement(PGconn *con, const char *sql)
@@ -1315,11 +1483,14 @@ top:
 			fprintf(stderr, "\n");
 		}
 
-		if (pg_strcasecmp(argv[0], "setrandom") == 0)
+		if ((pg_strcasecmp(argv[0], "setrandom") == 0) ||
+			(pg_strcasecmp(argv[0], "setgaussian") == 0) ||
+			(pg_strcasecmp(argv[0], "setexponential") == 0))
 		{
 			char	   *var;
 			int64		min,
 						max;
+			double		threshold = 0;
 			char		res[64];
 
 			if (*argv[2] == ':')
@@ -1365,11 +1536,11 @@ top:
 			}
 
 			/*
-			 * getrand() needs to be able to subtract max from min and add one
-			 * to the result without overflowing.  Since we know max > min, we
-			 * can detect overflow just by checking for a negative result. But
-			 * we must check both that the subtraction doesn't overflow, and
-			 * that adding one to the result doesn't overflow either.
+			 * Generate random number functions need to be able to subtract
+			 * max from min and add one to the result without overflowing.
+			 * Since we know max > min, we can detect overflow just by checking
+			 * for a negative result. But we must check both that the subtraction
+			 * doesn't overflow, and that adding one to the result doesn't overflow either.
 			 */
 			if (max - min < 0 || (max - min) + 1 < 0)
 			{
@@ -1378,10 +1549,55 @@ top:
 				return true;
 			}
 
+			if (pg_strcasecmp(argv[0], "setrandom") == 0)
+			{
 #ifdef DEBUG
-			printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getrand(thread, min, max));
+				printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getrand(thread, min, max));
 #endif
-			snprintf(res, sizeof(res), INT64_FORMAT, getrand(thread, min, max));
+				snprintf(res, sizeof(res), INT64_FORMAT, getrand(thread, min, max));
+			}
+			else if ((pg_strcasecmp(argv[0], "setgaussian") == 0) || (pg_strcasecmp(argv[0], "setexponential") == 0))
+			{
+				if(*argv[4] == ':')
+				{
+					if((var = getVariable(st, argv[4] + 1)) == NULL)
+					{
+						fprintf(stderr, "%s: invalid threshold number %s\n", argv[0], argv[4]);
+						st->ecnt++;
+						return true;
+					}
+					threshold = strtod(var, NULL);
+				}
+				else
+					threshold = strtod(argv[4], NULL);
+
+				if (pg_strcasecmp(argv[0], "setgaussian") == 0)
+				{
+					if (threshold < MIN_GAUSSIAN_THRESHOLD)
+					{
+						fprintf(stderr, "%s: gaussian threshold must be more than 2\n,", argv[4]);
+						st->ecnt++;
+						return true;
+					}
+#ifdef DEBUG
+					printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getGaussianrand(thread, min, max, threshold));
+#endif
+					snprintf(res, sizeof(res), INT64_FORMAT, getGaussianrand(thread, min, max, threshold));
+				}
+				else if (pg_strcasecmp(argv[0], "setexponential") == 0)
+				{
+					if (threshold < MIN_EXPONENTIAL_THRESHOLD)
+					{
+						fprintf(stderr, "%s: exponential threshold must be more than 2\n,", argv[4]);
+						st->ecnt++;
+						return true;
+					}
+#ifdef DEBUG
+					printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getExponentialrand(thread, min, max, threshold));
+#endif
+					snprintf(res, sizeof(res), INT64_FORMAT, getExponentialrand(thread, min, max, threshold));
+				}
+			}
 
 			if (!putVariable(st, argv[0], argv[1], res))
 			{
@@ -1915,6 +2131,19 @@ process_commands(char *buf)
 				fprintf(stderr, "%s: extra argument \"%s\" ignored\n",
 						my_commands->argv[0], my_commands->argv[j]);
 		}
+		else if ((pg_strcasecmp(my_commands->argv[0], "setgaussian") == 0) ||
+			 (pg_strcasecmp(my_commands->argv[0], "setexponential") == 0))
+		{
+			if (my_commands->argc < 5)
+			{
+				fprintf(stderr, "%s: missing argument\n", my_commands->argv[0]);
+				exit(1);
+			}
+
+			for (j = 5; j < my_commands->argc; j++)
+				fprintf(stderr, "%s: extra argument \"%s\" ignored\n",
+						my_commands->argv[0], my_commands->argv[j]);
+		}
 		else if (pg_strcasecmp(my_commands->argv[0], "set") == 0)
 		{
 			if (my_commands->argc < 3)
@@ -2188,16 +2417,47 @@ printResults(int ttype, int normal_xacts, int nclients,
 						(INSTR_TIME_GET_DOUBLE(conn_total_time) / nthreads));
 
 	if (ttype == 0)
-		s = "TPC-B (sort of)";
+	{
+		if (use_gaussian)
+			s = "Gaussian distributed TPC-B (sort of)";
+		else if (use_exponential)
+			s = "Exponential distributed TPC-B (sort of)";
+		else
+			s = "TPC-B (sort of)";
+	}
 	else if (ttype == 2)
-		s = "Update only pgbench_accounts";
+	{
+		if (use_gaussian)
+			s = "Gaussian distributed update only pgbench_accounts";
+		else if (use_exponential)
+			s = "Exponential distributed update only pgbench_accounts";
+		else
+			s = "Update only pgbench_accounts";
+	}
 	else if (ttype == 1)
-		s = "SELECT only";
+	{
+		if (use_gaussian)
+			s = "Gaussian distributed SELECT only";
+		else if (use_exponential)
+			s = "Exponential distributed SELECT only";
+		else
+			s = "SELECT only";
+	}
 	else
 		s = "Custom query";
 
 	printf("transaction type: %s\n", s);
 	printf("scaling factor: %d\n", scale);
+
+	/* output in only gaussian distributed benchmark */
+	if (use_gaussian)
+	{
+		printf("standard deviation threshold: %.5f\n", stdev_threshold);
+		printf("access probability of top 20%%, 10%% and 5%% records: %.5f %.5f %.5f\n",
+			(double) ((erf (stdev_threshold * 0.2 / sqrt(2.0))) / (erf (stdev_threshold / sqrt(2.0)))),
+			(double) ((erf (stdev_threshold * 0.1 / sqrt(2.0))) / (erf (stdev_threshold / sqrt(2.0)))),
+			(double) ((erf (stdev_threshold * 0.05 / sqrt(2.0))) / (erf (stdev_threshold / sqrt(2.0)))));
+	}
 	printf("query mode: %s\n", QUERYMODE[querymode]);
 	printf("number of clients: %d\n", nclients);
 	printf("number of threads: %d\n", nthreads);
@@ -2327,6 +2587,8 @@ main(int argc, char **argv)
 		{"unlogged-tables", no_argument, &unlogged_tables, 1},
 		{"sampling-rate", required_argument, NULL, 4},
 		{"aggregate-interval", required_argument, NULL, 5},
+		{"gaussian", required_argument, NULL, 6},
+		{"exponential", required_argument, NULL, 7},
 		{"rate", required_argument, NULL, 'R'},
 		{NULL, 0, NULL, 0}
 	};
@@ -2606,8 +2868,29 @@ main(int argc, char **argv)
 				}
 #endif
 				break;
+			case 6:
+				use_gaussian = true;
+				stdev_threshold = atof(optarg);
+				if(stdev_threshold < MIN_GAUSSIAN_THRESHOLD)
+				{
+					fprintf(stderr, "--gaussian=NUM must be more than %f: %f\n",
+							MIN_GAUSSIAN_THRESHOLD, stdev_threshold);
+					exit(1);
+				}
+				break;
+			case 7:
+				use_exponential = true;
+				exp_threshold = atof(optarg);
+				if(exp_threshold < MIN_EXPONENTIAL_THRESHOLD)
+				{
+					fprintf(stderr,	"--exponential=NUM must be more than %f: %f\n",
+							MIN_EXPONENTIAL_THRESHOLD, exp_threshold);
+					exit(1);
+				}
+				break;
 			default:
-				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+				fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
+						progname);
 				exit(1);
 				break;
 		}
@@ -2803,6 +3086,28 @@ main(int argc, char **argv)
 		}
 	}
 
+	/* set :stdev_threshold variable */
+	if(getVariable(&state[0], "stdev_threshold") == NULL)
+	{
+		snprintf(val, sizeof(val), "%lf", stdev_threshold);
+		for (i = 0; i < nclients; i++)
+		{
+			if (!putVariable(&state[i], "startup", "stdev_threshold", val))
+				exit(1);
+		}
+	}
+
+	/* set :exp_threshold variable */
+	if(getVariable(&state[0], "exp_threshold") == NULL)
+	{
+		snprintf(val, sizeof(val), "%lf", exp_threshold);
+		for (i = 0; i < nclients; i++)
+		{
+			if (!putVariable(&state[i], "startup", "exp_threshold", val))
+				exit(1);
+		}
+	}
+
 	if (!is_no_vacuum)
 	{
 		fprintf(stderr, "starting vacuum...");
@@ -2828,17 +3133,32 @@ main(int argc, char **argv)
 	switch (ttype)
 	{
 		case 0:
-			sql_files[0] = process_builtin(tpc_b);
+			if (use_gaussian)
+				sql_files[0] = process_builtin(gaussian_tpc_b);
+			else if (use_exponential)
+				sql_files[0] = process_builtin(exponential_tpc_b);
+			else
+				sql_files[0] = process_builtin(tpc_b);
 			num_files = 1;
 			break;
 
 		case 1:
-			sql_files[0] = process_builtin(select_only);
+			if (use_gaussian)
+				sql_files[0] = process_builtin(gaussian_select_only);
+			else if (use_exponential)
+				sql_files[0] = process_builtin(exponential_select_only);
+			else
+				sql_files[0] = process_builtin(select_only);
 			num_files = 1;
 			break;
 
 		case 2:
-			sql_files[0] = process_builtin(simple_update);
+			if (use_gaussian)
+				sql_files[0] = process_builtin(gaussian_simple_update);
+			else if (use_exponential)
+				sql_files[0] = process_builtin(exponential_simple_update);
+			else
+				sql_files[0] = process_builtin(simple_update);
 			num_files = 1;
 			break;
 
diff --git a/doc/src/sgml/pgbench.sgml b/doc/src/sgml/pgbench.sgml
index 05ca9b7..3e4bcea 100644
--- a/doc/src/sgml/pgbench.sgml
+++ b/doc/src/sgml/pgbench.sgml
@@ -307,6 +307,22 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
      </varlistentry>
 
      <varlistentry>
+      <term><option>--exponential</option><replaceable>deviation threshold</></term>
+      <listitem>
+       <para>
+         Deviation threshold for the exponential distribution pgbench test.
+         This threshold controls the distribution of accesses on the
+         <structname>pgbench_accounts</> table.
+         The larger the deviation threshold, the more records at the beginning
+         are accessed. The smaller the deviation threshold, the smoother the access
+         pattern distribution. The deviation threshold must be more than 2 for
+         internal performance. When set, this option applies to all test variants
+         (<option>-N</> for skipping updates, or <option>-S</> for selects).
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
       <term><option>-f</option> <replaceable>filename</></term>
       <term><option>--file=</option><replaceable>filename</></term>
       <listitem>
@@ -320,6 +336,22 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
      </varlistentry>
 
      <varlistentry>
+      <term><option>--gaussian</option><replaceable>standard deviation threshold</></term>
+      <listitem>
+       <para>
+         Deviation threshold for the Gaussian distribution pgbench test.
+         This threshold controls the distribution of accesses on the
+         <structname>pgbench_accounts</> table.
+         The larger the deviation threshold, the more records in the middle of the table
+         are accessed. The smaller the deviation threshold, the smoother the access
+         pattern distribution. The deviation threshold must be more than 2 for
+         internal performance. When set, this option applies to all test variants
+         (<option>-N</> for skipping updates, or <option>-S</> for selects).
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
       <term><option>-j</option> <replaceable>threads</></term>
       <term><option>--jobs=</option><replaceable>threads</></term>
       <listitem>
@@ -770,6 +802,59 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
 
    <varlistentry>
     <term>
+     <literal>\setgaussian <replaceable>varname</> <replaceable>min</> <replaceable>max</> <replaceable>
+     standard deviation threshold</literal>
+    </term>
+
+    <listitem>
+     <para>
+      Set variable <replaceable>varname</> to a gaussian random integer value
+      between <replaceable>min</> and <replaceable>max</> bounds inclusive.
+      Each bound can be either an integer constant or a
+      <literal>:</><replaceable>variablename</> reference to a variable
+      having an integer value. The standard deviation threshold controls
+      the distribution pattern. The larger the threshold, the more frequent
+      values close to the middle of the interval are drawn. The threshold is
+      the deviation for the <replaceable>min</> and <replaceable>max</> bounds.
+      The minimum threshold is 2.0, for performance.
+     </para>
+
+     <para>
+      Example:
+<programlisting>
+\setgaussian aid 1 :naccounts 5
+</programlisting></para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+       <term>
+        <literal>\setexponential <replaceable>varname</> <replaceable>min</> <replaceable>max</> <replaceable>
+        deviation threshold</literal>
+       </term>
+
+    <listitem>
+     <para>
+      Set variable <replaceable>varname</> to an exponential random integer value
+      between <replaceable>min</> and <replaceable>max</> bounds inclusive.
+      Each bound can be either an integer constant or a
+      <literal>:</><replaceable>variablename</> reference to a variable
+      having an integer value. The deviation threshold controls the distribution
+      pattern: the larger the deviation threshold, the more frequent values
+      close to <replaceable>min</> are drawn.
+      The minimum threshold is 2.0, for performance.
+     </para>
+
+     <para>
+      Example:
+<programlisting>
+\setexponential aid 1 :naccounts 5
+</programlisting></para>
+     </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term>
      <literal>\sleep <replaceable>number</> [ us | ms | s ]</literal>
     </term>
 
#21KONDO Mitsumasa
kondo.mitsumasa@lab.ntt.co.jp
In reply to: Fabien COELHO (#20)
1 attachment(s)
Re: gaussian distribution pgbench

(2014/02/16 7:38), Fabien COELHO wrote:

I have updated the patch (v7) based on Mitsumasa latest v6:
- some code simplifications & formula changes.
- I've added explicit looping probability computations in comments
to show the (low) looping probability of the iterative search.
- I've tried to clarify the sgml documentation.
- I've removed the 5.0 default value as it was not used anymore.
- I've renamed some variables to match the naming style around.

Thank you for yor detail review and fix some code! I checked your modification
version,
it seems better than previos version and very helpful for documents.

* Mathematical soundness

I've checked again the mathematical soundness for the methods involved.

After further thoughts, I'm not that sure that there is not a bias induced
by taking the second value based on "cos" when the first based on "sin"
as failed the test. So I removed the cos computation for the gaussian version,
and simplified the code accordingly. This mean that it may be a little
less efficient, but I'm more confident that there is no bias.

I tried to confirm which method is better. However, at the end of the day, it is
not a problem because other part of implementations have bigger overhead in
pgbench client. We like simple implementaion so I agree with your modification
version. And I tested this version, there is no overhead in creating gaussian and
exponential random number with minimum threshold that is most overhead situation.

* Conclusion

If Mitsumasa-san is okay with the changes I have made, I would suggest
to accept this patch.

Attached patch based on v7 is added output that is possibility of access record
when we use exponential option
in the end of pgbench result. It is caluculated by a definite integral method for
e^-x.
If you check it and think no problem, please mark it ready for commiter.
Ishii-san will review this patch:)

Regards,
--
Mitsumasa KONDO
NTT Open Source Software Center

Attachments:

gaussian_and_exponential_pgbench_v8.patchtext/x-diff; name=gaussian_and_exponential_pgbench_v8.patchDownload
*** a/contrib/pgbench/pgbench.c
--- b/contrib/pgbench/pgbench.c
***************
*** 98,103 **** static int	pthread_join(pthread_t th, void **thread_return);
--- 98,106 ----
  #define LOG_STEP_SECONDS	5	/* seconds between log messages */
  #define DEFAULT_NXACTS	10		/* default nxacts */
  
+ #define MIN_GAUSSIAN_THRESHOLD		2.0	/* minimum threshold for gauss */
+ #define MIN_EXPONENTIAL_THRESHOLD	2.0	/* minimum threshold for exp */
+ 
  int			nxacts = 0;			/* number of transactions per client */
  int			duration = 0;		/* duration in seconds */
  
***************
*** 169,174 **** bool		is_connect;			/* establish connection for each transaction */
--- 172,185 ----
  bool		is_latencies;		/* report per-command latencies */
  int			main_pid;			/* main process id used in log filename */
  
+ /* gaussian distribution tests: */
+ double		stdev_threshold;   /* standard deviation threshold */
+ bool        use_gaussian = false;
+ 
+ /* exponential distribution tests: */
+ double		exp_threshold;   /* threshold for exponential */
+ bool		use_exponential = false;
+ 
  char	   *pghost = "";
  char	   *pgport = "";
  char	   *login = NULL;
***************
*** 330,335 **** static char *select_only = {
--- 341,428 ----
  	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
  };
  
+ /* --exponential case */
+ static char *exponential_tpc_b = {
+ 	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+ 	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setexponential aid 1 :naccounts :exp_threshold\n"
+ 	"\\setrandom bid 1 :nbranches\n"
+ 	"\\setrandom tid 1 :ntellers\n"
+ 	"\\setrandom delta -5000 5000\n"
+ 	"BEGIN;\n"
+ 	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ 	"UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;\n"
+ 	"UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;\n"
+ 	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+ 	"END;\n"
+ };
+ 
+ /* --exponential with -N case */
+ static char *exponential_simple_update = {
+ 	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+ 	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setexponential aid 1 :naccounts :exp_threshold\n"
+ 	"\\setrandom bid 1 :nbranches\n"
+ 	"\\setrandom tid 1 :ntellers\n"
+ 	"\\setrandom delta -5000 5000\n"
+ 	"BEGIN;\n"
+ 	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ 	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+ 	"END;\n"
+ };
+ 
+ /* --exponential with -S case */
+ static char *exponential_select_only = {
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setexponential aid 1 :naccounts :exp_threshold\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ };
+ 
+ /* --gaussian case */
+ static char *gaussian_tpc_b = {
+ 	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+ 	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setgaussian aid 1 :naccounts :stdev_threshold\n"
+ 	"\\setrandom bid 1 :nbranches\n"
+ 	"\\setrandom tid 1 :ntellers\n"
+ 	"\\setrandom delta -5000 5000\n"
+ 	"BEGIN;\n"
+ 	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ 	"UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;\n"
+ 	"UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;\n"
+ 	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+ 	"END;\n"
+ };
+ 
+ /* --gaussian with -N case */
+ static char *gaussian_simple_update = {
+ 	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+ 	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setgaussian aid 1 :naccounts :stdev_threshold\n"
+ 	"\\setrandom bid 1 :nbranches\n"
+ 	"\\setrandom tid 1 :ntellers\n"
+ 	"\\setrandom delta -5000 5000\n"
+ 	"BEGIN;\n"
+ 	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ 	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+ 	"END;\n"
+ };
+ 
+ /* --gaussian with -S case */
+ static char *gaussian_select_only = {
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setgaussian aid 1 :naccounts :stdev_threshold\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ };
+ 
  /* Function prototypes */
  static void setalarm(int seconds);
  static void *threadRun(void *arg);
***************
*** 373,378 **** usage(void)
--- 466,473 ----
  		   "  -v, --vacuum-all         vacuum all four standard tables before tests\n"
  		   "  --aggregate-interval=NUM aggregate data over NUM seconds\n"
  		   "  --sampling-rate=NUM      fraction of transactions to log (e.g. 0.01 for 1%%)\n"
+ 		   "  --exponential=NUM	       exponential distribution with NUM threshold parameter\n "
+ 		   "  --gaussian=NUM           gaussian distribution with NUM standard deviation threshold\n"
  		   "\nCommon options:\n"
  		   "  -d, --debug              print debugging output\n"
  		   "  -h, --host=HOSTNAME      database server host or socket directory\n"
***************
*** 469,474 **** getrand(TState *thread, int64 min, int64 max)
--- 564,642 ----
  	return min + (int64) ((max - min + 1) * pg_erand48(thread->random_state));
  }
  
+ /* random number generator: exponential distribution from min to max inclusive */
+ static int64
+ getExponentialrand(TState *thread, int64 min, int64 max, double exp_threshold)
+ {
+ 	double		rand;
+ 
+ 	/*
+ 	 * Get user specified random number in this loop. This loop is executed until
+ 	 * the number in the expected range. As the minimum threshold is 2.0, the
+ 	 * probability of a retry is at worst 13.5%  as - ln(0.135) ~ 2.0 ;
+ 	 * For a 5.0 threshold, it is about e^{-5} ~ 0.7%.
+ 	 */
+ 	do
+ 	{
+ 		/* as pg_erand48 is in [0, 1), uniform is in (0, 1] */
+ 		double uniform = 1.0 - pg_erand48(thread->random_state);
+ 		/* rand is in [0 LARGE) */
+ 		rand = - log(uniform);
+ 	} while (rand >= exp_threshold);
+ 
+ 	/* rand in [0, exp_threshold), normalized to [0,1) */
+ 	rand /= exp_threshold;
+ 
+ 	/* return int64 random number within between min and max */
+ 	return min + (int64)((max - min + 1) * rand);
+ }
+ 
+ /* random number generator: gaussian distribution from min to max inclusive */
+ static int64
+ getGaussianrand(TState *thread, int64 min, int64 max, double stdev_threshold)
+ {
+ 	double		stdev;
+ 	double		rand;
+ 
+ 	/*
+ 	 * Get user specified random number from this loop, with
+ 	 *    -stdev_threshold < stdev <= stdev_threshold
+ 	 *
+ 	 * This loop is executed until the number is in the expected range.
+ 	 *
+ 	 * As the minimum threshold is 2.0, the probability of looping is low:
+ 	 * sqrt(-2 ln(r)) <= 2 => r >= e^{-2} ~ 0.135, then when taking the average
+ 	 * sinus multiplier as 2/pi, we have a 8.6% looping probability in the
+ 	 * worst case. For a 5.0 threshold value, the looping proability
+ 	 * is about e^{-5} * 2 / pi ~ 0.43%.
+ 	 */
+ 	do
+ 	{
+ 		/*
+ 		 * pg_erand48 generates [0,1), but for the basic version of the
+ 		 * Box-Muller transform the two uniformly distributed random numbers
+ 		 * are expected in (0, 1] (see http://en.wikipedia.org/wiki/Box_muller)
+ 		 */
+ 		double rand1 = 1.0 - pg_erand48(thread->random_state);
+ 		double rand2 = 1.0 - pg_erand48(thread->random_state);
+ 
+ 		/* Box-Muller basic form transform */
+ 		double var_sqrt = sqrt(-2.0 * log(rand1));
+ 		stdev = var_sqrt * sin(2.0 * M_PI * rand2);
+ 
+ 		/* we may try with cos, but there may be a bias induced if the previous
+ 		 * value fails the test? To be on the safe side, let us try over.
+ 		 */
+ 	}
+ 	while (stdev < -stdev_threshold || stdev >= stdev_threshold);
+ 
+ 	/* stdev is in [-threshold, threshold), normalization to [0,1) */
+ 	rand = (stdev + stdev_threshold) / (stdev_threshold * 2.0);
+ 
+ 	/* return int64 random number within between min and max */
+ 	return min + (int64)((max - min + 1) * rand);
+ }
+ 
  /* call PQexec() and exit() on failure */
  static void
  executeStatement(PGconn *con, const char *sql)
***************
*** 1307,1317 **** top:
  			fprintf(stderr, "\n");
  		}
  
! 		if (pg_strcasecmp(argv[0], "setrandom") == 0)
  		{
  			char	   *var;
  			int64		min,
  						max;
  			char		res[64];
  
  			if (*argv[2] == ':')
--- 1475,1488 ----
  			fprintf(stderr, "\n");
  		}
  
! 		if ((pg_strcasecmp(argv[0], "setrandom") == 0) ||
! 			(pg_strcasecmp(argv[0], "setgaussian") == 0) ||
! 			(pg_strcasecmp(argv[0], "setexponential") == 0))
  		{
  			char	   *var;
  			int64		min,
  						max;
+ 			double		threshold = 0;
  			char		res[64];
  
  			if (*argv[2] == ':')
***************
*** 1357,1367 **** top:
  			}
  
  			/*
! 			 * getrand() needs to be able to subtract max from min and add one
! 			 * to the result without overflowing.  Since we know max > min, we
! 			 * can detect overflow just by checking for a negative result. But
! 			 * we must check both that the subtraction doesn't overflow, and
! 			 * that adding one to the result doesn't overflow either.
  			 */
  			if (max - min < 0 || (max - min) + 1 < 0)
  			{
--- 1528,1538 ----
  			}
  
  			/*
! 			 * Generate random number functions need to be able to subtract
! 			 * max from min and add one to the result without overflowing.
! 			 * Since we know max > min, we can detect overflow just by checking
! 			 * for a negative result. But we must check both that the subtraction
! 			 * doesn't overflow, and that adding one to the result doesn't overflow either.
  			 */
  			if (max - min < 0 || (max - min) + 1 < 0)
  			{
***************
*** 1370,1379 **** top:
  				return true;
  			}
  
  #ifdef DEBUG
! 			printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getrand(thread, min, max));
  #endif
! 			snprintf(res, sizeof(res), INT64_FORMAT, getrand(thread, min, max));
  
  			if (!putVariable(st, argv[0], argv[1], res))
  			{
--- 1541,1595 ----
  				return true;
  			}
  
+ 			if (pg_strcasecmp(argv[0], "setrandom") == 0)
+ 			{
+ #ifdef DEBUG
+ 				printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getrand(thread, min, max));
+ #endif
+ 				snprintf(res, sizeof(res), INT64_FORMAT, getrand(thread, min, max));
+ 			}
+ 			else if ((pg_strcasecmp(argv[0], "setgaussian") == 0) || (pg_strcasecmp(argv[0], "setexponential") == 0))
+ 			{
+ 				if(*argv[4] == ':')
+ 				{
+ 					if((var = getVariable(st, argv[4] + 1)) == NULL)
+ 					{
+ 						fprintf(stderr, "%s: invalid threshold number %s\n", argv[0], argv[4]);
+ 						st->ecnt++;
+ 						return true;
+ 					}
+ 					threshold = strtod(var, NULL);
+ 				}
+ 				else
+ 					threshold = strtod(argv[4], NULL);
+ 
+ 				if (pg_strcasecmp(argv[0], "setgaussian") == 0)
+ 				{
+ 					if (threshold < MIN_GAUSSIAN_THRESHOLD)
+ 					{
+ 						fprintf(stderr, "%s: gaussian threshold must be more than 2\n,", argv[4]);
+ 						st->ecnt++;
+ 						return true;
+ 					}
+ #ifdef DEBUG
+ 					printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getGaussianrand(thread, min, max, threshold));
+ #endif
+ 					snprintf(res, sizeof(res), INT64_FORMAT, getGaussianrand(thread, min, max, threshold));
+ 				}
+ 				else if (pg_strcasecmp(argv[0], "setexponential") == 0)
+ 				{
+ 					if (threshold < MIN_EXPONENTIAL_THRESHOLD)
+ 					{
+ 						fprintf(stderr, "%s: exponential threshold must be more than 2\n,", argv[4]);
+ 						st->ecnt++;
+ 						return true;
+ 					}
  #ifdef DEBUG
! 					printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getExponentialrand(thread, min, max, threshold));
  #endif
! 					snprintf(res, sizeof(res), INT64_FORMAT, getExponentialrand(thread, min, max, threshold));
! 				}
! 			}
  
  			if (!putVariable(st, argv[0], argv[1], res))
  			{
***************
*** 1907,1912 **** process_commands(char *buf)
--- 2123,2141 ----
  				fprintf(stderr, "%s: extra argument \"%s\" ignored\n",
  						my_commands->argv[0], my_commands->argv[j]);
  		}
+ 		else if ((pg_strcasecmp(my_commands->argv[0], "setgaussian") == 0) ||
+ 			 (pg_strcasecmp(my_commands->argv[0], "setexponential") == 0))
+ 		{
+ 			if (my_commands->argc < 5)
+ 			{
+ 				fprintf(stderr, "%s: missing argument\n", my_commands->argv[0]);
+ 				exit(1);
+ 			}
+ 
+ 			for (j = 5; j < my_commands->argc; j++)
+ 				fprintf(stderr, "%s: extra argument \"%s\" ignored\n",
+ 						my_commands->argv[0], my_commands->argv[j]);
+ 		}
  		else if (pg_strcasecmp(my_commands->argv[0], "set") == 0)
  		{
  			if (my_commands->argc < 3)
***************
*** 2180,2195 **** printResults(int ttype, int normal_xacts, int nclients,
  						(INSTR_TIME_GET_DOUBLE(conn_total_time) / nthreads));
  
  	if (ttype == 0)
! 		s = "TPC-B (sort of)";
  	else if (ttype == 2)
! 		s = "Update only pgbench_accounts";
  	else if (ttype == 1)
! 		s = "SELECT only";
  	else
  		s = "Custom query";
  
  	printf("transaction type: %s\n", s);
  	printf("scaling factor: %d\n", scale);
  	printf("query mode: %s\n", QUERYMODE[querymode]);
  	printf("number of clients: %d\n", nclients);
  	printf("number of threads: %d\n", nthreads);
--- 2409,2465 ----
  						(INSTR_TIME_GET_DOUBLE(conn_total_time) / nthreads));
  
  	if (ttype == 0)
! 	{
! 		if (use_gaussian)
! 			s = "Gaussian distribution TPC-B (sort of)";
! 		else if (use_exponential)
! 			s = "Exponential distribution TPC-B (sort of)";
! 		else
! 			s = "TPC-B (sort of)";
! 	}
  	else if (ttype == 2)
! 	{
! 		if (use_gaussian)
! 			s = "Gaussian distribution update only pgbench_accounts";
! 		else if (use_exponential)
! 			s = "Exponential distribution update only pgbench_accounts";
! 		else
! 			s = "Update only pgbench_accounts";
! 	}
  	else if (ttype == 1)
! 	{
! 		if (use_gaussian)
! 			s = "Gaussian distribution SELECT only";
! 		else if (use_exponential)
! 			s = "Exponential distribution SELECT only";
! 		else
! 			s = "SELECT only";
! 	}
  	else
  		s = "Custom query";
  
  	printf("transaction type: %s\n", s);
  	printf("scaling factor: %d\n", scale);
+ 
+ 	/* output in gaussian distribution benchmark */
+ 	if (use_gaussian)
+ 	{
+ 		printf("standard deviation threshold: %.5f\n", stdev_threshold);
+ 		printf("access probability of top 20%%, 10%% and 5%% records: %.5f %.5f %.5f\n",
+ 			(double) ((erf (stdev_threshold * 0.2 / sqrt(2.0))) / (erf (stdev_threshold / sqrt(2.0)))),
+ 			(double) ((erf (stdev_threshold * 0.1 / sqrt(2.0))) / (erf (stdev_threshold / sqrt(2.0)))),
+ 			(double) ((erf (stdev_threshold * 0.05 / sqrt(2.0))) / (erf (stdev_threshold / sqrt(2.0)))));
+ 	}
+ 	/* output in exponential distribution benchmark  */
+ 	else if (use_exponential)
+ 	{
+ 		printf("exponential threshold: %.5f\n", exp_threshold);
+ 		printf("access probability of top 20%%, 10%% and 5%% records: %.5f %.5f %.5f\n",
+ 			(double) (-exp(-exp_threshold * 0.2) + 1),
+ 			(double) (-exp(-exp_threshold * 0.1) + 1),
+ 			(double) (-exp(-exp_threshold * 0.05) + 1));
+ 	}
+ 
  	printf("query mode: %s\n", QUERYMODE[querymode]);
  	printf("number of clients: %d\n", nclients);
  	printf("number of threads: %d\n", nthreads);
***************
*** 2319,2324 **** main(int argc, char **argv)
--- 2589,2596 ----
  		{"unlogged-tables", no_argument, &unlogged_tables, 1},
  		{"sampling-rate", required_argument, NULL, 4},
  		{"aggregate-interval", required_argument, NULL, 5},
+ 		{"gaussian", required_argument, NULL, 6},
+ 		{"exponential", required_argument, NULL, 7},
  		{"rate", required_argument, NULL, 'R'},
  		{NULL, 0, NULL, 0}
  	};
***************
*** 2598,2605 **** main(int argc, char **argv)
  				}
  #endif
  				break;
  			default:
! 				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
  				exit(1);
  				break;
  		}
--- 2870,2897 ----
  				}
  #endif
  				break;
+ 			case 6:
+ 				use_gaussian = true;
+ 				stdev_threshold = atof(optarg);
+ 				if(stdev_threshold < MIN_GAUSSIAN_THRESHOLD)
+ 				{
+ 					fprintf(stderr, "--gaussian=NUM must be more than %f: %f\n",
+ 							MIN_GAUSSIAN_THRESHOLD, stdev_threshold);
+ 					exit(1);
+ 				}
+ 				break;
+ 			case 7:
+ 				use_exponential = true;
+ 				exp_threshold = atof(optarg);
+ 				if(exp_threshold < MIN_EXPONENTIAL_THRESHOLD)
+ 				{
+ 					fprintf(stderr,	"--exponential=NUM must be more than %f: %f\n",
+ 							MIN_EXPONENTIAL_THRESHOLD, exp_threshold);
+ 					exit(1);
+ 				}
+ 				break;
  			default:
! 				fprintf(stderr, _("Try \"%s --help\" for more information.\n"),	progname);
  				exit(1);
  				break;
  		}
***************
*** 2795,2800 **** main(int argc, char **argv)
--- 3087,3114 ----
  		}
  	}
  
+ 	/* set :stdev_threshold variable */
+ 	if(getVariable(&state[0], "stdev_threshold") == NULL)
+ 	{
+ 		snprintf(val, sizeof(val), "%lf", stdev_threshold);
+ 		for (i = 0; i < nclients; i++)
+ 		{
+ 			if (!putVariable(&state[i], "startup", "stdev_threshold", val))
+ 				exit(1);
+ 		}
+ 	}
+ 
+ 	/* set :exp_threshold variable */
+ 	if(getVariable(&state[0], "exp_threshold") == NULL)
+ 	{
+ 		snprintf(val, sizeof(val), "%lf", exp_threshold);
+ 		for (i = 0; i < nclients; i++)
+ 		{
+ 			if (!putVariable(&state[i], "startup", "exp_threshold", val))
+ 				exit(1);
+ 		}
+ 	}
+ 
  	if (!is_no_vacuum)
  	{
  		fprintf(stderr, "starting vacuum...");
***************
*** 2820,2836 **** main(int argc, char **argv)
  	switch (ttype)
  	{
  		case 0:
! 			sql_files[0] = process_builtin(tpc_b);
  			num_files = 1;
  			break;
  
  		case 1:
! 			sql_files[0] = process_builtin(select_only);
  			num_files = 1;
  			break;
  
  		case 2:
! 			sql_files[0] = process_builtin(simple_update);
  			num_files = 1;
  			break;
  
--- 3134,3165 ----
  	switch (ttype)
  	{
  		case 0:
! 			if (use_gaussian)
! 				sql_files[0] = process_builtin(gaussian_tpc_b);
! 			else if (use_exponential)
! 				sql_files[0] = process_builtin(exponential_tpc_b);
! 			else
! 				sql_files[0] = process_builtin(tpc_b);
  			num_files = 1;
  			break;
  
  		case 1:
! 			if (use_gaussian)
! 				sql_files[0] = process_builtin(gaussian_select_only);
! 			else if (use_exponential)
! 				sql_files[0] = process_builtin(exponential_select_only);
! 			else
! 				sql_files[0] = process_builtin(select_only);
  			num_files = 1;
  			break;
  
  		case 2:
! 			if (use_gaussian)
! 				sql_files[0] = process_builtin(gaussian_simple_update);
! 			else if (use_exponential)
! 				sql_files[0] = process_builtin(exponential_simple_update);
! 			else
! 				sql_files[0] = process_builtin(simple_update);
  			num_files = 1;
  			break;
  
*** a/doc/src/sgml/pgbench.sgml
--- b/doc/src/sgml/pgbench.sgml
***************
*** 307,312 **** pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
--- 307,328 ----
       </varlistentry>
  
       <varlistentry>
+       <term><option>--exponential</option><replaceable>deviation threshold</></term>
+       <listitem>
+        <para>
+          Deviation threshold for the exponential distribution pgbench test.
+          This threshold controls the distribution of accesses on the
+          <structname>pgbench_accounts</> table.
+          The larger the deviation threshold, the more records at the beginning
+          are accessed. The smaller the deviation threshold, the smoother the access
+          pattern distribution. The deviation threshold must be more than 2 for
+          internal performance. When set, this option applies to all test variants
+          (<option>-N</> for skipping updates, or <option>-S</> for selects).
+        </para>
+       </listitem>
+      </varlistentry>
+ 
+      <varlistentry>
        <term><option>-f</option> <replaceable>filename</></term>
        <term><option>--file=</option><replaceable>filename</></term>
        <listitem>
***************
*** 320,325 **** pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
--- 336,357 ----
       </varlistentry>
  
       <varlistentry>
+       <term><option>--gaussian</option><replaceable>standard deviation threshold</></term>
+       <listitem>
+        <para>
+          Deviation threshold for the Gaussian distribution pgbench test.
+          This threshold controls the distribution of accesses on the
+          <structname>pgbench_accounts</> table.
+          The larger the deviation threshold, the more records in the middle of the table
+          are accessed. The smaller the deviation threshold, the smoother the access
+          pattern distribution. The deviation threshold must be more than 2 for
+          internal performance. When set, this option applies to all test variants
+          (<option>-N</> for skipping updates, or <option>-S</> for selects).
+        </para>
+       </listitem>
+      </varlistentry>
+ 
+      <varlistentry>
        <term><option>-j</option> <replaceable>threads</></term>
        <term><option>--jobs=</option><replaceable>threads</></term>
        <listitem>
***************
*** 770,775 **** pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
--- 802,860 ----
  
     <varlistentry>
      <term>
+      <literal>\setgaussian <replaceable>varname</> <replaceable>min</> <replaceable>max</> <replaceable>
+      standard deviation threshold</literal>
+     </term>
+ 
+     <listitem>
+      <para>
+       Set variable <replaceable>varname</> to a gaussian random integer value
+       between <replaceable>min</> and <replaceable>max</> bounds inclusive.
+       Each bound can be either an integer constant or a
+       <literal>:</><replaceable>variablename</> reference to a variable
+       having an integer value. The standard deviation threshold controls
+       the distribution pattern. The larger the threshold, the more frequent
+       values close to the middle of the interval are drawn. The threshold is
+       the deviation for the <replaceable>min</> and <replaceable>max</> bounds.
+       The minimum threshold is 2.0, for performance.
+      </para>
+ 
+      <para>
+       Example:
+ <programlisting>
+ \setgaussian aid 1 :naccounts 5
+ </programlisting></para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+        <term>
+         <literal>\setexponential <replaceable>varname</> <replaceable>min</> <replaceable>max</> <replaceable>
+         deviation threshold</literal>
+        </term>
+ 
+     <listitem>
+      <para>
+       Set variable <replaceable>varname</> to an exponential random integer value
+       between <replaceable>min</> and <replaceable>max</> bounds inclusive.
+       Each bound can be either an integer constant or a
+       <literal>:</><replaceable>variablename</> reference to a variable
+       having an integer value. The deviation threshold controls the distribution
+       pattern: the larger the deviation threshold, the more frequent values
+       close to <replaceable>min</> are drawn.
+       The minimum threshold is 2.0, for performance.
+      </para>
+ 
+      <para>
+       Example:
+ <programlisting>
+ \setexponential aid 1 :naccounts 5
+ </programlisting></para>
+      </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term>
       <literal>\sleep <replaceable>number</> [ us | ms | s ]</literal>
      </term>
  
#22Fabien COELHO
coelho@cri.ensmp.fr
In reply to: KONDO Mitsumasa (#21)
1 attachment(s)
Re: gaussian distribution pgbench

Gaussian Pgbench v8 patch by Mitsumasa KONDO review & patch v9.

* The purpose of the patch is to allow a pgbench script to draw from normally
distributed or exponentially distributed integer values instead of uniformly
distributed.

This is a valuable contribution to enable pgbench to generate more realistic
loads, which is seldom uniform in practice.

* Very minor change

I have updated the patch (v9) based on Mitsumasa latest v8:
- remove one spurious space in the help message.

* Compilation

The patch applies cleanly and compiles against current head.

* Check

I have checked that the aid values are skewed depending on the
parameters by looking at the "aid" distribution in the "pgbench_history"
table after a run.

* Mathematical soundness

I've checked the mathematical soundness of the methods involved.

I'm fine with casting doubles to integers for having the expected
distribution on integers.

Although there is a retry loop for finding a suitable, the looping
probability is low thanks to the minimum threshold parameter required.

* Conclusion

I suggest to apply this patch which provide a useful and more realistic
testing capability to pgbench.

--
Fabien.

Attachments:

gaussian_and_exponential_pgbench_v9.patchtext/x-diff; name=gaussian_and_exponential_pgbench_v9.patchDownload
diff --git a/contrib/pgbench/pgbench.c b/contrib/pgbench/pgbench.c
index a836acf..35edd27 100644
--- a/contrib/pgbench/pgbench.c
+++ b/contrib/pgbench/pgbench.c
@@ -98,6 +98,9 @@ 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 MIN_GAUSSIAN_THRESHOLD		2.0	/* minimum threshold for gauss */
+#define MIN_EXPONENTIAL_THRESHOLD	2.0	/* minimum threshold for exp */
+
 int			nxacts = 0;			/* number of transactions per client */
 int			duration = 0;		/* duration in seconds */
 
@@ -169,6 +172,14 @@ bool		is_connect;			/* establish connection for each transaction */
 bool		is_latencies;		/* report per-command latencies */
 int			main_pid;			/* main process id used in log filename */
 
+/* gaussian distribution tests: */
+double		stdev_threshold;   /* standard deviation threshold */
+bool        use_gaussian = false;
+
+/* exponential distribution tests: */
+double		exp_threshold;   /* threshold for exponential */
+bool		use_exponential = false;
+
 char	   *pghost = "";
 char	   *pgport = "";
 char	   *login = NULL;
@@ -330,6 +341,88 @@ static char *select_only = {
 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
 };
 
+/* --exponential case */
+static char *exponential_tpc_b = {
+	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+	"\\setexponential aid 1 :naccounts :exp_threshold\n"
+	"\\setrandom bid 1 :nbranches\n"
+	"\\setrandom tid 1 :ntellers\n"
+	"\\setrandom delta -5000 5000\n"
+	"BEGIN;\n"
+	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+	"UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;\n"
+	"UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;\n"
+	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+	"END;\n"
+};
+
+/* --exponential with -N case */
+static char *exponential_simple_update = {
+	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+	"\\setexponential aid 1 :naccounts :exp_threshold\n"
+	"\\setrandom bid 1 :nbranches\n"
+	"\\setrandom tid 1 :ntellers\n"
+	"\\setrandom delta -5000 5000\n"
+	"BEGIN;\n"
+	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+	"END;\n"
+};
+
+/* --exponential with -S case */
+static char *exponential_select_only = {
+	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+	"\\setexponential aid 1 :naccounts :exp_threshold\n"
+	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+};
+
+/* --gaussian case */
+static char *gaussian_tpc_b = {
+	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+	"\\setgaussian aid 1 :naccounts :stdev_threshold\n"
+	"\\setrandom bid 1 :nbranches\n"
+	"\\setrandom tid 1 :ntellers\n"
+	"\\setrandom delta -5000 5000\n"
+	"BEGIN;\n"
+	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+	"UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;\n"
+	"UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;\n"
+	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+	"END;\n"
+};
+
+/* --gaussian with -N case */
+static char *gaussian_simple_update = {
+	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+	"\\setgaussian aid 1 :naccounts :stdev_threshold\n"
+	"\\setrandom bid 1 :nbranches\n"
+	"\\setrandom tid 1 :ntellers\n"
+	"\\setrandom delta -5000 5000\n"
+	"BEGIN;\n"
+	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+	"END;\n"
+};
+
+/* --gaussian with -S case */
+static char *gaussian_select_only = {
+	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+	"\\setgaussian aid 1 :naccounts :stdev_threshold\n"
+	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+};
+
 /* Function prototypes */
 static void setalarm(int seconds);
 static void *threadRun(void *arg);
@@ -373,6 +466,8 @@ usage(void)
 		   "  -v, --vacuum-all         vacuum all four standard tables before tests\n"
 		   "  --aggregate-interval=NUM aggregate data over NUM seconds\n"
 		   "  --sampling-rate=NUM      fraction of transactions to log (e.g. 0.01 for 1%%)\n"
+		   "  --exponential=NUM        exponential distribution with NUM threshold parameter\n"
+		   "  --gaussian=NUM           gaussian distribution with NUM standard deviation threshold\n"
 		   "\nCommon options:\n"
 		   "  -d, --debug              print debugging output\n"
 		   "  -h, --host=HOSTNAME      database server host or socket directory\n"
@@ -469,6 +564,79 @@ getrand(TState *thread, int64 min, int64 max)
 	return min + (int64) ((max - min + 1) * pg_erand48(thread->random_state));
 }
 
+/* random number generator: exponential distribution from min to max inclusive */
+static int64
+getExponentialrand(TState *thread, int64 min, int64 max, double exp_threshold)
+{
+	double		rand;
+
+	/*
+	 * Get user specified random number in this loop. This loop is executed until
+	 * the number in the expected range. As the minimum threshold is 2.0, the
+	 * probability of a retry is at worst 13.5%  as - ln(0.135) ~ 2.0 ;
+	 * For a 5.0 threshold, it is about e^{-5} ~ 0.7%.
+	 */
+	do
+	{
+		/* as pg_erand48 is in [0, 1), uniform is in (0, 1] */
+		double uniform = 1.0 - pg_erand48(thread->random_state);
+		/* rand is in [0 LARGE) */
+		rand = - log(uniform);
+	} while (rand >= exp_threshold);
+
+	/* rand in [0, exp_threshold), normalized to [0,1) */
+	rand /= exp_threshold;
+
+	/* return int64 random number within between min and max */
+	return min + (int64)((max - min + 1) * rand);
+}
+
+/* random number generator: gaussian distribution from min to max inclusive */
+static int64
+getGaussianrand(TState *thread, int64 min, int64 max, double stdev_threshold)
+{
+	double		stdev;
+	double		rand;
+
+	/*
+	 * Get user specified random number from this loop, with
+	 *    -stdev_threshold < stdev <= stdev_threshold
+	 *
+	 * This loop is executed until the number is in the expected range.
+	 *
+	 * As the minimum threshold is 2.0, the probability of looping is low:
+	 * sqrt(-2 ln(r)) <= 2 => r >= e^{-2} ~ 0.135, then when taking the average
+	 * sinus multiplier as 2/pi, we have a 8.6% looping probability in the
+	 * worst case. For a 5.0 threshold value, the looping proability
+	 * is about e^{-5} * 2 / pi ~ 0.43%.
+	 */
+	do
+	{
+		/*
+		 * pg_erand48 generates [0,1), but for the basic version of the
+		 * Box-Muller transform the two uniformly distributed random numbers
+		 * are expected in (0, 1] (see http://en.wikipedia.org/wiki/Box_muller)
+		 */
+		double rand1 = 1.0 - pg_erand48(thread->random_state);
+		double rand2 = 1.0 - pg_erand48(thread->random_state);
+
+		/* Box-Muller basic form transform */
+		double var_sqrt = sqrt(-2.0 * log(rand1));
+		stdev = var_sqrt * sin(2.0 * M_PI * rand2);
+
+		/* we may try with cos, but there may be a bias induced if the previous
+		 * value fails the test? To be on the safe side, let us try over.
+		 */
+	}
+	while (stdev < -stdev_threshold || stdev >= stdev_threshold);
+
+	/* stdev is in [-threshold, threshold), normalization to [0,1) */
+	rand = (stdev + stdev_threshold) / (stdev_threshold * 2.0);
+
+	/* return int64 random number within between min and max */
+	return min + (int64)((max - min + 1) * rand);
+}
+
 /* call PQexec() and exit() on failure */
 static void
 executeStatement(PGconn *con, const char *sql)
@@ -1307,11 +1475,14 @@ top:
 			fprintf(stderr, "\n");
 		}
 
-		if (pg_strcasecmp(argv[0], "setrandom") == 0)
+		if ((pg_strcasecmp(argv[0], "setrandom") == 0) ||
+			(pg_strcasecmp(argv[0], "setgaussian") == 0) ||
+			(pg_strcasecmp(argv[0], "setexponential") == 0))
 		{
 			char	   *var;
 			int64		min,
 						max;
+			double		threshold = 0;
 			char		res[64];
 
 			if (*argv[2] == ':')
@@ -1357,11 +1528,11 @@ top:
 			}
 
 			/*
-			 * getrand() needs to be able to subtract max from min and add one
-			 * to the result without overflowing.  Since we know max > min, we
-			 * can detect overflow just by checking for a negative result. But
-			 * we must check both that the subtraction doesn't overflow, and
-			 * that adding one to the result doesn't overflow either.
+			 * Generate random number functions need to be able to subtract
+			 * max from min and add one to the result without overflowing.
+			 * Since we know max > min, we can detect overflow just by checking
+			 * for a negative result. But we must check both that the subtraction
+			 * doesn't overflow, and that adding one to the result doesn't overflow either.
 			 */
 			if (max - min < 0 || (max - min) + 1 < 0)
 			{
@@ -1370,10 +1541,55 @@ top:
 				return true;
 			}
 
+			if (pg_strcasecmp(argv[0], "setrandom") == 0)
+			{
+#ifdef DEBUG
+				printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getrand(thread, min, max));
+#endif
+				snprintf(res, sizeof(res), INT64_FORMAT, getrand(thread, min, max));
+			}
+			else if ((pg_strcasecmp(argv[0], "setgaussian") == 0) || (pg_strcasecmp(argv[0], "setexponential") == 0))
+			{
+				if(*argv[4] == ':')
+				{
+					if((var = getVariable(st, argv[4] + 1)) == NULL)
+					{
+						fprintf(stderr, "%s: invalid threshold number %s\n", argv[0], argv[4]);
+						st->ecnt++;
+						return true;
+					}
+					threshold = strtod(var, NULL);
+				}
+				else
+					threshold = strtod(argv[4], NULL);
+
+				if (pg_strcasecmp(argv[0], "setgaussian") == 0)
+				{
+					if (threshold < MIN_GAUSSIAN_THRESHOLD)
+					{
+						fprintf(stderr, "%s: gaussian threshold must be more than 2\n,", argv[4]);
+						st->ecnt++;
+						return true;
+					}
+#ifdef DEBUG
+					printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getGaussianrand(thread, min, max, threshold));
+#endif
+					snprintf(res, sizeof(res), INT64_FORMAT, getGaussianrand(thread, min, max, threshold));
+				}
+				else if (pg_strcasecmp(argv[0], "setexponential") == 0)
+				{
+					if (threshold < MIN_EXPONENTIAL_THRESHOLD)
+					{
+						fprintf(stderr, "%s: exponential threshold must be more than 2\n,", argv[4]);
+						st->ecnt++;
+						return true;
+					}
 #ifdef DEBUG
-			printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getrand(thread, min, max));
+					printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getExponentialrand(thread, min, max, threshold));
 #endif
-			snprintf(res, sizeof(res), INT64_FORMAT, getrand(thread, min, max));
+					snprintf(res, sizeof(res), INT64_FORMAT, getExponentialrand(thread, min, max, threshold));
+				}
+			}
 
 			if (!putVariable(st, argv[0], argv[1], res))
 			{
@@ -1907,6 +2123,19 @@ process_commands(char *buf)
 				fprintf(stderr, "%s: extra argument \"%s\" ignored\n",
 						my_commands->argv[0], my_commands->argv[j]);
 		}
+		else if ((pg_strcasecmp(my_commands->argv[0], "setgaussian") == 0) ||
+			 (pg_strcasecmp(my_commands->argv[0], "setexponential") == 0))
+		{
+			if (my_commands->argc < 5)
+			{
+				fprintf(stderr, "%s: missing argument\n", my_commands->argv[0]);
+				exit(1);
+			}
+
+			for (j = 5; j < my_commands->argc; j++)
+				fprintf(stderr, "%s: extra argument \"%s\" ignored\n",
+						my_commands->argv[0], my_commands->argv[j]);
+		}
 		else if (pg_strcasecmp(my_commands->argv[0], "set") == 0)
 		{
 			if (my_commands->argc < 3)
@@ -2180,16 +2409,57 @@ printResults(int ttype, int normal_xacts, int nclients,
 						(INSTR_TIME_GET_DOUBLE(conn_total_time) / nthreads));
 
 	if (ttype == 0)
-		s = "TPC-B (sort of)";
+	{
+		if (use_gaussian)
+			s = "Gaussian distribution TPC-B (sort of)";
+		else if (use_exponential)
+			s = "Exponential distribution TPC-B (sort of)";
+		else
+			s = "TPC-B (sort of)";
+	}
 	else if (ttype == 2)
-		s = "Update only pgbench_accounts";
+	{
+		if (use_gaussian)
+			s = "Gaussian distribution update only pgbench_accounts";
+		else if (use_exponential)
+			s = "Exponential distribution update only pgbench_accounts";
+		else
+			s = "Update only pgbench_accounts";
+	}
 	else if (ttype == 1)
-		s = "SELECT only";
+	{
+		if (use_gaussian)
+			s = "Gaussian distribution SELECT only";
+		else if (use_exponential)
+			s = "Exponential distribution SELECT only";
+		else
+			s = "SELECT only";
+	}
 	else
 		s = "Custom query";
 
 	printf("transaction type: %s\n", s);
 	printf("scaling factor: %d\n", scale);
+
+	/* output in gaussian distribution benchmark */
+	if (use_gaussian)
+	{
+		printf("standard deviation threshold: %.5f\n", stdev_threshold);
+		printf("access probability of top 20%%, 10%% and 5%% records: %.5f %.5f %.5f\n",
+			(double) ((erf (stdev_threshold * 0.2 / sqrt(2.0))) / (erf (stdev_threshold / sqrt(2.0)))),
+			(double) ((erf (stdev_threshold * 0.1 / sqrt(2.0))) / (erf (stdev_threshold / sqrt(2.0)))),
+			(double) ((erf (stdev_threshold * 0.05 / sqrt(2.0))) / (erf (stdev_threshold / sqrt(2.0)))));
+	}
+	/* output in exponential distribution benchmark  */
+	else if (use_exponential)
+	{
+		printf("exponential threshold: %.5f\n", exp_threshold);
+		printf("access probability of top 20%%, 10%% and 5%% records: %.5f %.5f %.5f\n",
+			(double) (-exp(-exp_threshold * 0.2) + 1),
+			(double) (-exp(-exp_threshold * 0.1) + 1),
+			(double) (-exp(-exp_threshold * 0.05) + 1));
+	}
+
 	printf("query mode: %s\n", QUERYMODE[querymode]);
 	printf("number of clients: %d\n", nclients);
 	printf("number of threads: %d\n", nthreads);
@@ -2319,6 +2589,8 @@ main(int argc, char **argv)
 		{"unlogged-tables", no_argument, &unlogged_tables, 1},
 		{"sampling-rate", required_argument, NULL, 4},
 		{"aggregate-interval", required_argument, NULL, 5},
+		{"gaussian", required_argument, NULL, 6},
+		{"exponential", required_argument, NULL, 7},
 		{"rate", required_argument, NULL, 'R'},
 		{NULL, 0, NULL, 0}
 	};
@@ -2598,8 +2870,28 @@ main(int argc, char **argv)
 				}
 #endif
 				break;
+			case 6:
+				use_gaussian = true;
+				stdev_threshold = atof(optarg);
+				if(stdev_threshold < MIN_GAUSSIAN_THRESHOLD)
+				{
+					fprintf(stderr, "--gaussian=NUM must be more than %f: %f\n",
+							MIN_GAUSSIAN_THRESHOLD, stdev_threshold);
+					exit(1);
+				}
+				break;
+			case 7:
+				use_exponential = true;
+				exp_threshold = atof(optarg);
+				if(exp_threshold < MIN_EXPONENTIAL_THRESHOLD)
+				{
+					fprintf(stderr,	"--exponential=NUM must be more than %f: %f\n",
+							MIN_EXPONENTIAL_THRESHOLD, exp_threshold);
+					exit(1);
+				}
+				break;
 			default:
-				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+				fprintf(stderr, _("Try \"%s --help\" for more information.\n"),	progname);
 				exit(1);
 				break;
 		}
@@ -2795,6 +3087,28 @@ main(int argc, char **argv)
 		}
 	}
 
+	/* set :stdev_threshold variable */
+	if(getVariable(&state[0], "stdev_threshold") == NULL)
+	{
+		snprintf(val, sizeof(val), "%lf", stdev_threshold);
+		for (i = 0; i < nclients; i++)
+		{
+			if (!putVariable(&state[i], "startup", "stdev_threshold", val))
+				exit(1);
+		}
+	}
+
+	/* set :exp_threshold variable */
+	if(getVariable(&state[0], "exp_threshold") == NULL)
+	{
+		snprintf(val, sizeof(val), "%lf", exp_threshold);
+		for (i = 0; i < nclients; i++)
+		{
+			if (!putVariable(&state[i], "startup", "exp_threshold", val))
+				exit(1);
+		}
+	}
+
 	if (!is_no_vacuum)
 	{
 		fprintf(stderr, "starting vacuum...");
@@ -2820,17 +3134,32 @@ main(int argc, char **argv)
 	switch (ttype)
 	{
 		case 0:
-			sql_files[0] = process_builtin(tpc_b);
+			if (use_gaussian)
+				sql_files[0] = process_builtin(gaussian_tpc_b);
+			else if (use_exponential)
+				sql_files[0] = process_builtin(exponential_tpc_b);
+			else
+				sql_files[0] = process_builtin(tpc_b);
 			num_files = 1;
 			break;
 
 		case 1:
-			sql_files[0] = process_builtin(select_only);
+			if (use_gaussian)
+				sql_files[0] = process_builtin(gaussian_select_only);
+			else if (use_exponential)
+				sql_files[0] = process_builtin(exponential_select_only);
+			else
+				sql_files[0] = process_builtin(select_only);
 			num_files = 1;
 			break;
 
 		case 2:
-			sql_files[0] = process_builtin(simple_update);
+			if (use_gaussian)
+				sql_files[0] = process_builtin(gaussian_simple_update);
+			else if (use_exponential)
+				sql_files[0] = process_builtin(exponential_simple_update);
+			else
+				sql_files[0] = process_builtin(simple_update);
 			num_files = 1;
 			break;
 
diff --git a/doc/src/sgml/pgbench.sgml b/doc/src/sgml/pgbench.sgml
index 05ca9b7..3e4bcea 100644
--- a/doc/src/sgml/pgbench.sgml
+++ b/doc/src/sgml/pgbench.sgml
@@ -307,6 +307,22 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
      </varlistentry>
 
      <varlistentry>
+      <term><option>--exponential</option><replaceable>deviation threshold</></term>
+      <listitem>
+       <para>
+         Deviation threshold for the exponential distribution pgbench test.
+         This threshold controls the distribution of accesses on the
+         <structname>pgbench_accounts</> table.
+         The larger the deviation threshold, the more records at the beginning
+         are accessed. The smaller the deviation threshold, the smoother the access
+         pattern distribution. The deviation threshold must be more than 2 for
+         internal performance. When set, this option applies to all test variants
+         (<option>-N</> for skipping updates, or <option>-S</> for selects).
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
       <term><option>-f</option> <replaceable>filename</></term>
       <term><option>--file=</option><replaceable>filename</></term>
       <listitem>
@@ -320,6 +336,22 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
      </varlistentry>
 
      <varlistentry>
+      <term><option>--gaussian</option><replaceable>standard deviation threshold</></term>
+      <listitem>
+       <para>
+         Deviation threshold for the Gaussian distribution pgbench test.
+         This threshold controls the distribution of accesses on the
+         <structname>pgbench_accounts</> table.
+         The larger the deviation threshold, the more records in the middle of the table
+         are accessed. The smaller the deviation threshold, the smoother the access
+         pattern distribution. The deviation threshold must be more than 2 for
+         internal performance. When set, this option applies to all test variants
+         (<option>-N</> for skipping updates, or <option>-S</> for selects).
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
       <term><option>-j</option> <replaceable>threads</></term>
       <term><option>--jobs=</option><replaceable>threads</></term>
       <listitem>
@@ -770,6 +802,59 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
 
    <varlistentry>
     <term>
+     <literal>\setgaussian <replaceable>varname</> <replaceable>min</> <replaceable>max</> <replaceable>
+     standard deviation threshold</literal>
+    </term>
+
+    <listitem>
+     <para>
+      Set variable <replaceable>varname</> to a gaussian random integer value
+      between <replaceable>min</> and <replaceable>max</> bounds inclusive.
+      Each bound can be either an integer constant or a
+      <literal>:</><replaceable>variablename</> reference to a variable
+      having an integer value. The standard deviation threshold controls
+      the distribution pattern. The larger the threshold, the more frequent
+      values close to the middle of the interval are drawn. The threshold is
+      the deviation for the <replaceable>min</> and <replaceable>max</> bounds.
+      The minimum threshold is 2.0, for performance.
+     </para>
+
+     <para>
+      Example:
+<programlisting>
+\setgaussian aid 1 :naccounts 5
+</programlisting></para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+       <term>
+        <literal>\setexponential <replaceable>varname</> <replaceable>min</> <replaceable>max</> <replaceable>
+        deviation threshold</literal>
+       </term>
+
+    <listitem>
+     <para>
+      Set variable <replaceable>varname</> to an exponential random integer value
+      between <replaceable>min</> and <replaceable>max</> bounds inclusive.
+      Each bound can be either an integer constant or a
+      <literal>:</><replaceable>variablename</> reference to a variable
+      having an integer value. The deviation threshold controls the distribution
+      pattern: the larger the deviation threshold, the more frequent values
+      close to <replaceable>min</> are drawn.
+      The minimum threshold is 2.0, for performance.
+     </para>
+
+     <para>
+      Example:
+<programlisting>
+\setexponential aid 1 :naccounts 5
+</programlisting></para>
+     </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term>
      <literal>\sleep <replaceable>number</> [ us | ms | s ]</literal>
     </term>
 
#23Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Fabien COELHO (#22)
Re: gaussian distribution pgbench

Seems that in the review so far, Fabien has focused mainly in the
mathematical properties of the new random number generation. That seems
perfectly fine, but no comment has been made about the chosen UI for the
feature. Per the few initial messages in the thread, in the patch as
submitted you ask for a gaussian random number by using \setgaussian,
and exponential via \setexp. Is this the right UI? Currently you get
an evenly distributed number with \setrandom. There is nothing that
makes it obvious on \setgaussian by itself that it produces random
numbers. Perhaps we should simply add a new argument to \setrandom,
instead of creating new commands for each distribution? I would guess
that, in the future, we're going to want other distributions as well.

Not sure what it would look like; perhaps
\setrandom foo 1 10 gaussian
or
\setrandom foo 1 10 dist=gaussian
or
\setrandom(gaussian) foo 1 10
or
\setrandom(dist=gaussian) foo 1 10

I think we could easily support

\set distrib gaussian
\setrandom(dist=:distrib) foo 1 10

so that it can be changed for a bunch of commands easily.

Or maybe I'm going overboard, everybody else is happy with \setgaussian,
and should just use that?

--
�lvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#24Tom Lane
tgl@sss.pgh.pa.us
In reply to: Alvaro Herrera (#23)
Re: gaussian distribution pgbench

Alvaro Herrera <alvherre@2ndquadrant.com> writes:

Seems that in the review so far, Fabien has focused mainly in the
mathematical properties of the new random number generation. That seems
perfectly fine, but no comment has been made about the chosen UI for the
feature. Per the few initial messages in the thread, in the patch as
submitted you ask for a gaussian random number by using \setgaussian,
and exponential via \setexp. Is this the right UI? Currently you get
an evenly distributed number with \setrandom. There is nothing that
makes it obvious on \setgaussian by itself that it produces random
numbers. Perhaps we should simply add a new argument to \setrandom,
instead of creating new commands for each distribution? I would guess
that, in the future, we're going to want other distributions as well.

+1 for an argument to \setrandom instead of separate commands.

Not sure what it would look like; perhaps
\setrandom foo 1 10 gaussian

FWIW, I think this style is sufficient; the others seem overcomplicated
for not much gain. I'm not strongly attached to that position though.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#25Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Tom Lane (#24)
Re: gaussian distribution pgbench

Hello Alvaro & Tom,

Alvaro Herrera <alvherre@2ndquadrant.com> writes:

Seems that in the review so far, Fabien has focused mainly in the
mathematical properties of the new random number generation. That seems
perfectly fine, but no comment has been made about the chosen UI for the
feature.
Per the few initial messages in the thread, in the patch as submitted
you ask for a gaussian random number by using \setgaussian, and
exponential via \setexp. Is this the right UI?

I thought it would be both concise & clear to have that as another form of
\set*.

If I had it designed from the start, I think I may have put only "\set"
with some functions such as "uniform", "gaussian" and so on. but once
there is a set and a setrandom for uniform, this suggested other settings
would have their own set commands as well. Also, the number of expected
arguments is not the same, so it may make the parsing code less obvious.
Finally, this is not a "language" heavily used, so I would emphasize
simpler code over more elegant features, for once.

Currently you get an evenly distributed number with \setrandom. There
is nothing that makes it obvious on \setgaussian by itself that it
produces random numbers.

Well, "gaussian" or "exp" are kind of a clue, at least to my
mathematically-oriented mind.

Perhaps we should simply add a new argument to \setrandom, instead of
creating new commands for each distribution? I would guess that, in
the future, we're going to want other distributions as well.

+1 for an argument to \setrandom instead of separate commands.

Not sure what it would look like; perhaps
\setrandom foo 1 10 gaussian

There is an additional argument expected. That would make:

\setrandom foo 1 10 [uniform]
\setrandom foo 1 :size gaussian 3.6
\setrandom foo 1 100 exponential 7.2

FWIW, I think this style is sufficient; the others seem overcomplicated
for not much gain. I'm not strongly attached to that position though.

If there is a change, I agree that one simple style is enough, especially
as the parsing code is rather low-level already.

So I'm basically fine with the current status of the patch, but I would
be okay with a \setrandom as well.

--
Fabien.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#26KONDO Mitsumasa
kondo.mitsumasa@lab.ntt.co.jp
In reply to: Fabien COELHO (#25)
Re: gaussian distribution pgbench

(2014/03/02 22:32), Fabien COELHO wrote:

Alvaro Herrera <alvherre@2ndquadrant.com> writes:

Seems that in the review so far, Fabien has focused mainly in the
mathematical properties of the new random number generation. That seems
perfectly fine, but no comment has been made about the chosen UI for the
feature.
Per the few initial messages in the thread, in the patch as submitted you ask
for a gaussian random number by using \setgaussian, and exponential via
\setexp. Is this the right UI?

I thought it would be both concise & clear to have that as another form of \set*.

Yeah, but we got only two or three? concise. So I agree with discussing about UI.

There is an additional argument expected. That would make:

\setrandom foo 1 10 [uniform]
\setrandom foo 1 :size gaussian 3.6
\setrandom foo 1 100 exponential 7.2

It's good design. I think it will become more low overhead at part of parsing in
pgbench, because comparison of strings will be redeced(maybe). And I'd like to
remove [uniform], beacause we have to have compatibility for old scripts, and
random function always gets uniform distribution in common sense of programming.

However, new grammer is little bit long in user script. It seems trade-off that
are visibility of scripts and user writing cost.

Regards,
--
Mitsumasa KONDO
NTT Open Source Software Center

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#27Fabien COELHO
coelho@cri.ensmp.fr
In reply to: KONDO Mitsumasa (#26)
Re: gaussian distribution pgbench

\setrandom foo 1 10 [uniform]
\setrandom foo 1 :size gaussian 3.6
\setrandom foo 1 100 exponential 7.2

It's good design. I think it will become more low overhead at part of parsing
in pgbench, because comparison of strings will be redeced(maybe). And I'd
like to remove [uniform], beacause we have to have compatibility for old
scripts, and random function always gets uniform distribution in common sense
of programming.

I just put "uniform" as an optional default, hence the brackets.

Otherwise, what I would have in mind if this would be designed from
scratch:

\set foo 124
\set foo "string value" (?)
\set foo :variable
\set foo 12 + :shift

And then

\set foo uniform 1 10
\set foo gaussian 1 10 4.2
\set foo exponential 1 100 5.2

or maybe functions could be repended with something like "&uniform".
But that would be for another life:-)

However, new grammer is little bit long in user script. It seems trade-off
that are visibility of scripts and user writing cost.

Yep.

--
Fabien.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#28KONDO Mitsumasa
kondo.mitsumasa@lab.ntt.co.jp
In reply to: Fabien COELHO (#27)
Re: gaussian distribution pgbench

(2014/03/03 16:51), Fabien COELHO wrote:>>> \setrandom foo 1 10 [uniform]

\setrandom foo 1 :size gaussian 3.6
\setrandom foo 1 100 exponential 7.2

It's good design. I think it will become more low overhead at part of parsing
in pgbench, because comparison of strings will be redeced(maybe). And I'd like
to remove [uniform], beacause we have to have compatibility for old scripts,
and random function always gets uniform distribution in common sense of
programming.

I just put "uniform" as an optional default, hence the brackets.

All right. I was misunderstanding. However, if we select this format, I'd like to
remove it. Because pgbench needs to check counts of argment number. If we allow
brackets, it will not be simple.

Otherwise, what I would have in mind if this would be designed from scratch:

\set foo 124
\set foo "string value" (?)
\set foo :variable
\set foo 12 + :shift

And then

\set foo uniform 1 10
\set foo gaussian 1 10 4.2
\set foo exponential 1 100 5.2

or maybe functions could be repended with something like "&uniform".
But that would be for another life:-)

I don't agree with that.. They are more overhead in parsing part and more complex
for user.

However, new grammer is little bit long in user script. It seems trade-off that
are visibility of scripts and user writing cost.

Yep.

OK. I'm not sure which idia is the best. So I wait for comments in community:)

Regards,
--
Mitsumasa KONDO
NTT Open Source Software Center

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#29Fabien COELHO
coelho@cri.ensmp.fr
In reply to: KONDO Mitsumasa (#28)
Re: gaussian distribution pgbench

OK. I'm not sure which idia is the best. So I wait for comments in
community:)

Hmmm. Maybe you can do what Tom voted for, he is the committer:-)

--
Fabien.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#30KONDO Mitsumasa
kondo.mitsumasa@lab.ntt.co.jp
In reply to: Fabien COELHO (#29)
Re: gaussian distribution pgbench

(2014/03/04 17:28), Fabien COELHO wrote:

OK. I'm not sure which idia is the best. So I wait for comments in community:)

Hmmm. Maybe you can do what Tom voted for, he is the committer:-)

Yeah, but he might change his mind by our disscuttion. So I wait untill tomorrow,
and if nothing to comment, I will start to fix what Tom voted for.

Regards,
--
Mitsumasa KONDO
NTT Open Source Software Center

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#31KONDO Mitsumasa
kondo.mitsumasa@lab.ntt.co.jp
In reply to: KONDO Mitsumasa (#30)
1 attachment(s)
Re: gaussian distribution pgbench

Hi,

(2014/03/04 17:42), KONDO Mitsumasa wrote:> (2014/03/04 17:28), Fabien COELHO wrote:

OK. I'm not sure which idia is the best. So I wait for comments in community:)

Hmmm. Maybe you can do what Tom voted for, he is the committer:-)

Yeah, but he might change his mind by our disscuttion. So I wait untill tomorrow,
and if nothing to comment, I will start to fix what Tom voted for.

I create the patch which is fixed UI. If we agree with this interface,
I also start to fix the document.

New "\setrandom" interface is here.
\setrandom var min max [gaussian threshold | exponential threshold]

Attached patch realizes this interface, but it has little bit ugly codeing in
executeStatement() and process_commands().. That is under following.
if(argc == 4)
{
... /* uniform */
}
else if (argv[4]== gaussian or exponential)
{
... /* gaussian or exponential */
}
else
{
... /* uniform with extra argments */
}

It is beacause pgbench custom script allows extra comments or extra argument in
its file. For example, under following cases are no problem case.
\setrandom var min max #hoge --> uniform random
\setrandom var min max #hoge1 #hoge2 --> uniform random
\setrandom var min max gaussian threshold #hoge -->gaussian random

And other cases are classified under following.
\setrandom var min max gaussian #hoge --> uniform
\setrandom var min max max2 gaussian threshold --> uniform
\setrandom var min gaussian #hoge --> ERROR

However, if we wrong grammer in pgbench custom script,
pgbench outputs error log on user terminal. So I think it is especially no problem.

What do you think?

Regards,
--
Mitsumasa KONDO
NTT Open Source Software Center

Attachments:

gaussian_and_exponential_pgbench_v10b.patchtext/x-diff; name=gaussian_and_exponential_pgbench_v10b.patchDownload
*** a/contrib/pgbench/pgbench.c
--- b/contrib/pgbench/pgbench.c
***************
*** 98,103 **** static int	pthread_join(pthread_t th, void **thread_return);
--- 98,106 ----
  #define LOG_STEP_SECONDS	5	/* seconds between log messages */
  #define DEFAULT_NXACTS	10		/* default nxacts */
  
+ #define MIN_GAUSSIAN_THRESHOLD		2.0	/* minimum threshold for gauss */
+ #define MIN_EXPONENTIAL_THRESHOLD	2.0	/* minimum threshold for exp */
+ 
  int			nxacts = 0;			/* number of transactions per client */
  int			duration = 0;		/* duration in seconds */
  
***************
*** 169,174 **** bool		is_connect;			/* establish connection for each transaction */
--- 172,185 ----
  bool		is_latencies;		/* report per-command latencies */
  int			main_pid;			/* main process id used in log filename */
  
+ /* gaussian distribution tests: */
+ double		stdev_threshold;   /* standard deviation threshold */
+ bool        use_gaussian = false;
+ 
+ /* exponential distribution tests: */
+ double		exp_threshold;   /* threshold for exponential */
+ bool		use_exponential = false;
+ 
  char	   *pghost = "";
  char	   *pgport = "";
  char	   *login = NULL;
***************
*** 330,335 **** static char *select_only = {
--- 341,428 ----
  	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
  };
  
+ /* --exponential case */
+ static char *exponential_tpc_b = {
+ 	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+ 	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setrandom aid 1 :naccounts exponential :exp_threshold\n"
+ 	"\\setrandom bid 1 :nbranches\n"
+ 	"\\setrandom tid 1 :ntellers\n"
+ 	"\\setrandom delta -5000 5000\n"
+ 	"BEGIN;\n"
+ 	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ 	"UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;\n"
+ 	"UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;\n"
+ 	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+ 	"END;\n"
+ };
+ 
+ /* --exponential with -N case */
+ static char *exponential_simple_update = {
+ 	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+ 	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setrandom aid 1 :naccounts exponential :exp_threshold\n"
+ 	"\\setrandom bid 1 :nbranches\n"
+ 	"\\setrandom tid 1 :ntellers\n"
+ 	"\\setrandom delta -5000 5000\n"
+ 	"BEGIN;\n"
+ 	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ 	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+ 	"END;\n"
+ };
+ 
+ /* --exponential with -S case */
+ static char *exponential_select_only = {
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setrandom aid 1 :naccounts exponential :exp_threshold\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ };
+ 
+ /* --gaussian case */
+ static char *gaussian_tpc_b = {
+ 	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+ 	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setrandom aid 1 :naccounts gaussian :stdev_threshold\n"
+ 	"\\setrandom bid 1 :nbranches\n"
+ 	"\\setrandom tid 1 :ntellers\n"
+ 	"\\setrandom delta -5000 5000\n"
+ 	"BEGIN;\n"
+ 	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ 	"UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;\n"
+ 	"UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;\n"
+ 	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+ 	"END;\n"
+ };
+ 
+ /* --gaussian with -N case */
+ static char *gaussian_simple_update = {
+ 	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+ 	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setrandom aid 1 :naccounts gaussian :stdev_threshold\n"
+ 	"\\setrandom bid 1 :nbranches\n"
+ 	"\\setrandom tid 1 :ntellers\n"
+ 	"\\setrandom delta -5000 5000\n"
+ 	"BEGIN;\n"
+ 	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ 	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+ 	"END;\n"
+ };
+ 
+ /* --gaussian with -S case */
+ static char *gaussian_select_only = {
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setrandom aid 1 :naccounts gaussian :stdev_threshold\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ };
+ 
  /* Function prototypes */
  static void setalarm(int seconds);
  static void *threadRun(void *arg);
***************
*** 373,378 **** usage(void)
--- 466,473 ----
  		   "  -v, --vacuum-all         vacuum all four standard tables before tests\n"
  		   "  --aggregate-interval=NUM aggregate data over NUM seconds\n"
  		   "  --sampling-rate=NUM      fraction of transactions to log (e.g. 0.01 for 1%%)\n"
+ 		   "  --exponential=NUM        exponential distribution with NUM threshold parameter\n"
+ 		   "  --gaussian=NUM           gaussian distribution with NUM standard deviation threshold\n"
  		   "\nCommon options:\n"
  		   "  -d, --debug              print debugging output\n"
  		   "  -h, --host=HOSTNAME      database server host or socket directory\n"
***************
*** 469,474 **** getrand(TState *thread, int64 min, int64 max)
--- 564,642 ----
  	return min + (int64) ((max - min + 1) * pg_erand48(thread->random_state));
  }
  
+ /* random number generator: exponential distribution from min to max inclusive */
+ static int64
+ getExponentialrand(TState *thread, int64 min, int64 max, double exp_threshold)
+ {
+ 	double		rand;
+ 
+ 	/*
+ 	 * Get user specified random number in this loop. This loop is executed until
+ 	 * the number in the expected range. As the minimum threshold is 2.0, the
+ 	 * probability of a retry is at worst 13.5%  as - ln(0.135) ~ 2.0 ;
+ 	 * For a 5.0 threshold, it is about e^{-5} ~ 0.7%.
+ 	 */
+ 	do
+ 	{
+ 		/* as pg_erand48 is in [0, 1), uniform is in (0, 1] */
+ 		double uniform = 1.0 - pg_erand48(thread->random_state);
+ 		/* rand is in [0 LARGE) */
+ 		rand = - log(uniform);
+ 	} while (rand >= exp_threshold);
+ 
+ 	/* rand in [0, exp_threshold), normalized to [0,1) */
+ 	rand /= exp_threshold;
+ 
+ 	/* return int64 random number within between min and max */
+ 	return min + (int64)((max - min + 1) * rand);
+ }
+ 
+ /* random number generator: gaussian distribution from min to max inclusive */
+ static int64
+ getGaussianrand(TState *thread, int64 min, int64 max, double stdev_threshold)
+ {
+ 	double		stdev;
+ 	double		rand;
+ 
+ 	/*
+ 	 * Get user specified random number from this loop, with
+ 	 *    -stdev_threshold < stdev <= stdev_threshold
+ 	 *
+ 	 * This loop is executed until the number is in the expected range.
+ 	 *
+ 	 * As the minimum threshold is 2.0, the probability of looping is low:
+ 	 * sqrt(-2 ln(r)) <= 2 => r >= e^{-2} ~ 0.135, then when taking the average
+ 	 * sinus multiplier as 2/pi, we have a 8.6% looping probability in the
+ 	 * worst case. For a 5.0 threshold value, the looping proability
+ 	 * is about e^{-5} * 2 / pi ~ 0.43%.
+ 	 */
+ 	do
+ 	{
+ 		/*
+ 		 * pg_erand48 generates [0,1), but for the basic version of the
+ 		 * Box-Muller transform the two uniformly distributed random numbers
+ 		 * are expected in (0, 1] (see http://en.wikipedia.org/wiki/Box_muller)
+ 		 */
+ 		double rand1 = 1.0 - pg_erand48(thread->random_state);
+ 		double rand2 = 1.0 - pg_erand48(thread->random_state);
+ 
+ 		/* Box-Muller basic form transform */
+ 		double var_sqrt = sqrt(-2.0 * log(rand1));
+ 		stdev = var_sqrt * sin(2.0 * M_PI * rand2);
+ 
+ 		/* we may try with cos, but there may be a bias induced if the previous
+ 		 * value fails the test? To be on the safe side, let us try over.
+ 		 */
+ 	}
+ 	while (stdev < -stdev_threshold || stdev >= stdev_threshold);
+ 
+ 	/* stdev is in [-threshold, threshold), normalization to [0,1) */
+ 	rand = (stdev + stdev_threshold) / (stdev_threshold * 2.0);
+ 
+ 	/* return int64 random number within between min and max */
+ 	return min + (int64)((max - min + 1) * rand);
+ }
+ 
  /* call PQexec() and exit() on failure */
  static void
  executeStatement(PGconn *con, const char *sql)
***************
*** 1312,1317 **** top:
--- 1480,1486 ----
  			char	   *var;
  			int64		min,
  						max;
+ 			double		threshold = 0;
  			char		res[64];
  
  			if (*argv[2] == ':')
***************
*** 1357,1367 **** top:
  			}
  
  			/*
! 			 * getrand() needs to be able to subtract max from min and add one
! 			 * to the result without overflowing.  Since we know max > min, we
! 			 * can detect overflow just by checking for a negative result. But
! 			 * we must check both that the subtraction doesn't overflow, and
! 			 * that adding one to the result doesn't overflow either.
  			 */
  			if (max - min < 0 || (max - min) + 1 < 0)
  			{
--- 1526,1536 ----
  			}
  
  			/*
! 			 * Generate random number functions need to be able to subtract
! 			 * max from min and add one to the result without overflowing.
! 			 * Since we know max > min, we can detect overflow just by checking
! 			 * for a negative result. But we must check both that the subtraction
! 			 * doesn't overflow, and that adding one to the result doesn't overflow either.
  			 */
  			if (max - min < 0 || (max - min) + 1 < 0)
  			{
***************
*** 1370,1379 **** top:
  				return true;
  			}
  
  #ifdef DEBUG
! 			printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getrand(thread, min, max));
  #endif
! 			snprintf(res, sizeof(res), INT64_FORMAT, getrand(thread, min, max));
  
  			if (!putVariable(st, argv[0], argv[1], res))
  			{
--- 1539,1601 ----
  				return true;
  			}
  
+ 			if (argc == 4) /* uniform */
+ 			{
  #ifdef DEBUG
! 				printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getrand(thread, min, max));
  #endif
! 				snprintf(res, sizeof(res), INT64_FORMAT, getrand(thread, min, max));
! 			}
! 			else if ((pg_strcasecmp(argv[4], "gaussian") == 0) ||
! 				 (pg_strcasecmp(argv[4], "exponential") == 0))
! 			{
! 				if (*argv[5] == ':')
! 				{
! 					if ((var = getVariable(st, argv[5] + 1)) == NULL)
! 					{
! 						fprintf(stderr, "%s: invalid threshold number %s\n", argv[0], argv[5]);
! 						st->ecnt++;
! 						return true;
! 					}
! 					threshold = strtod(var, NULL);
! 				}
! 				else
! 					threshold = strtod(argv[5], NULL);
! 
! 				if (pg_strcasecmp(argv[4], "gaussian") == 0)
! 				{
! 					if (threshold < MIN_GAUSSIAN_THRESHOLD)
! 					{
! 						fprintf(stderr, "%s: gaussian threshold must be more than %f\n,", argv[5], MIN_GAUSSIAN_THRESHOLD);
! 						st->ecnt++;
! 						return true;
! 					}
! #ifdef DEBUG
! 					printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getGaussianrand(thread, min, max, threshold));
! #endif
! 					snprintf(res, sizeof(res), INT64_FORMAT, getGaussianrand(thread, min, max, threshold));
! 				}
! 				else if (pg_strcasecmp(argv[4], "exponential") == 0)
! 				{
! 					if (threshold < MIN_EXPONENTIAL_THRESHOLD)
! 					{
! 						fprintf(stderr, "%s: exponential threshold must be more than %f\n,", argv[5], MIN_EXPONENTIAL_THRESHOLD);
! 						st->ecnt++;
! 						return true;
! 					}
! #ifdef DEBUG
! 					printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getExponentialrand(thread, min, max, threshold));
! #endif
! 					snprintf(res, sizeof(res), INT64_FORMAT, getExponentialrand(thread, min, max, threshold));
! 				}
! 			}
! 			else /* uniform with extra arguments */
! 			{
! #ifdef DEBUG
! 				printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getrand(thread, min, max));
! #endif
! 				snprintf(res, sizeof(res), INT64_FORMAT, getrand(thread, min, max));
! 			}
  
  			if (!putVariable(st, argv[0], argv[1], res))
  			{
***************
*** 1903,1911 **** process_commands(char *buf)
  				exit(1);
  			}
  
! 			for (j = 4; j < my_commands->argc; j++)
! 				fprintf(stderr, "%s: extra argument \"%s\" ignored\n",
! 						my_commands->argv[0], my_commands->argv[j]);
  		}
  		else if (pg_strcasecmp(my_commands->argv[0], "set") == 0)
  		{
--- 2125,2153 ----
  				exit(1);
  			}
  
! 			if (my_commands->argc == 4) /* uniform */
! 			{
! 				/* nothing to do */
! 			}
! 			else if ((pg_strcasecmp(my_commands->argv[4], "gaussian") == 0) ||
! 				 (pg_strcasecmp(my_commands->argv[4], "exponential") == 0))
! 			{
! 				if (my_commands->argc < 6)
! 				{
! 					fprintf(stderr, "%s(%s): missing argument\n", my_commands->argv[0], my_commands->argv[4]);
! 					exit(1);
! 				}
! 
! 				for (j = 6; j < my_commands->argc; j++)
! 					fprintf(stderr, "%s(%s): extra argument \"%s\" ignored\n",
! 							my_commands->argv[0], my_commands->argv[4], my_commands->argv[j]);
! 			}
! 			else /* uniform with extra argument */
! 			{
! 				for (j = 4; j < my_commands->argc; j++)
! 					fprintf(stderr, "%s(uniform): extra argument \"%s\" ignored\n",
! 							my_commands->argv[0], my_commands->argv[j]);
! 			}
  		}
  		else if (pg_strcasecmp(my_commands->argv[0], "set") == 0)
  		{
***************
*** 2180,2195 **** printResults(int ttype, int normal_xacts, int nclients,
  						(INSTR_TIME_GET_DOUBLE(conn_total_time) / nthreads));
  
  	if (ttype == 0)
! 		s = "TPC-B (sort of)";
  	else if (ttype == 2)
! 		s = "Update only pgbench_accounts";
  	else if (ttype == 1)
! 		s = "SELECT only";
  	else
  		s = "Custom query";
  
  	printf("transaction type: %s\n", s);
  	printf("scaling factor: %d\n", scale);
  	printf("query mode: %s\n", QUERYMODE[querymode]);
  	printf("number of clients: %d\n", nclients);
  	printf("number of threads: %d\n", nthreads);
--- 2422,2478 ----
  						(INSTR_TIME_GET_DOUBLE(conn_total_time) / nthreads));
  
  	if (ttype == 0)
! 	{
! 		if (use_gaussian)
! 			s = "Gaussian distribution TPC-B (sort of)";
! 		else if (use_exponential)
! 			s = "Exponential distribution TPC-B (sort of)";
! 		else
! 			s = "TPC-B (sort of)";
! 	}
  	else if (ttype == 2)
! 	{
! 		if (use_gaussian)
! 			s = "Gaussian distribution update only pgbench_accounts";
! 		else if (use_exponential)
! 			s = "Exponential distribution update only pgbench_accounts";
! 		else
! 			s = "Update only pgbench_accounts";
! 	}
  	else if (ttype == 1)
! 	{
! 		if (use_gaussian)
! 			s = "Gaussian distribution SELECT only";
! 		else if (use_exponential)
! 			s = "Exponential distribution SELECT only";
! 		else
! 			s = "SELECT only";
! 	}
  	else
  		s = "Custom query";
  
  	printf("transaction type: %s\n", s);
  	printf("scaling factor: %d\n", scale);
+ 
+ 	/* output in gaussian distribution benchmark */
+ 	if (use_gaussian)
+ 	{
+ 		printf("standard deviation threshold: %.5f\n", stdev_threshold);
+ 		printf("access probability of top 20%%, 10%% and 5%% records: %.5f %.5f %.5f\n",
+ 			(double) ((erf (stdev_threshold * 0.2 / sqrt(2.0))) / (erf (stdev_threshold / sqrt(2.0)))),
+ 			(double) ((erf (stdev_threshold * 0.1 / sqrt(2.0))) / (erf (stdev_threshold / sqrt(2.0)))),
+ 			(double) ((erf (stdev_threshold * 0.05 / sqrt(2.0))) / (erf (stdev_threshold / sqrt(2.0)))));
+ 	}
+ 	/* output in exponential distribution benchmark  */
+ 	else if (use_exponential)
+ 	{
+ 		printf("exponential threshold: %.5f\n", exp_threshold);
+ 		printf("access probability of top 20%%, 10%% and 5%% records: %.5f %.5f %.5f\n",
+ 			(double) (-exp(-exp_threshold * 0.2) + 1),
+ 			(double) (-exp(-exp_threshold * 0.1) + 1),
+ 			(double) (-exp(-exp_threshold * 0.05) + 1));
+ 	}
+ 
  	printf("query mode: %s\n", QUERYMODE[querymode]);
  	printf("number of clients: %d\n", nclients);
  	printf("number of threads: %d\n", nthreads);
***************
*** 2319,2324 **** main(int argc, char **argv)
--- 2602,2609 ----
  		{"unlogged-tables", no_argument, &unlogged_tables, 1},
  		{"sampling-rate", required_argument, NULL, 4},
  		{"aggregate-interval", required_argument, NULL, 5},
+ 		{"gaussian", required_argument, NULL, 6},
+ 		{"exponential", required_argument, NULL, 7},
  		{"rate", required_argument, NULL, 'R'},
  		{NULL, 0, NULL, 0}
  	};
***************
*** 2598,2605 **** main(int argc, char **argv)
  				}
  #endif
  				break;
  			default:
! 				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
  				exit(1);
  				break;
  		}
--- 2883,2910 ----
  				}
  #endif
  				break;
+ 			case 6:
+ 				use_gaussian = true;
+ 				stdev_threshold = atof(optarg);
+ 				if(stdev_threshold < MIN_GAUSSIAN_THRESHOLD)
+ 				{
+ 					fprintf(stderr, "--gaussian=NUM must be more than %f: %f\n",
+ 							MIN_GAUSSIAN_THRESHOLD, stdev_threshold);
+ 					exit(1);
+ 				}
+ 				break;
+ 			case 7:
+ 				use_exponential = true;
+ 				exp_threshold = atof(optarg);
+ 				if(exp_threshold < MIN_EXPONENTIAL_THRESHOLD)
+ 				{
+ 					fprintf(stderr,	"--exponential=NUM must be more than %f: %f\n",
+ 							MIN_EXPONENTIAL_THRESHOLD, exp_threshold);
+ 					exit(1);
+ 				}
+ 				break;
  			default:
! 				fprintf(stderr, _("Try \"%s --help\" for more information.\n"),	progname);
  				exit(1);
  				break;
  		}
***************
*** 2795,2800 **** main(int argc, char **argv)
--- 3100,3127 ----
  		}
  	}
  
+ 	/* set :stdev_threshold variable */
+ 	if(getVariable(&state[0], "stdev_threshold") == NULL)
+ 	{
+ 		snprintf(val, sizeof(val), "%lf", stdev_threshold);
+ 		for (i = 0; i < nclients; i++)
+ 		{
+ 			if (!putVariable(&state[i], "startup", "stdev_threshold", val))
+ 				exit(1);
+ 		}
+ 	}
+ 
+ 	/* set :exp_threshold variable */
+ 	if(getVariable(&state[0], "exp_threshold") == NULL)
+ 	{
+ 		snprintf(val, sizeof(val), "%lf", exp_threshold);
+ 		for (i = 0; i < nclients; i++)
+ 		{
+ 			if (!putVariable(&state[i], "startup", "exp_threshold", val))
+ 				exit(1);
+ 		}
+ 	}
+ 
  	if (!is_no_vacuum)
  	{
  		fprintf(stderr, "starting vacuum...");
***************
*** 2820,2836 **** main(int argc, char **argv)
  	switch (ttype)
  	{
  		case 0:
! 			sql_files[0] = process_builtin(tpc_b);
  			num_files = 1;
  			break;
  
  		case 1:
! 			sql_files[0] = process_builtin(select_only);
  			num_files = 1;
  			break;
  
  		case 2:
! 			sql_files[0] = process_builtin(simple_update);
  			num_files = 1;
  			break;
  
--- 3147,3178 ----
  	switch (ttype)
  	{
  		case 0:
! 			if (use_gaussian)
! 				sql_files[0] = process_builtin(gaussian_tpc_b);
! 			else if (use_exponential)
! 				sql_files[0] = process_builtin(exponential_tpc_b);
! 			else
! 				sql_files[0] = process_builtin(tpc_b);
  			num_files = 1;
  			break;
  
  		case 1:
! 			if (use_gaussian)
! 				sql_files[0] = process_builtin(gaussian_select_only);
! 			else if (use_exponential)
! 				sql_files[0] = process_builtin(exponential_select_only);
! 			else
! 				sql_files[0] = process_builtin(select_only);
  			num_files = 1;
  			break;
  
  		case 2:
! 			if (use_gaussian)
! 				sql_files[0] = process_builtin(gaussian_simple_update);
! 			else if (use_exponential)
! 				sql_files[0] = process_builtin(exponential_simple_update);
! 			else
! 				sql_files[0] = process_builtin(simple_update);
  			num_files = 1;
  			break;
  
*** a/doc/src/sgml/pgbench.sgml
--- b/doc/src/sgml/pgbench.sgml
***************
*** 307,312 **** pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
--- 307,328 ----
       </varlistentry>
  
       <varlistentry>
+       <term><option>--exponential</option><replaceable>deviation threshold</></term>
+       <listitem>
+        <para>
+          Deviation threshold for the exponential distribution pgbench test.
+          This threshold controls the distribution of accesses on the
+          <structname>pgbench_accounts</> table.
+          The larger the deviation threshold, the more records at the beginning
+          are accessed. The smaller the deviation threshold, the smoother the access
+          pattern distribution. The deviation threshold must be more than 2 for
+          internal performance. When set, this option applies to all test variants
+          (<option>-N</> for skipping updates, or <option>-S</> for selects).
+        </para>
+       </listitem>
+      </varlistentry>
+ 
+      <varlistentry>
        <term><option>-f</option> <replaceable>filename</></term>
        <term><option>--file=</option><replaceable>filename</></term>
        <listitem>
***************
*** 320,325 **** pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
--- 336,357 ----
       </varlistentry>
  
       <varlistentry>
+       <term><option>--gaussian</option><replaceable>standard deviation threshold</></term>
+       <listitem>
+        <para>
+          Deviation threshold for the Gaussian distribution pgbench test.
+          This threshold controls the distribution of accesses on the
+          <structname>pgbench_accounts</> table.
+          The larger the deviation threshold, the more records in the middle of the table
+          are accessed. The smaller the deviation threshold, the smoother the access
+          pattern distribution. The deviation threshold must be more than 2 for
+          internal performance. When set, this option applies to all test variants
+          (<option>-N</> for skipping updates, or <option>-S</> for selects).
+        </para>
+       </listitem>
+      </varlistentry>
+ 
+      <varlistentry>
        <term><option>-j</option> <replaceable>threads</></term>
        <term><option>--jobs=</option><replaceable>threads</></term>
        <listitem>
***************
*** 770,775 **** pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
--- 802,860 ----
  
     <varlistentry>
      <term>
+      <literal>\setgaussian <replaceable>varname</> <replaceable>min</> <replaceable>max</> <replaceable>
+      standard deviation threshold</literal>
+     </term>
+ 
+     <listitem>
+      <para>
+       Set variable <replaceable>varname</> to a gaussian random integer value
+       between <replaceable>min</> and <replaceable>max</> bounds inclusive.
+       Each bound can be either an integer constant or a
+       <literal>:</><replaceable>variablename</> reference to a variable
+       having an integer value. The standard deviation threshold controls
+       the distribution pattern. The larger the threshold, the more frequent
+       values close to the middle of the interval are drawn. The threshold is
+       the deviation for the <replaceable>min</> and <replaceable>max</> bounds.
+       The minimum threshold is 2.0, for performance.
+      </para>
+ 
+      <para>
+       Example:
+ <programlisting>
+ \setgaussian aid 1 :naccounts 5
+ </programlisting></para>
+     </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+        <term>
+         <literal>\setexponential <replaceable>varname</> <replaceable>min</> <replaceable>max</> <replaceable>
+         deviation threshold</literal>
+        </term>
+ 
+     <listitem>
+      <para>
+       Set variable <replaceable>varname</> to an exponential random integer value
+       between <replaceable>min</> and <replaceable>max</> bounds inclusive.
+       Each bound can be either an integer constant or a
+       <literal>:</><replaceable>variablename</> reference to a variable
+       having an integer value. The deviation threshold controls the distribution
+       pattern: the larger the deviation threshold, the more frequent values
+       close to <replaceable>min</> are drawn.
+       The minimum threshold is 2.0, for performance.
+      </para>
+ 
+      <para>
+       Example:
+ <programlisting>
+ \setexponential aid 1 :naccounts 5
+ </programlisting></para>
+      </listitem>
+    </varlistentry>
+ 
+    <varlistentry>
+     <term>
       <literal>\sleep <replaceable>number</> [ us | ms | s ]</literal>
      </term>
  
#32KONDO Mitsumasa
kondo.mitsumasa@lab.ntt.co.jp
In reply to: KONDO Mitsumasa (#31)
Re: gaussian distribution pgbench

(2014/03/07 16:02), KONDO Mitsumasa wrote:

And other cases are classified under following.
\setrandom var min max gaussian #hoge --> uniform

Oh, it's wrong... It will be..
\setrandom var min max gaussian #hoge --> ERROR

Regards,
--
Mitsumasa KONDO
NTT Open Source Software Center

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#33Fabien COELHO
coelho@cri.ensmp.fr
In reply to: KONDO Mitsumasa (#31)
Re: gaussian distribution pgbench

Hello Mitsumasa-san,

New "\setrandom" interface is here.
\setrandom var min max [gaussian threshold | exponential threshold]

Attached patch realizes this interface, but it has little bit ugly codeing in
executeStatement() and process_commands()..

I think it is not too bad. The "ignore extra arguments on the line" is a
little pre-existing mess anyway.

What do you think?

I'm okay with this UI and its implementation.

--
Fabien.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#34KONDO Mitsumasa
kondo.mitsumasa@lab.ntt.co.jp
In reply to: Fabien COELHO (#33)
1 attachment(s)
Re: gaussian distribution pgbench

(2014/03/09 1:49), Fabien COELHO wrote:

Hello Mitsumasa-san,

New "\setrandom" interface is here.
\setrandom var min max [gaussian threshold | exponential threshold]

Attached patch realizes this interface, but it has little bit ugly codeing in
executeStatement() and process_commands()..

I think it is not too bad. The "ignore extra arguments on the line" is a little
pre-existing mess anyway.

All right.

What do you think?

I'm okay with this UI and its implementation.

OK.

Attached patch is updated in the document. I don't like complex sentence,
so I use <para> tag a lot. If you like this documents, please mark ready for
commiter.

Regards,
--
Mitsumasa KONDO
NTT Open Source Software Center

Attachments:

gaussian_and_exponential_pgbench_v10doc.patchtext/x-diff; name=gaussian_and_exponential_pgbench_v10doc.patchDownload
*** a/contrib/pgbench/pgbench.c
--- b/contrib/pgbench/pgbench.c
***************
*** 98,103 **** static int	pthread_join(pthread_t th, void **thread_return);
--- 98,106 ----
  #define LOG_STEP_SECONDS	5	/* seconds between log messages */
  #define DEFAULT_NXACTS	10		/* default nxacts */
  
+ #define MIN_GAUSSIAN_THRESHOLD		2.0	/* minimum threshold for gauss */
+ #define MIN_EXPONENTIAL_THRESHOLD	2.0	/* minimum threshold for exp */
+ 
  int			nxacts = 0;			/* number of transactions per client */
  int			duration = 0;		/* duration in seconds */
  
***************
*** 169,174 **** bool		is_connect;			/* establish connection for each transaction */
--- 172,185 ----
  bool		is_latencies;		/* report per-command latencies */
  int			main_pid;			/* main process id used in log filename */
  
+ /* gaussian distribution tests: */
+ double		stdev_threshold;   /* standard deviation threshold */
+ bool        use_gaussian = false;
+ 
+ /* exponential distribution tests: */
+ double		exp_threshold;   /* threshold for exponential */
+ bool		use_exponential = false;
+ 
  char	   *pghost = "";
  char	   *pgport = "";
  char	   *login = NULL;
***************
*** 330,335 **** static char *select_only = {
--- 341,428 ----
  	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
  };
  
+ /* --exponential case */
+ static char *exponential_tpc_b = {
+ 	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+ 	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setrandom aid 1 :naccounts exponential :exp_threshold\n"
+ 	"\\setrandom bid 1 :nbranches\n"
+ 	"\\setrandom tid 1 :ntellers\n"
+ 	"\\setrandom delta -5000 5000\n"
+ 	"BEGIN;\n"
+ 	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ 	"UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;\n"
+ 	"UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;\n"
+ 	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+ 	"END;\n"
+ };
+ 
+ /* --exponential with -N case */
+ static char *exponential_simple_update = {
+ 	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+ 	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setrandom aid 1 :naccounts exponential :exp_threshold\n"
+ 	"\\setrandom bid 1 :nbranches\n"
+ 	"\\setrandom tid 1 :ntellers\n"
+ 	"\\setrandom delta -5000 5000\n"
+ 	"BEGIN;\n"
+ 	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ 	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+ 	"END;\n"
+ };
+ 
+ /* --exponential with -S case */
+ static char *exponential_select_only = {
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setrandom aid 1 :naccounts exponential :exp_threshold\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ };
+ 
+ /* --gaussian case */
+ static char *gaussian_tpc_b = {
+ 	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+ 	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setrandom aid 1 :naccounts gaussian :stdev_threshold\n"
+ 	"\\setrandom bid 1 :nbranches\n"
+ 	"\\setrandom tid 1 :ntellers\n"
+ 	"\\setrandom delta -5000 5000\n"
+ 	"BEGIN;\n"
+ 	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ 	"UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;\n"
+ 	"UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;\n"
+ 	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+ 	"END;\n"
+ };
+ 
+ /* --gaussian with -N case */
+ static char *gaussian_simple_update = {
+ 	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+ 	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setrandom aid 1 :naccounts gaussian :stdev_threshold\n"
+ 	"\\setrandom bid 1 :nbranches\n"
+ 	"\\setrandom tid 1 :ntellers\n"
+ 	"\\setrandom delta -5000 5000\n"
+ 	"BEGIN;\n"
+ 	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ 	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+ 	"END;\n"
+ };
+ 
+ /* --gaussian with -S case */
+ static char *gaussian_select_only = {
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setrandom aid 1 :naccounts gaussian :stdev_threshold\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ };
+ 
  /* Function prototypes */
  static void setalarm(int seconds);
  static void *threadRun(void *arg);
***************
*** 373,378 **** usage(void)
--- 466,473 ----
  		   "  -v, --vacuum-all         vacuum all four standard tables before tests\n"
  		   "  --aggregate-interval=NUM aggregate data over NUM seconds\n"
  		   "  --sampling-rate=NUM      fraction of transactions to log (e.g. 0.01 for 1%%)\n"
+ 		   "  --exponential=NUM        exponential distribution with NUM threshold parameter\n"
+ 		   "  --gaussian=NUM           gaussian distribution with NUM standard deviation threshold\n"
  		   "\nCommon options:\n"
  		   "  -d, --debug              print debugging output\n"
  		   "  -h, --host=HOSTNAME      database server host or socket directory\n"
***************
*** 469,474 **** getrand(TState *thread, int64 min, int64 max)
--- 564,642 ----
  	return min + (int64) ((max - min + 1) * pg_erand48(thread->random_state));
  }
  
+ /* random number generator: exponential distribution from min to max inclusive */
+ static int64
+ getExponentialrand(TState *thread, int64 min, int64 max, double exp_threshold)
+ {
+ 	double		rand;
+ 
+ 	/*
+ 	 * Get user specified random number in this loop. This loop is executed until
+ 	 * the number in the expected range. As the minimum threshold is 2.0, the
+ 	 * probability of a retry is at worst 13.5% as - ln(0.135) ~ 2.0 ;
+ 	 * For a 5.0 threshold, it is about e^{-5} ~ 0.7%.
+ 	 */
+ 	do
+ 	{
+ 		/* as pg_erand48 is in [0, 1), uniform is in (0, 1] */
+ 		double uniform = 1.0 - pg_erand48(thread->random_state);
+ 		/* rand is in [0 LARGE) */
+ 		rand = - log(uniform);
+ 	} while (rand >= exp_threshold);
+ 
+ 	/* rand in [0, exp_threshold), normalized to [0,1) */
+ 	rand /= exp_threshold;
+ 
+ 	/* return int64 random number within between min and max */
+ 	return min + (int64)((max - min + 1) * rand);
+ }
+ 
+ /* random number generator: gaussian distribution from min to max inclusive */
+ static int64
+ getGaussianrand(TState *thread, int64 min, int64 max, double stdev_threshold)
+ {
+ 	double		stdev;
+ 	double		rand;
+ 
+ 	/*
+ 	 * Get user specified random number from this loop, with
+ 	 * -stdev_threshold < stdev <= stdev_threshold
+ 	 *
+ 	 * This loop is executed until the number is in the expected range.
+ 	 *
+ 	 * As the minimum threshold is 2.0, the probability of looping is low:
+ 	 * sqrt(-2 ln(r)) <= 2 => r >= e^{-2} ~ 0.135, then when taking the average
+ 	 * sinus multiplier as 2/pi, we have a 8.6% looping probability in the
+ 	 * worst case. For a 5.0 threshold value, the looping proability
+ 	 * is about e^{-5} * 2 / pi ~ 0.43%.
+ 	 */
+ 	do
+ 	{
+ 		/*
+ 		 * pg_erand48 generates [0,1), but for the basic version of the
+ 		 * Box-Muller transform the two uniformly distributed random numbers
+ 		 * are expected in (0, 1] (see http://en.wikipedia.org/wiki/Box_muller)
+ 		 */
+ 		double rand1 = 1.0 - pg_erand48(thread->random_state);
+ 		double rand2 = 1.0 - pg_erand48(thread->random_state);
+ 
+ 		/* Box-Muller basic form transform */
+ 		double var_sqrt = sqrt(-2.0 * log(rand1));
+ 		stdev = var_sqrt * sin(2.0 * M_PI * rand2);
+ 
+ 		/* we may try with cos, but there may be a bias induced if the previous
+ 		 * value fails the test? To be on the safe side, let us try over.
+ 		 */
+ 	}
+ 	while (stdev < -stdev_threshold || stdev >= stdev_threshold);
+ 
+ 	/* stdev is in [-threshold, threshold), normalization to [0,1) */
+ 	rand = (stdev + stdev_threshold) / (stdev_threshold * 2.0);
+ 
+ 	/* return int64 random number within between min and max */
+ 	return min + (int64)((max - min + 1) * rand);
+ }
+ 
  /* call PQexec() and exit() on failure */
  static void
  executeStatement(PGconn *con, const char *sql)
***************
*** 1312,1317 **** top:
--- 1480,1486 ----
  			char	   *var;
  			int64		min,
  						max;
+ 			double		threshold = 0;
  			char		res[64];
  
  			if (*argv[2] == ':')
***************
*** 1357,1367 **** top:
  			}
  
  			/*
! 			 * getrand() needs to be able to subtract max from min and add one
! 			 * to the result without overflowing.  Since we know max > min, we
! 			 * can detect overflow just by checking for a negative result. But
! 			 * we must check both that the subtraction doesn't overflow, and
! 			 * that adding one to the result doesn't overflow either.
  			 */
  			if (max - min < 0 || (max - min) + 1 < 0)
  			{
--- 1526,1536 ----
  			}
  
  			/*
! 			 * Generate random number functions need to be able to subtract
! 			 * max from min and add one to the result without overflowing.
! 			 * Since we know max > min, we can detect overflow just by checking
! 			 * for a negative result. But we must check both that the subtraction
! 			 * doesn't overflow, and that adding one to the result doesn't overflow either.
  			 */
  			if (max - min < 0 || (max - min) + 1 < 0)
  			{
***************
*** 1370,1379 **** top:
  				return true;
  			}
  
  #ifdef DEBUG
! 			printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getrand(thread, min, max));
  #endif
! 			snprintf(res, sizeof(res), INT64_FORMAT, getrand(thread, min, max));
  
  			if (!putVariable(st, argv[0], argv[1], res))
  			{
--- 1539,1601 ----
  				return true;
  			}
  
+ 			if (argc == 4) /* uniform */
+ 			{
  #ifdef DEBUG
! 				printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getrand(thread, min, max));
  #endif
! 				snprintf(res, sizeof(res), INT64_FORMAT, getrand(thread, min, max));
! 			}
! 			else if ((pg_strcasecmp(argv[4], "gaussian") == 0) ||
! 				 (pg_strcasecmp(argv[4], "exponential") == 0))
! 			{
! 				if (*argv[5] == ':')
! 				{
! 					if ((var = getVariable(st, argv[5] + 1)) == NULL)
! 					{
! 						fprintf(stderr, "%s: invalid threshold number %s\n", argv[0], argv[5]);
! 						st->ecnt++;
! 						return true;
! 					}
! 					threshold = strtod(var, NULL);
! 				}
! 				else
! 					threshold = strtod(argv[5], NULL);
! 
! 				if (pg_strcasecmp(argv[4], "gaussian") == 0)
! 				{
! 					if (threshold < MIN_GAUSSIAN_THRESHOLD)
! 					{
! 						fprintf(stderr, "%s: gaussian threshold must be more than %f\n,", argv[5], MIN_GAUSSIAN_THRESHOLD);
! 						st->ecnt++;
! 						return true;
! 					}
! #ifdef DEBUG
! 					printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getGaussianrand(thread, min, max, threshold));
! #endif
! 					snprintf(res, sizeof(res), INT64_FORMAT, getGaussianrand(thread, min, max, threshold));
! 				}
! 				else if (pg_strcasecmp(argv[4], "exponential") == 0)
! 				{
! 					if (threshold < MIN_EXPONENTIAL_THRESHOLD)
! 					{
! 						fprintf(stderr, "%s: exponential threshold must be more than %f\n,", argv[5], MIN_EXPONENTIAL_THRESHOLD);
! 						st->ecnt++;
! 						return true;
! 					}
! #ifdef DEBUG
! 					printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getExponentialrand(thread, min, max, threshold));
! #endif
! 					snprintf(res, sizeof(res), INT64_FORMAT, getExponentialrand(thread, min, max, threshold));
! 				}
! 			}
! 			else /* uniform with extra arguments */
! 			{
! #ifdef DEBUG
! 				printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getrand(thread, min, max));
! #endif
! 				snprintf(res, sizeof(res), INT64_FORMAT, getrand(thread, min, max));
! 			}
  
  			if (!putVariable(st, argv[0], argv[1], res))
  			{
***************
*** 1903,1911 **** process_commands(char *buf)
  				exit(1);
  			}
  
! 			for (j = 4; j < my_commands->argc; j++)
! 				fprintf(stderr, "%s: extra argument \"%s\" ignored\n",
! 						my_commands->argv[0], my_commands->argv[j]);
  		}
  		else if (pg_strcasecmp(my_commands->argv[0], "set") == 0)
  		{
--- 2125,2153 ----
  				exit(1);
  			}
  
! 			if (my_commands->argc == 4) /* uniform */
! 			{
! 				/* nothing to do */
! 			}
! 			else if ((pg_strcasecmp(my_commands->argv[4], "gaussian") == 0) ||
! 				 (pg_strcasecmp(my_commands->argv[4], "exponential") == 0))
! 			{
! 				if (my_commands->argc < 6)
! 				{
! 					fprintf(stderr, "%s(%s): missing argument\n", my_commands->argv[0], my_commands->argv[4]);
! 					exit(1);
! 				}
! 
! 				for (j = 6; j < my_commands->argc; j++)
! 					fprintf(stderr, "%s(%s): extra argument \"%s\" ignored\n",
! 							my_commands->argv[0], my_commands->argv[4], my_commands->argv[j]);
! 			}
! 			else /* uniform with extra argument */
! 			{
! 				for (j = 4; j < my_commands->argc; j++)
! 					fprintf(stderr, "%s(uniform): extra argument \"%s\" ignored\n",
! 							my_commands->argv[0], my_commands->argv[j]);
! 			}
  		}
  		else if (pg_strcasecmp(my_commands->argv[0], "set") == 0)
  		{
***************
*** 2180,2195 **** printResults(int ttype, int normal_xacts, int nclients,
  						(INSTR_TIME_GET_DOUBLE(conn_total_time) / nthreads));
  
  	if (ttype == 0)
! 		s = "TPC-B (sort of)";
  	else if (ttype == 2)
! 		s = "Update only pgbench_accounts";
  	else if (ttype == 1)
! 		s = "SELECT only";
  	else
  		s = "Custom query";
  
  	printf("transaction type: %s\n", s);
  	printf("scaling factor: %d\n", scale);
  	printf("query mode: %s\n", QUERYMODE[querymode]);
  	printf("number of clients: %d\n", nclients);
  	printf("number of threads: %d\n", nthreads);
--- 2422,2478 ----
  						(INSTR_TIME_GET_DOUBLE(conn_total_time) / nthreads));
  
  	if (ttype == 0)
! 	{
! 		if (use_gaussian)
! 			s = "Gaussian distribution TPC-B (sort of)";
! 		else if (use_exponential)
! 			s = "Exponential distribution TPC-B (sort of)";
! 		else
! 			s = "TPC-B (sort of)";
! 	}
  	else if (ttype == 2)
! 	{
! 		if (use_gaussian)
! 			s = "Gaussian distribution update only pgbench_accounts";
! 		else if (use_exponential)
! 			s = "Exponential distribution update only pgbench_accounts";
! 		else
! 			s = "Update only pgbench_accounts";
! 	}
  	else if (ttype == 1)
! 	{
! 		if (use_gaussian)
! 			s = "Gaussian distribution SELECT only";
! 		else if (use_exponential)
! 			s = "Exponential distribution SELECT only";
! 		else
! 			s = "SELECT only";
! 	}
  	else
  		s = "Custom query";
  
  	printf("transaction type: %s\n", s);
  	printf("scaling factor: %d\n", scale);
+ 
+ 	/* output in gaussian distribution benchmark */
+ 	if (use_gaussian)
+ 	{
+ 		printf("standard deviation threshold: %.5f\n", stdev_threshold);
+ 		printf("access probability of top 20%%, 10%% and 5%% records: %.5f %.5f %.5f\n",
+ 			(double) ((erf (stdev_threshold * 0.2 / sqrt(2.0))) / (erf (stdev_threshold / sqrt(2.0)))),
+ 			(double) ((erf (stdev_threshold * 0.1 / sqrt(2.0))) / (erf (stdev_threshold / sqrt(2.0)))),
+ 			(double) ((erf (stdev_threshold * 0.05 / sqrt(2.0))) / (erf (stdev_threshold / sqrt(2.0)))));
+ 	}
+ 	/* output in exponential distribution benchmark */
+ 	else if (use_exponential)
+ 	{
+ 		printf("exponential threshold: %.5f\n", exp_threshold);
+ 		printf("access probability of top 20%%, 10%% and 5%% records: %.5f %.5f %.5f\n",
+ 			(double) (-exp(-exp_threshold * 0.2) + 1),
+ 			(double) (-exp(-exp_threshold * 0.1) + 1),
+ 			(double) (-exp(-exp_threshold * 0.05) + 1));
+ 	}
+ 
  	printf("query mode: %s\n", QUERYMODE[querymode]);
  	printf("number of clients: %d\n", nclients);
  	printf("number of threads: %d\n", nthreads);
***************
*** 2319,2324 **** main(int argc, char **argv)
--- 2602,2609 ----
  		{"unlogged-tables", no_argument, &unlogged_tables, 1},
  		{"sampling-rate", required_argument, NULL, 4},
  		{"aggregate-interval", required_argument, NULL, 5},
+ 		{"gaussian", required_argument, NULL, 6},
+ 		{"exponential", required_argument, NULL, 7},
  		{"rate", required_argument, NULL, 'R'},
  		{NULL, 0, NULL, 0}
  	};
***************
*** 2598,2605 **** main(int argc, char **argv)
  				}
  #endif
  				break;
  			default:
! 				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
  				exit(1);
  				break;
  		}
--- 2883,2910 ----
  				}
  #endif
  				break;
+ 			case 6:
+ 				use_gaussian = true;
+ 				stdev_threshold = atof(optarg);
+ 				if(stdev_threshold < MIN_GAUSSIAN_THRESHOLD)
+ 				{
+ 					fprintf(stderr, "--gaussian=NUM must be more than %f: %f\n",
+ 							MIN_GAUSSIAN_THRESHOLD, stdev_threshold);
+ 					exit(1);
+ 				}
+ 				break;
+ 			case 7:
+ 				use_exponential = true;
+ 				exp_threshold = atof(optarg);
+ 				if(exp_threshold < MIN_EXPONENTIAL_THRESHOLD)
+ 				{
+ 					fprintf(stderr,	"--exponential=NUM must be more than %f: %f\n",
+ 							MIN_EXPONENTIAL_THRESHOLD, exp_threshold);
+ 					exit(1);
+ 				}
+ 				break;
  			default:
! 				fprintf(stderr, _("Try \"%s --help\" for more information.\n"),	progname);
  				exit(1);
  				break;
  		}
***************
*** 2795,2800 **** main(int argc, char **argv)
--- 3100,3127 ----
  		}
  	}
  
+ 	/* set :stdev_threshold variable */
+ 	if(getVariable(&state[0], "stdev_threshold") == NULL)
+ 	{
+ 		snprintf(val, sizeof(val), "%lf", stdev_threshold);
+ 		for (i = 0; i < nclients; i++)
+ 		{
+ 			if (!putVariable(&state[i], "startup", "stdev_threshold", val))
+ 				exit(1);
+ 		}
+ 	}
+ 
+ 	/* set :exp_threshold variable */
+ 	if(getVariable(&state[0], "exp_threshold") == NULL)
+ 	{
+ 		snprintf(val, sizeof(val), "%lf", exp_threshold);
+ 		for (i = 0; i < nclients; i++)
+ 		{
+ 			if (!putVariable(&state[i], "startup", "exp_threshold", val))
+ 				exit(1);
+ 		}
+ 	}
+ 
  	if (!is_no_vacuum)
  	{
  		fprintf(stderr, "starting vacuum...");
***************
*** 2820,2836 **** main(int argc, char **argv)
  	switch (ttype)
  	{
  		case 0:
! 			sql_files[0] = process_builtin(tpc_b);
  			num_files = 1;
  			break;
  
  		case 1:
! 			sql_files[0] = process_builtin(select_only);
  			num_files = 1;
  			break;
  
  		case 2:
! 			sql_files[0] = process_builtin(simple_update);
  			num_files = 1;
  			break;
  
--- 3147,3178 ----
  	switch (ttype)
  	{
  		case 0:
! 			if (use_gaussian)
! 				sql_files[0] = process_builtin(gaussian_tpc_b);
! 			else if (use_exponential)
! 				sql_files[0] = process_builtin(exponential_tpc_b);
! 			else
! 				sql_files[0] = process_builtin(tpc_b);
  			num_files = 1;
  			break;
  
  		case 1:
! 			if (use_gaussian)
! 				sql_files[0] = process_builtin(gaussian_select_only);
! 			else if (use_exponential)
! 				sql_files[0] = process_builtin(exponential_select_only);
! 			else
! 				sql_files[0] = process_builtin(select_only);
  			num_files = 1;
  			break;
  
  		case 2:
! 			if (use_gaussian)
! 				sql_files[0] = process_builtin(gaussian_simple_update);
! 			else if (use_exponential)
! 				sql_files[0] = process_builtin(exponential_simple_update);
! 			else
! 				sql_files[0] = process_builtin(simple_update);
  			num_files = 1;
  			break;
  
*** a/doc/src/sgml/pgbench.sgml
--- b/doc/src/sgml/pgbench.sgml
***************
*** 307,312 **** pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
--- 307,328 ----
       </varlistentry>
  
       <varlistentry>
+       <term><option>--exponential</option><replaceable>deviation threshold</></term>
+       <listitem>
+        <para>
+          Deviation threshold for the exponential distribution pgbench test.
+          This threshold controls the distribution of accesses on the
+          <structname>pgbench_accounts</> table.
+          The larger the deviation threshold, the more records at the beginning
+          are accessed. The smaller the deviation threshold, the smoother the access
+          pattern distribution. The deviation threshold must be more than 2 for
+          internal performance. When set, this option applies to all test variants
+          (<option>-N</> for skipping updates, or <option>-S</> for selects).
+        </para>
+       </listitem>
+      </varlistentry>
+ 
+      <varlistentry>
        <term><option>-f</option> <replaceable>filename</></term>
        <term><option>--file=</option><replaceable>filename</></term>
        <listitem>
***************
*** 320,325 **** pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
--- 336,357 ----
       </varlistentry>
  
       <varlistentry>
+       <term><option>--gaussian</option><replaceable>standard deviation threshold</></term>
+       <listitem>
+        <para>
+          Deviation threshold for the Gaussian distribution pgbench test.
+          This threshold controls the distribution of accesses on the
+          <structname>pgbench_accounts</> table.
+          The larger the deviation threshold, the more records in the middle of the table
+          are accessed. The smaller the deviation threshold, the smoother the access
+          pattern distribution. The deviation threshold must be more than 2 for
+          internal performance. When set, this option applies to all test variants
+          (<option>-N</> for skipping updates, or <option>-S</> for selects).
+        </para>
+       </listitem>
+      </varlistentry>
+ 
+      <varlistentry>
        <term><option>-j</option> <replaceable>threads</></term>
        <term><option>--jobs=</option><replaceable>threads</></term>
        <listitem>
***************
*** 748,755 **** pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
  
     <varlistentry>
      <term>
!      <literal>\setrandom <replaceable>varname</> <replaceable>min</> <replaceable>max</></literal>
!     </term>
  
      <listitem>
       <para>
--- 780,787 ----
  
     <varlistentry>
      <term>
!      <literal>\setrandom <replaceable>varname</> <replaceable>min</> <replaceable>max</> [ { gaussian | exponential } <replaceable>threshold</> ]</literal>
!      </term>
  
      <listitem>
       <para>
***************
*** 761,769 **** pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
       </para>
  
       <para>
        Example:
  <programlisting>
! \setrandom aid 1 :naccounts
  </programlisting></para>
      </listitem>
     </varlistentry>
--- 793,823 ----
       </para>
  
       <para>
+       Moreover, set gaussian or exponential with threshold interger value,
+       we can get gaussian or exponential random in integer value between
+       <replaceable>min</> and <replaceable>max</> bounds inclusive.
+       The threshold controls the distribution pattern. Without these options,
+       we can get uniform random in interger value between <replaceable>min</>
+       and <replaceable>max</> bounds inclusive.
+      </para>
+ 
+      <para>
+       In gaussian option, the larger the threshold, the more frequent values
+       close to the middle of the interval are drawn. The threshold is
+       the deviation for the <replaceable>min</> and <replaceable>max</> bounds.
+       The minimum threshold is 2.0, for performance.
+      </para>
+ 
+      <para>
+       In exponential option, the deviation, threshold controls the distribution
+       pattern: the larger the deviation threshold, the more frequent values
+       close to <replaceable>min</> are drawn. The minimum threshold is 2.0, for performance.
+      </para>
+ 
+      <para>
        Example:
  <programlisting>
! \setrandom aid 1 :naccounts gaussian 5
  </programlisting></para>
      </listitem>
     </varlistentry>
#35Fujii Masao
masao.fujii@gmail.com
In reply to: KONDO Mitsumasa (#34)
Re: gaussian distribution pgbench

On Tue, Mar 11, 2014 at 1:49 PM, KONDO Mitsumasa
<kondo.mitsumasa@lab.ntt.co.jp> wrote:

(2014/03/09 1:49), Fabien COELHO wrote:

Hello Mitsumasa-san,

New "\setrandom" interface is here.
\setrandom var min max [gaussian threshold | exponential threshold]

Attached patch realizes this interface, but it has little bit ugly
codeing in
executeStatement() and process_commands()..

I think it is not too bad. The "ignore extra arguments on the line" is a
little
pre-existing mess anyway.

All right.

What do you think?

I'm okay with this UI and its implementation.

OK.

We should do the same discussion for the UI of command-line option?
The patch adds two options --gaussian and --exponential, but this UI
seems to be a bit inconsistent with the UI for \setrandom. Instead,
we can use something like --distribution=[uniform | gaussian | exponential].

Regards,

--
Fujii Masao

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#36Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Fujii Masao (#35)
Re: gaussian distribution pgbench

On 03/13/2014 03:17 PM, Fujii Masao wrote:

On Tue, Mar 11, 2014 at 1:49 PM, KONDO Mitsumasa
<kondo.mitsumasa@lab.ntt.co.jp> wrote:

(2014/03/09 1:49), Fabien COELHO wrote:

I'm okay with this UI and its implementation.

OK.

We should do the same discussion for the UI of command-line option?
The patch adds two options --gaussian and --exponential, but this UI
seems to be a bit inconsistent with the UI for \setrandom. Instead,
we can use something like --distribution=[uniform | gaussian | exponential].

IMHO we should just implement the \setrandom changes, and not add any of
these options to modify the standard test workload. If someone wants to
run TPC-B workload with gaussian or exponential distribution, they can
implement it as a custom script. The docs include the script for the
standard TPC-B workload; just copy-paster that and modify the \setrandom
lines.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#37Fujii Masao
masao.fujii@gmail.com
In reply to: Heikki Linnakangas (#36)
Re: gaussian distribution pgbench

On Thu, Mar 13, 2014 at 10:51 PM, Heikki Linnakangas
<hlinnakangas@vmware.com> wrote:

On 03/13/2014 03:17 PM, Fujii Masao wrote:

On Tue, Mar 11, 2014 at 1:49 PM, KONDO Mitsumasa
<kondo.mitsumasa@lab.ntt.co.jp> wrote:

(2014/03/09 1:49), Fabien COELHO wrote:

I'm okay with this UI and its implementation.

OK.

We should do the same discussion for the UI of command-line option?
The patch adds two options --gaussian and --exponential, but this UI
seems to be a bit inconsistent with the UI for \setrandom. Instead,
we can use something like --distribution=[uniform | gaussian |
exponential].

IMHO we should just implement the \setrandom changes, and not add any of
these options to modify the standard test workload. If someone wants to run
TPC-B workload with gaussian or exponential distribution, they can implement
it as a custom script. The docs include the script for the standard TPC-B
workload; just copy-paster that and modify the \setrandom lines.

Yeah, I'm OK with this.

Regards,

--
Fujii Masao

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#38Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Fujii Masao (#35)
Re: gaussian distribution pgbench

We should do the same discussion for the UI of command-line option? The
patch adds two options --gaussian and --exponential, but this UI seems
to be a bit inconsistent with the UI for \setrandom.
Instead, we can use something like --distribution=[uniform | gaussian |
exponential].

Hmmm. That is possible, obviously.

Note that it does not need to resort to a custom script, if one can do
something like "--define=exp_threshold=5.6". If so, maybe one simpler
named variable could be used, say "threshold", instead of separate names
for each options.

However there is a catch: currently the option allows to check that the
threshold is large enough so as to avoid loops in the generator. So this
mean moving the check in the generator, and doing it over and over.
Possibly this is a good idea, because otherwise a custom script could
circumvent the check. Well, the current status is that the check can be
avoided with --define...

Also, a shorter possibly additional name, would be nice, maybe something
like: --dist=exp|gauss|uniform? Not sure. I like long options not to be
too long.

--
Fabien.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#39KONDO Mitsumasa
kondo.mitsumasa@lab.ntt.co.jp
In reply to: Fabien COELHO (#38)
Re: gaussian distribution pgbench

Hi,

(2014/03/14 4:21), Fabien COELHO wrote:

We should do the same discussion for the UI of command-line option? The patch
adds two options --gaussian and --exponential, but this UI seems to be a bit
inconsistent with the UI for \setrandom.
Instead, we can use something like --distribution=[uniform | gaussian |
exponential].

Hmmm. That is possible, obviously.

Note that it does not need to resort to a custom script, if one can do something
like "--define=exp_threshold=5.6".

Yeah, threshold paramter should be needed by generating distribution algorithms
in my patch. And it is important that we can control distribution pattern by this
paramter.

If so, maybe one simpler named variable could
be used, say "threshold", instead of separate names for each options.

If we separate threshold option, I think it is difficult to understand dependency
of this parameter. Because "threshold" is very general term, and
when we will add other new feature, it is difficult to undestand which parameter
is dependent and be needed.

However there is a catch: currently the option allows to check that the threshold
is large enough so as to avoid loops in the generator. So this mean moving the
check in the generator, and doing it over and over. Possibly this is a good idea,
because otherwise a custom script could circumvent the check. Well, the current
status is that the check can be avoided with --define...

Also, a shorter possibly additional name, would be nice, maybe something like:
--dist=exp|gauss|uniform? Not sure. I like long options not to be too long.

Well, if we run standard benchmark in pgbench, we need not set option because it
is default benmchmark, and it is same as uniform distribution. And if we run
extra benchmarks in pgbench which are like '-S' or '-N', we need to set option.
Because they are non-standard benchmark setting, and it is same as gaussian or
exponential distribution. So present UI keeps consistency and along the pgbench
history.

I like long options not to be too long.

Yes, I like so too. Present UI is very simple and useful for combination using
such like '-S' and '--gaussian'. So I hope not changing UI.

ex)
pgbench -S --gaussian=5
pgbench -N --exponential=2 --sampling-rate=0.8

Regards,
--
Mitsumasa KONDO
NTT Open Source Software Center

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#40KONDO Mitsumasa
kondo.mitsumasa@lab.ntt.co.jp
In reply to: Fujii Masao (#37)
Re: gaussian distribution pgbench

(2014/03/13 23:00), Fujii Masao wrote:

On Thu, Mar 13, 2014 at 10:51 PM, Heikki Linnakangas
<hlinnakangas@vmware.com> wrote:

On 03/13/2014 03:17 PM, Fujii Masao wrote:

On Tue, Mar 11, 2014 at 1:49 PM, KONDO Mitsumasa
<kondo.mitsumasa@lab.ntt.co.jp> wrote:

(2014/03/09 1:49), Fabien COELHO wrote:

I'm okay with this UI and itsaccess probability of top implementation.

OK.

We should do the same discussion for the UI of command-line option?
The patch adds two options --gaussian and --exponential, but this UI
seems to be a bit inconsistent with the UI for \setrandom. Instead,
we can use something like --distribution=[uniform | gaussian |
exponential].

IMHO we should just implement the \setrandom changes, and not add any of
these options to modify the standard test workload. If someone wants to run
TPC-B workload with gaussian or exponential distribution, they can implement
it as a custom script. The docs include the script for the standard TPC-B
workload; just copy-paster that and modify the \setrandom lines.

Well, when we set '--gaussian=NUM' or '--exponential=NUM' on command line, we can
see access probability of top N records in result of final output. This out put
is under following,

[mitsu-ko@localhost pgbench]$ ./pgbench --exponential=10 postgres
starting vacuum...end.
transaction type: Exponential distribution TPC-B (sort of)
scaling factor: 1
exponential threshold: 10.00000
access probability of top 20%, 10% and 5% records: 0.86466 0.63212 0.39347
~

This feature helps user to understand bias of distribution for tuning threshold
parameter.
If this feature is nothing, it is difficult to understand distribution of access
pattern, and it cannot realized on custom script. Because range of distribution
(min, max, and SQL pattern) are unknown on custom script. So I think present UI
is not bad and should not change.

Regards,
--
Mitsumasa KONDO
NTT Open Source Software Center

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#41Fabien COELHO
coelho@cri.ensmp.fr
In reply to: KONDO Mitsumasa (#40)
Re: gaussian distribution pgbench

Well, when we set '--gaussian=NUM' or '--exponential=NUM' on command line, we
can see access probability of top N records in result of final output. This
out put is under following,

Indeed. I had forgotten this point. This is a significant information that
I would not like to loose.

This feature helps user to understand bias of distribution for tuning
threshold parameter.
If this feature is nothing, it is difficult to understand distribution of
access pattern, and it cannot realized on custom script. Because range of
distribution (min, max, and SQL pattern) are unknown on custom script. So I
think present UI is not bad and should not change.

Ok. I agree with this argument.

--
Fabien.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#42Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Fujii Masao (#37)
1 attachment(s)
Re: gaussian distribution pgbench

On 03/13/2014 04:00 PM, Fujii Masao wrote:

On Thu, Mar 13, 2014 at 10:51 PM, Heikki Linnakangas
<hlinnakangas@vmware.com> wrote:

IMHO we should just implement the \setrandom changes, and not add any of
these options to modify the standard test workload. If someone wants to run
TPC-B workload with gaussian or exponential distribution, they can implement
it as a custom script. The docs include the script for the standard TPC-B
workload; just copy-paster that and modify the \setrandom lines.

Yeah, I'm OK with this.

So I took a look at the \setrandom parts of this patch to see if that's
ready for commit, without any of the changes to modify the standard
TPC-B workload. Attached is a patch with just those parts; everyone
please focus on this.

A couple of comments:

* There should be an explicit "\setrandom ... uniform" option too, even
though you get that implicitly if you don't specify the distribution

* What exactly does the "threshold" mean? The docs informally explain
that "the larger the thresold, the more frequent values close to the
middle of the interval are drawn", but that's pretty vague.

* Does min and max really make sense for gaussian and exponential
distributions? For gaussian, I would expect mean and standard deviation
as the parameters, not min/max/threshold.

* How about setting the variable as a float instead of integer? Would
seem more natural to me. At least as an option.

- Heikki

Attachments:

gaussian_and_exponential_pgbench_v11doc.patchtext/x-diff; name=gaussian_and_exponential_pgbench_v11doc.patchDownload
diff --git a/contrib/pgbench/pgbench.c b/contrib/pgbench/pgbench.c
index 7c1e59e..a7713af 100644
--- a/contrib/pgbench/pgbench.c
+++ b/contrib/pgbench/pgbench.c
@@ -98,6 +98,9 @@ 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 MIN_GAUSSIAN_THRESHOLD		2.0	/* minimum threshold for gauss */
+#define MIN_EXPONENTIAL_THRESHOLD	2.0	/* minimum threshold for exp */
+
 int			nxacts = 0;			/* number of transactions per client */
 int			duration = 0;		/* duration in seconds */
 
@@ -469,6 +472,79 @@ getrand(TState *thread, int64 min, int64 max)
 	return min + (int64) ((max - min + 1) * pg_erand48(thread->random_state));
 }
 
+/* random number generator: exponential distribution from min to max inclusive */
+static int64
+getExponentialrand(TState *thread, int64 min, int64 max, double exp_threshold)
+{
+	double		rand;
+
+	/*
+	 * Get user specified random number in this loop. This loop is executed until
+	 * the number in the expected range. As the minimum threshold is 2.0, the
+	 * probability of a retry is at worst 13.5% as - ln(0.135) ~ 2.0 ;
+	 * For a 5.0 threshold, it is about e^{-5} ~ 0.7%.
+	 */
+	do
+	{
+		/* as pg_erand48 is in [0, 1), uniform is in (0, 1] */
+		double uniform = 1.0 - pg_erand48(thread->random_state);
+		/* rand is in [0 LARGE) */
+		rand = - log(uniform);
+	} while (rand >= exp_threshold);
+
+	/* rand in [0, exp_threshold), normalized to [0,1) */
+	rand /= exp_threshold;
+
+	/* return int64 random number within between min and max */
+	return min + (int64)((max - min + 1) * rand);
+}
+
+/* random number generator: gaussian distribution from min to max inclusive */
+static int64
+getGaussianrand(TState *thread, int64 min, int64 max, double stdev_threshold)
+{
+	double		stdev;
+	double		rand;
+
+	/*
+	 * Get user specified random number from this loop, with
+	 * -stdev_threshold < stdev <= stdev_threshold
+	 *
+	 * This loop is executed until the number is in the expected range.
+	 *
+	 * As the minimum threshold is 2.0, the probability of looping is low:
+	 * sqrt(-2 ln(r)) <= 2 => r >= e^{-2} ~ 0.135, then when taking the average
+	 * sinus multiplier as 2/pi, we have a 8.6% looping probability in the
+	 * worst case. For a 5.0 threshold value, the looping proability
+	 * is about e^{-5} * 2 / pi ~ 0.43%.
+	 */
+	do
+	{
+		/*
+		 * pg_erand48 generates [0,1), but for the basic version of the
+		 * Box-Muller transform the two uniformly distributed random numbers
+		 * are expected in (0, 1] (see http://en.wikipedia.org/wiki/Box_muller)
+		 */
+		double rand1 = 1.0 - pg_erand48(thread->random_state);
+		double rand2 = 1.0 - pg_erand48(thread->random_state);
+
+		/* Box-Muller basic form transform */
+		double var_sqrt = sqrt(-2.0 * log(rand1));
+		stdev = var_sqrt * sin(2.0 * M_PI * rand2);
+
+		/* we may try with cos, but there may be a bias induced if the previous
+		 * value fails the test? To be on the safe side, let us try over.
+		 */
+	}
+	while (stdev < -stdev_threshold || stdev >= stdev_threshold);
+
+	/* stdev is in [-threshold, threshold), normalization to [0,1) */
+	rand = (stdev + stdev_threshold) / (stdev_threshold * 2.0);
+
+	/* return int64 random number within between min and max */
+	return min + (int64)((max - min + 1) * rand);
+}
+
 /* call PQexec() and exit() on failure */
 static void
 executeStatement(PGconn *con, const char *sql)
@@ -1312,6 +1388,7 @@ top:
 			char	   *var;
 			int64		min,
 						max;
+			double		threshold = 0;
 			char		res[64];
 
 			if (*argv[2] == ':')
@@ -1357,7 +1434,7 @@ top:
 			}
 
 			/*
-			 * getrand() needs to be able to subtract max from min and add one
+			 * Random number generation functions need to be able to subtract max from min and add one
 			 * to the result without overflowing.  Since we know max > min, we
 			 * can detect overflow just by checking for a negative result. But
 			 * we must check both that the subtraction doesn't overflow, and
@@ -1370,10 +1447,63 @@ top:
 				return true;
 			}
 
+			if (argc == 4) /* uniform */
+			{
 #ifdef DEBUG
-			printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getrand(thread, min, max));
+				printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getrand(thread, min, max));
 #endif
-			snprintf(res, sizeof(res), INT64_FORMAT, getrand(thread, min, max));
+				snprintf(res, sizeof(res), INT64_FORMAT, getrand(thread, min, max));
+			}
+			else if ((pg_strcasecmp(argv[4], "gaussian") == 0) ||
+				 (pg_strcasecmp(argv[4], "exponential") == 0))
+			{
+				if (*argv[5] == ':')
+				{
+					if ((var = getVariable(st, argv[5] + 1)) == NULL)
+					{
+						fprintf(stderr, "%s: invalid threshold number %s\n", argv[0], argv[5]);
+						st->ecnt++;
+						return true;
+					}
+					threshold = strtod(var, NULL);
+				}
+				else
+					threshold = strtod(argv[5], NULL);
+
+				if (pg_strcasecmp(argv[4], "gaussian") == 0)
+				{
+					if (threshold < MIN_GAUSSIAN_THRESHOLD)
+					{
+						fprintf(stderr, "%s: gaussian threshold must be more than %f\n,", argv[5], MIN_GAUSSIAN_THRESHOLD);
+						st->ecnt++;
+						return true;
+					}
+#ifdef DEBUG
+					printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getGaussianrand(thread, min, max, threshold));
+#endif
+					snprintf(res, sizeof(res), INT64_FORMAT, getGaussianrand(thread, min, max, threshold));
+				}
+				else if (pg_strcasecmp(argv[4], "exponential") == 0)
+				{
+					if (threshold < MIN_EXPONENTIAL_THRESHOLD)
+					{
+						fprintf(stderr, "%s: exponential threshold must be more than %f\n,", argv[5], MIN_EXPONENTIAL_THRESHOLD);
+						st->ecnt++;
+						return true;
+					}
+#ifdef DEBUG
+					printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getExponentialrand(thread, min, max, threshold));
+#endif
+					snprintf(res, sizeof(res), INT64_FORMAT, getExponentialrand(thread, min, max, threshold));
+				}
+			}
+			else /* uniform with extra arguments */
+			{
+#ifdef DEBUG
+				printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getrand(thread, min, max));
+#endif
+				snprintf(res, sizeof(res), INT64_FORMAT, getrand(thread, min, max));
+			}
 
 			if (!putVariable(st, argv[0], argv[1], res))
 			{
@@ -1903,9 +2033,29 @@ process_commands(char *buf)
 				exit(1);
 			}
 
-			for (j = 4; j < my_commands->argc; j++)
-				fprintf(stderr, "%s: extra argument \"%s\" ignored\n",
-						my_commands->argv[0], my_commands->argv[j]);
+			if (my_commands->argc == 4) /* uniform */
+			{
+				/* nothing to do */
+			}
+			else if ((pg_strcasecmp(my_commands->argv[4], "gaussian") == 0) ||
+				 (pg_strcasecmp(my_commands->argv[4], "exponential") == 0))
+			{
+				if (my_commands->argc < 6)
+				{
+					fprintf(stderr, "%s(%s): missing argument\n", my_commands->argv[0], my_commands->argv[4]);
+					exit(1);
+				}
+
+				for (j = 6; j < my_commands->argc; j++)
+					fprintf(stderr, "%s(%s): extra argument \"%s\" ignored\n",
+							my_commands->argv[0], my_commands->argv[4], my_commands->argv[j]);
+			}
+			else /* uniform with extra argument */
+			{
+				for (j = 4; j < my_commands->argc; j++)
+					fprintf(stderr, "%s(uniform): extra argument \"%s\" ignored\n",
+							my_commands->argv[0], my_commands->argv[j]);
+			}
 		}
 		else if (pg_strcasecmp(my_commands->argv[0], "set") == 0)
 		{
@@ -2319,7 +2469,7 @@ main(int argc, char **argv)
 		{"unlogged-tables", no_argument, &unlogged_tables, 1},
 		{"sampling-rate", required_argument, NULL, 4},
 		{"aggregate-interval", required_argument, NULL, 5},
-		{"rate", required_argument, NULL, 'R'},
+		{"gaussian", required_argument, NULL, 6},
 		{NULL, 0, NULL, 0}
 	};
 
@@ -2599,7 +2749,7 @@ main(int argc, char **argv)
 #endif
 				break;
 			default:
-				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+				fprintf(stderr, _("Try \"%s --help\" for more information.\n"),	progname);
 				exit(1);
 				break;
 		}
diff --git a/doc/src/sgml/pgbench.sgml b/doc/src/sgml/pgbench.sgml
index 4367563..48e3641 100644
--- a/doc/src/sgml/pgbench.sgml
+++ b/doc/src/sgml/pgbench.sgml
@@ -748,8 +748,8 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
 
    <varlistentry>
     <term>
-     <literal>\setrandom <replaceable>varname</> <replaceable>min</> <replaceable>max</></literal>
-    </term>
+     <literal>\setrandom <replaceable>varname</> <replaceable>min</> <replaceable>max</> [ { gaussian | exponential } <replaceable>threshold</> ]</literal>
+     </term>
 
     <listitem>
      <para>
@@ -761,9 +761,31 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
      </para>
 
      <para>
+      Moreover, set gaussian or exponential with threshold interger value,
+      we can get gaussian or exponential random in integer value between
+      <replaceable>min</> and <replaceable>max</> bounds inclusive.
+      The threshold controls the distribution pattern. Without these options,
+      we can get uniform random in interger value between <replaceable>min</>
+      and <replaceable>max</> bounds inclusive.
+     </para>
+
+     <para>
+      In gaussian option, the larger the threshold, the more frequent values
+      close to the middle of the interval are drawn. The threshold is
+      the deviation for the <replaceable>min</> and <replaceable>max</> bounds.
+      The minimum threshold is 2.0, for performance.
+     </para>
+
+     <para>
+      In exponential option, the deviation, threshold controls the distribution
+      pattern: the larger the deviation threshold, the more frequent values
+      close to <replaceable>min</> are drawn. The minimum threshold is 2.0, for performance.
+     </para>
+
+     <para>
       Example:
 <programlisting>
-\setrandom aid 1 :naccounts
+\setrandom aid 1 :naccounts gaussian 5
 </programlisting></para>
     </listitem>
    </varlistentry>
#43Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Heikki Linnakangas (#42)
Re: gaussian distribution pgbench

Hello Heikki,

A couple of comments:

* There should be an explicit "\setrandom ... uniform" option too, even
though you get that implicitly if you don't specify the distribution

Indeed. I agree. I suggested it, but it got lost.

* What exactly does the "threshold" mean? The docs informally explain that
"the larger the thresold, the more frequent values close to the middle of the
interval are drawn", but that's pretty vague.

There are explanations and computations as comments in the code. If it is
about the documentation, I'm not sure that a very precise mathematical
definition will help a lot of people, and might rather hinder
understanding, so the doc focuses on an intuitive explanation instead.

* Does min and max really make sense for gaussian and exponential
distributions? For gaussian, I would expect mean and standard deviation as
the parameters, not min/max/threshold.

Yes... and no:-) The aim is to draw an integer primary key from a table,
so it must be in a specified range. This is approximated by drawing a
double value with the expected distribution (gaussian or exponential) and
project it carefully onto integers. If it is out of range, there is a loop
and another value is drawn. The minimal threshold constraint (2.0) ensures
that the probability of looping is low.

* How about setting the variable as a float instead of integer? Would seem
more natural to me. At least as an option.

Which variable? The values set by setrandom are mostly used for primary
keys. We really want integers in a range.

--
Fabien.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#44Mitsumasa KONDO
kondo.mitsumasa@gmail.com
In reply to: Fabien COELHO (#43)
1 attachment(s)
Re: gaussian distribution pgbench

Hi

2014-03-15 15:53 GMT+09:00 Fabien COELHO <coelho@cri.ensmp.fr>:

Hello Heikki,

A couple of comments:

* There should be an explicit "\setrandom ... uniform" option too, even
though you get that implicitly if you don't specify the distribution

Indeed. I agree. I suggested it, but it got lost.

OK. If we keep to the SQL grammar, your saying is right. I will add it.

* What exactly does the "threshold" mean? The docs informally explain

that "the larger the thresold, the more frequent values close to the middle
of the interval are drawn", but that's pretty vague.

There are explanations and computations as comments in the code. If it is
about the documentation, I'm not sure that a very precise mathematical
definition will help a lot of people, and might rather hinder
understanding, so the doc focuses on an intuitive explanation instead.

Yeah, I think that we had better to only explain necessary infomation for
using this feature. If we add mathematical theory in docs, it will be too
difficult for user. And it's waste.

* Does min and max really make sense for gaussian and exponential

distributions? For gaussian, I would expect mean and standard deviation as
the parameters, not min/max/threshold.

Yes... and no:-) The aim is to draw an integer primary key from a table,
so it must be in a specified range. This is approximated by drawing a
double value with the expected distribution (gaussian or exponential) and
project it carefully onto integers. If it is out of range, there is a loop
and another value is drawn. The minimal threshold constraint (2.0) ensures
that the probability of looping is low.

I think it is difficult to understand from our text... So I create picture
that will help you to understand it.
Please see it.

* How about setting the variable as a float instead of integer? Would

seem more natural to me. At least as an option.

Which variable? The values set by setrandom are mostly used for primary
keys. We really want integers in a range.

I think he said threshold parameter. Threshold parameter is very sensitive
parameter, so we need to set double in threshold. I think that you can
consent it when you see attached picture.

regards,
--
Mitsumasa KONDO
NTT Open Source Software Center

Attachments:

explain_gaussian_and_exponential_generating_algorithm2.pdfapplication/pdf; name=explain_gaussian_and_exponential_generating_algorithm2.pdfDownload
%PDF-1.3
%�����������
4 0 obj
<< /Length 5 0 R /Filter /FlateDecode >>
stream
x+TT(T02Q0P0�4Q0�4U(JUW�S�H-JN-()M�Q(�*�!H
XCr���g���K>��@�l�
endstream
endobj
5 0 obj
71
endobj
2 0 obj
<< /Type /Page /Parent 3 0 R /Resources 6 0 R /Contents 4 0 R /MediaBox [0 0 842 595]
>>
endobj
6 0 obj
<< /ProcSet [ /PDF /ImageB /ImageC /ImageI ] /XObject << /Im1 7 0 R >> >>
endobj
7 0 obj
<< /Length 8 0 R /Type /XObject /Subtype /Image /Width 794 /Height 595 /Interpolate
true /ColorSpace 9 0 R /SMask 10 0 R /BitsPerComponent 8 /Filter /FlateDecode
>>
stream
x��|UE��U�um��m����uu�����**%�	�P!$�T��B�)$!�zGz�R������'�����soa����9s��f���<�s�����Y,��E�"`�X,��E�"`�X,��E�"`�X,��E�"`�X,��E�"`�X,��E�"`�X,��E�"`�X,��E�"`�X,��E�"`�X,��E�"`�X,��E�"`�X,��E�"`�X,��E�"`�X,��E�"`�X,��E�"`�X,��E�"`�X,��E�"`�X,��E�"`�X,��E�"`�X,��E�"`�X,��E�"`�X,��E�"`(�,Z��3��b��R����Y�z����[_�m/R����#����E�P�D6l�*��[w���F�2d���_/�z���?��c��.Cs����	�LL\\=��������c��9r��jyk����e V�ZU2];z�h�n�z����{�Q�F������l���i��~��i����@BB�e�9VzZu�d������s�?��S�z�5�\D9WS������={L�#F�������m�/U��;���i��7�H�6m����/���?_~��_}��G�=zH�<n��d��v�����}����)���e�@Q�7���kW	Tg�8���2����J������?���R����7���u������[���>��e�!������i�����K���-[��w�)V���3�������)))�;&L�HN�&����+WJ��//��w�}���*U��i)�m�v��W���/:3<x����'�Z�j��r��.L����
�����[�.M��?�����Z`XX��#��t�������$1s�Lw
������+�87n\��+W�,k�s�����������o���d��XJ�N�:�����@�0�Xs�UN�y���s��\�f�T�_��,�T��I������K^6�����'��s�6g�n�����h��Mz���.l3l���""�{�I�w�^Y�O>��D��-_�<)Io��@�G�?�m���������;4���<���y���h��������G���<2����,iii�,����yv����U��:�	��k�������k��S'88�s�)������kW��/�\�~}�dn��4�,}��%�c���#9P�~���&M!�p���/���q�!%���N����}�]�v����z�-�'N��'NH��������O�N
>|���'��&cDDD�z��f�8��Q�����F�m�50j&
�S2����+l������]x���X���(-4��]6q.()Qr�����0`�N^�����=k�,L�C�E�0���\V$�C���C��=�n�j����!�����_��u����
�����_�j��abb���3f��GAAA"�>��S����l�������_2^�sI������o�o����LbX��
M���u�X��+������?�����w���e�HC��J%����@|6X���GJ�m�1��E�?����a��a��*1#n��Q������Y��@7o�,y�'/�33Y�T�\���$;u1��i9�dP<�����KA��w���Ef`�l����{������O���B&�����c���>`�L�.\H��H4���c���S�����\���4�5�d(�	������L~i��3g�D>p�aD�e:H��IIF�b�,��&M����(j3fL�f����'O6�K�����,��5k~����O������x��i������B^afd������2i�Y4o�������5i��a-��c����T1E��������@!���k�G�e�,��,df,�=��
���T�p3Xx��_x��q�&���7����#������?�x0!�w�����������f���3�������m��������4����A�:�B&K.�N�g���r/[�$����q�%�\�TV�D.��(��M��^�L�""Q��ae��%��r�d�o6qgk����dtfA�U�R��I�n�����3�M7�d�lJn!f�?��D?�\��������+���{���������d/��	�����H�_���vFm��^@�s�=R��]�qd��W�P�5��E�;K��o~c�^fR:�k��A�<���.FgM��E�N�	�>�&�����*�r&����-���w�y�3���<f����V�Tj�5g�Is���"�%q��)A�w�J	�0�8��<������2�K�S� �7�8q���o���O8f�d�cg����u�]Gv�� -�&����\�4P��R5���#.%�! R�X`Y�b�dw���7�p�i�0w8�$���������+�C����+M��
�����0!�vp��������yJ!��"��%�':'�D29��kb���������~y��ZF����&��$6��FBS��T�����j�?&��z��18���������=K��aE@��&����gp��{�n��]n1�ab&W��Vr��P2$� �W���]&?Y<�a5��+`:(	���)��9,
p���](cp�g�����������M,��_�h2������gO��r�%�l�1'Df��tb�H.Y�����o�vq��%T
~c�29I@.�c�+�ME��*,�C4��S�&��w�--aIr	gC�Ar8Fqz�k���s6[�(gH�GcNs�Nd�Y�Fi`�GM�R�2I�(��L�aw��ll=  k�C�T8����VX(�P6zz 	%�v���zB�9B\i|�������"O�,������O��x��5;��<?���Rgp� gh!:Q		a���9U��2=�>���@������q����@�'�4C�Q#d������,�bp��>�c2IPJC0�
=BnK�t�����-��jA+h��1�R�Z��G��C����	�6L���o��6��L8�����E�O|�lJ*��,���J����	ap
��\�r�5%���e@ vI�3Dl�������C�	��=QP&*�:���M<0�Tp�$���I�1��N"��T�@��������0%�X@������$e�����,��fT,!���aShWdu�*9.�X:�"/�(&�E^��)�0ai-��-��?����`�Y�R)�s,R,���)i'�I^T�d$R.��\R���X<��R89S�G@��*e���< �h�i�Y�j��)��`�D
tGpi���gk���x�^!��k��h��D�'��|�Z#d��2D����%s�C��
`]K�����4BM� ����4[.�1���2sD����q ��Q����H!!9�2c_�XIK8��E�3��9��g�3��2����MJZ�$)�{� �Tg���'|�Gd���>r�h�����?O2Cd'5���dC��D="�;�Q�p�E���e�"B����V�rP�0�Z�lGb �}�#<�4�"���r����K�U�������?P�����gw�@���^��Qq�X��>�(����p06�L�����+1���Q"d��G
-q��d��1��-�a���% U8�{�!dG>;�	I3GN����4G0�y�d�[p����`�B������;�D$#Lfw1�H$��X?�%I��i�|�I���'��4�@������7l
qg����%���Sh���`���+�044�K�&g3����r�)N��f"��fn��K3
�*Cx�JvV
��+H�nb|1�������
�.��%�9���0`J!� �p	3^�CA�9(�*�������<�����K��P5�1b/y
��"��4���b�c|%b���E�J<��,[=��E$�9��d���ee�D�;�EhA��&�I%�r��.d�QGpB��/��zM��N���
�w�c��TK$��4�"�C��H��4��@�����;�"�����wx8��2���g��HN|���L2;f�� ��Qt&�9e��xKf�
�.��lL��8�����,���J��<8}�I��B��!����.���)�r�S���/(�>I�<�*��<2����
�����51d6e����HY��
�O���nQ��$��l�83p^pJK�a2��d
"��	<��i2Q�zg���d3lJ�0&��&D��H�� ��C�H�@�6eN�M�<�'�O�����:�2�F�N�5L!d+'���H;��=:�G��J(�75��9�EgS�q�kS,.�qt�\��
ut6:'������tZr�
Li��$E��I��q�C�7��p6m3�+�H~���L��J V�f4*�4�1�Ii�H�%{��%c��!�/**J�D�K��&#���Dc��D+BFL�R�|#���]�PUo�|�l��+�Y���i<:j��b����)����"��5������9�e�.*l�KN/_I/Fv����):kJ3��o"eN��01����K������mZ2Is.6����B\��9#�aaS(���b#���H��(�})f/���9��w�����H��<�=.� )"�yK��M�[�m�f���8P��Maw��1�,�)R����2
c��L&�y�����:�"�<H�q,5:��eB�x��9D8��
gpg�
�=X�"���9�M���.�t�/	+��/��bS����dg�����y�c��d� 1%�C�gS^X��2M^'�2�G3���~;)�{��E�X����x�dQc4��Rg�0%���tDib?EcF�-[T�������M�4�|C~8��:�:���l���*��I�PV.q�����]�<K2��:���sdG�\�M��D�3����F
hH���m�6"HM{�tO#� �z�����Z!�71��r0o%������� i���x�$�
+��	���,�Lx�)sf�q��,���Y�� ���Un����N���P�9�a������Y��2�E��<d�%�Fg�b�6�R�S�'������Ma2E���l�g
d}�d���S	��'e]1Z)�@6e���E���������)�&�~�4�x�-�����tF��������$���[��U��F7.)�c)&���Ma�0�GS5�b�)�����V
a@�����o�������r�J�N6EE�w��B��$c�	�B��� �=��K�[N�~��ld@@�/<�"����,L���Y�(�������Y8�2FC���	�-�c��Q���1�a)Ho�'$�| �Rl
*%vy��d�����s��)��,=��J�<$lJ����j"��Q|��M!����~��b��$l�Q�/.�����S6���'7�}���>��}aJ��Fp;/�r*���A�=�3���v����I�fS��0��	�w=���c-t�dS�8b88��[��Y���$O��}�(��m�<M@��l���)cd7-��&;&l
+0�����H\t6e��i�)\hq)M��A%!)��RL��M��N�1l���-:�2����rn�~eS���-����{�)T��Qd�����7�f��4y��xd6SgEa���5�LIs��
�BJ��
�2���3�p6e��N�-�9n,�4��#�ll
��h�62�B�/�4 �=�B�&�v.vC�����6���@�D�u��2^��G��D�g���b���"�
��c��9t_�}R��M9���9���3%;�^DU����7X�S>]�6�"�"�����2�y�I�����t^6E�;��f
����B��<n�H9��03��+6%{
���T����1�)�=2�����O>��bE/�,���f/.�n��<�d�]&����x�:�@���aSL*YN��,���?��i��bqz�����\�k�QZ|{������:�+��l���f3�N[�,=�����������AX�X����av�)���9w�qP�9�dSr�������`'��	�3�� ��������d��D�3?��" \��)<!��T����#	��M��I���L	���9�=66�V���:�!����;��)y�:��@	#�1^�?��)�>!���L��`�]�i�����1D��aS��#�dQ �(S��-l����N����I&�
{�����r6��&����M��+bP8C�L����F�
��4�UC.Q>�����'^��A|o {�
l���9W�M��b�X`����-�H-Q���������H$���#��}t��"���%^��\��4�?��!�d�@��qK�-�����l���E7����9��M����0���H�<t��P��J�����1�����Q�d��<��a��3��~�r�&R���W� �L<���<\��+�ye���_���Q������P)0�K4>�N�$���_7��<e�*��!`JEgS�P������v�����
	����$#�8�0�Ege�T��>��\t�T��>���C7eV��O��#�4�n,}F�HiN�����!C��E�i��9����FZ��"������ci0��=Q��"9����
��)Q'b���YP��yh��)d;bm�')��8>�$v����M�3P&>��sL���
/,}��6*BtC*}��he
D6P��
�A�i�C/�������g�I�l�c��e����E�1��� *N���(.�$1%OUp�����aU�&1��iV1
�5��U�]�P�P��p_cn@IN	8g����Eh	^a����$���V�7����6���H$��������)��Q�!`�l��%0�J<�������'O�Q2p���C�I#�������$g�#<%��D!�~��9MTmDJ^�cR��{�G�%0�4�c~E
��rZ��d�%��7bM+��P/�d|Mz3��<8�(�HoAZ������a�l���.Ea�b/`zo,��*����jH�F�gtxPB)�a�4���"i��p<q� �(IY��<�����l4�e��x6V�
��
(c��C1�I#��'�Dp~i'�U�?���H9�>y1Ql��10:4`f]�^h�y����$��GQ,��,��	C���F�k���^��v��0='7)J��2�~Q"��0����HQV39	�������<.'�fj�W��@)G\��\�C�`kV/�nZn�%�"`e L�0O�r�[b�������Hs�C���f#H���(��B�C�J!��LdE1�~h����|Q�0� ����+r1^��H�Cn�V5�G���ER`E�}���LQ9���b���9���������
3v�Z�e*'
ofK�����%�a&��@��b
����'w�?;���8�����(:8~[�,
��Lx9��������e\��f�1�$�|#��&���%����+���JE�b
��*�rd�G�Q��8
;����5�K�P>�V<�EQ�D"[DXIF��L�j�0�,�$�0�%OE!�������/YX��S#��1Y����������p�3�9���4�!�:I&;2���"`�v>;��
������EE##�v���������V&������[��d��/���a�3�N��D���(RL����G"��/�Ef2��AI%�Y���i��NBh*2�Qq����% :.Q�:�%&N/t���0�����L�$�(ct�&o3�s(���DF&��(��*;a��<�'�=�T'G1DJ�8+�TM�e%mf�K�JT�&%
�B**��tV7g:Q$JiF 0L�����8����|��q�f�� -�
������T!:�w.���
u�"qj�����!���)�"K��j�"����O��s��(����?gH�[c :��k�y<D4]R;�0����� �d7�4|#1���LK�� �$c`����q�����x�r0lP���p&��8��Y�4 ��#�x��i��3M���
�
����6\�`����A.�8���
��<����1�0u���Bx[���& ���)�0�q������)��4���NTLr��R�j$���(F�,^�Ji�d�Iz�Y5���� ���$�����T���N4HT�]r��R��]�\�%^��RaN�tA
�NO ��R�~�]���<�a%�BX����7�����l�9����L���%`J���C$N�"6>g2�H`g�3��#�8�*4I"��H%�9���N���!��p	3�We��< eo������ C��c�����������.�3b�Q��~�������8����v��M�G��z��	�I��B ����
dw��)%������YYF�eJ�{�+:Z�40���F����F<���N�B$#���b�)D�$s��$�U�,i!k�z������`�\k�D:m��}H"��"03�)��SI.�����Y1�y\�r#����T�R��h���o�4i<�!��H��b�HQ�_K^�Q0aV=B����� �$�k2��Y��=���N`�p9E(�@��BR�dD����R{7��}�v
�eEK�����)�M�?J���U���!:W�E:���Q0��)Z�`y
����G�Ju�o"�	�\f��U�th�e\)P���b����fL	qd�d��)���%(
�7N�,.Y�p6����#��b�L6_|�8t��B�!��,�]�|f��t�W1�Ux�����X|�z?40��y�A�3�@�&��t��z�LQ�G�!���2�)����DG#vF��G�����e�.�=�|�Vu.�3�N�����A�_bX��`5�Hl/=�5��1{�����j1�h�������w\�F���QM�&\�($Ks�m��C�'�)v@N"8
���=�w���\���(�RDLW���,����^P��^���H�1w;��xJ�;-^4E�{)��:V���R����T!��g~S�0|q��Z�b���o�Xmb��E�"`�X,��E�"`�X,��E�"`�X,��E�"`�X,��E�"`�X,��E�"`�X,��E�"`�X,��E�"`�X,��E�"`�X,��E�"`�X,�~��_�77��*K���Y,��E�"P����u���
�����~�6��E�"`�X,��g������j��E�"`�X,�B�_���Gdd�St��~p<((h����S��
z��:o�<��/�L�8188�o����3��8p �_���<z���a�bbb�m�f"M`�����M
0 ::�����������s���K���7�`~�t��E&/~A��Q��}��,Y�t�R"���C�I�l��Y�f�%?�I�m\�����6m�����{���k�.����a�2f������,'?��]v�`���E�"`�X,e����?���#F�����/�Y�M8��>��y�����?���'�$��I�&�u�]�����j���g~���7n�G}�Oo������y������7��������0d���z�[�nR�������W^yeTTT��]��S=���?�x�.]`}�L�>�������_lll�*U 6�=,,��[n����[�o���{wn}��g����4|S�$@;y��9v����k����&  �,��O�>��5���~��~n�����z�6m��a�����?���{��~�a��7���A/M�
X,��E�"P�@��"H��D�B��[o�e:X�x��?���?�i����g��F���A����OsW����K�)��Z�j%�7n�f�G���{��'33S��o;�_��A;��?����S&��{����%��ph��;*V��JJ�w��}���C��l���3�E��%����?�H�_~�eMh!����#r�5z�h�-Z�h���D�

C�G������J�I4il�"`�X,�K����:����� t�������C=�]���k0�y���y��g`,���;���"���a_0�Z�u��I4`
4@��w�^���2?����LJ�Es�W����h��h��&�dh�aSN%�aS(�P��������d##mv����[��EVZZ��
	�"{��w	C�P�>|8c�X,�@YE�����f�8��)h��A������o�o�j��%L,����Jr��RRR>��S&<�D������)��d�7��l
s��������� �2L�����IF�z���)TR���4b=t�`�2�� {�(Ts��������I@7at�<��XM�
X,��E�"P�� 9}~�6m*l
:�4�a�#%���*U2&6�A5���fF��]\|����X�
��=:$)QX�t�M��]��)L�7�|�S�����[<��:#��)�]�Ja�<���):��S��^�b����	��M���������)q�K�!fh�<��Mb�X,��E�L"��u8�_���7�xc�&M�)�a��1=�.���`������/n��	�"|��z�)�	�
�
����u�p���?��(�C�������#-!���GDz������6��!������`��������y�����c���7-j�\���)�����	�������}���4i8�C��K�I���
d�������G���<����[�n�dX-��a
4�l�"`�X,�K�n5k���@�����+W_�jU��������x��G�~'����`)�|�{�9�]R��/P8nK���g���B�>#���x����I�G�L�L2���}�Y<D�f��x�:N\�L�SSS�w6E.�����{t�<������pA7.�p'������?A.1���QCY&�����,��E�"`�\��t�aS��NS��z���C�h��X,��E�"�ox����������m�8�Vd�X,��E�"P��X�������1�y�g1b��"[�E�"`�X,��E�"`�X,��E�"`�X,��E�"`���������%[,��E�� `7���Y��l����^[,������e���O)i����� ���h�a�X,���\f�h��<g�� �kz�x��E�"`�����\f��}��j�83X��h���Z,�E��11�-���\�P.����B!gF��p)Lu����B�:v?���7����kE�p�p9�]f����`.�3�nA�g���E��]��,�b��w���Kz��m?��#��u���C���w���6_��,�������v��i��NP���?�!�����.=u��%T~B��b]����n�������[.�i������C0�U��\���G1��������!T��������Q�1��,�f�.������E�/���[��<�_�����d����X��c���_���z��{f,����%T�E���\����n�������[.�i������C0�U��\����M9rd���b��J=��_Hr�����YN�d��]������)���@[.����n���T=�� {��KaS�V����z�}���	tH�t��#�OZ���������ev��}�e�83��Km����v���seA>2.�
���'Jq��U�M����6|}�>��g��]���]6���nqf�-����yO���9��� ���'1�MUK�S�c�R�MA��#��H��g��O^������V?�`�E!.����^4���bqf�-����I����	��bA.��6kz,4
Be�T��JGVM�bEf,;s����y
���.����nZ~I��83������~��y���C���w��<%0y,�~��)�@_�T���8Pi��?���8Qbp�j<h��o�YB���(�X���e�Bfo9�8���9%�>y�������?g��������<x�Ou�d�v���d?��dS'�}���Ja�B��*���#�,���3�}���F��b]���l������L,�'�oc����oo��������]�5j,^��������T)�4r����4l
�l���t���B�A��+BE���j���� nT�`K(
.����Ei�MgB�,^8q�D�R�&M���:t(��d��k-v�^)���'�
�Rd�x���A=�R�y1*�-��
����',8q�4�����O���X���evv�leqf|-%3���Z�vmBBB�V��M�V2���;y��0�[���aS�&��I;�C�`SB���\�U�NT����q�;�fQ��m�X�����2�i�
���|,�O��$��6�-[������D&c;����Kgu1N��N�p1���U����/)e�k���Rp"|	5�\Vi;^�'��R��?�#c������$%���.���w�*���s!S��/��V�!-�m�_^��S�����Oo�����5k��[�g����P],�����~s���{�m��k��}�>|����Bd], >�K�]��0�=�+�B���y��THJ��qj�)XtK��4�R���:M���u)���K�p��$���e��6��	S���8�k���;w�������g���a�+�"����?J�d$-�6w�\l|3f�X�`����E������+!W�-�nbb"�~	4�'U����(f/X�{�����k���<rH��a���� 4.������v��S��*� �d.�|!��)L{Nos�n�,*�b
�$6>�Th� TX��'�Z��{���

����2��z�(��`��}�f�
�������b?��I��W��~�K�n��f��_��/�8; �W���/d�=z/������{�������[��}�ry:��;wN�0�������:X�'x�X��s��5���z���z�������xu��J�:�Z�q�/����.D�Pj�.� �Z���0����A���\���hSR����)��A�����
�Ezs�����;0����(�+����2{���EbTOs,n1���:�^�����*�{+<�����jv��V��C�m����e+V�f?�R��C�E��+|	�����i��+V������V���h�<�K�e��<X��;�l����+��\�nt��1�D��#%���k���|�^�kw��V�5����7�������\j�ef��R7i��\�sy1��V)��VR)����
�����V��������t3��R��,"8�'����2�_q��7<)���t��f�������\�P7�f�I5c�/TR��
���V;��/�!�8���I^^�q��S��hA(.b�M�t�}S�;w�<y2T�x��JMM�`Sl�����$�����G����OH��������|��������*_7��Z=_���k�5
{���"_��+���`��.qCv���*�B=��W��M	SESelyZO%��e����]h����B�eW������:}��K�D�n%\����2��:{�����h��b�0-�r�T���1i5�R/�@j����J5:=�a��J��������_�!?&>�a���8p o�2d��Q�v���Q�eS�xw��T���'��tU��JF��B"��'���W�Z=���Ux��
K�n��P�f�ys�����o(E��5�L�v2'����2���P��R����,nT�q��S��T����?s���e,�:�%����2{QZX�4Z���?�����+�cdQ�(O6�{K��*��Z������'����;})���]�_�!?&������x���
4*Y6�v�T�ac��kW)���;9d�s�)�)�z�o��$4z����o�OJ�+�'0�/�W6;�2�7v*G)�z��2������������C�hU�k�$=Z�\�U���O^�inT����������.���R!���VP���T
1u6����c*�
�30�S�����*m8�|��R��((�@��\�|������h\)���'��06��V�[ku�5���R�CeW���q���M	�z�S*tP�O�*����@��D���m�R1�0�X����<<g
O7@�X��j��?/.�����m^q|�����A�T>0<?�:��b�b��{��6�����m�G����?�x�2-���dX6�g+���k��@�T����T�#���������=v�������Mi�$J�� LI���������EJ����qAWo�
I�'����~�\Hb"�3���STTH3���p9�]f/�m����b���w���|����sS����QZL]Y��#
��I�(=#�*��;.�JoA��.��l�
�9���y���W*�QJ���>�nJ$����V�^�Q��J�{��
u31
��?lJ?���~:
(�L��r���2�V��Mir�UH
JT[J1�;�&Z�Vi��5zO����}yB!Q�-���e���V����R�fU{�]�s(����4���v�g>�0%+��p���s����-�����,���M�6|����=��
��@���
BU�������[$�]�^O��3�������H���,}��.��$tM�z�����I��1�.�^�A?�aO�M�\���'���T8&������2�����t�W/6n[���
|FX�_7��}<}M���~�{�|�H7����t�}^�{}R�eS���7fPx�[k�S/C��9����4���F��Zt��}��]�|���P_!�Q�aS�S��v$,���J��@��H�/�R	�Yf%�J%k7�4R	�E������SW�o�x4�^�����2{�m+�]d���;��~��m�RH���)M��F�T;�I�(k*bK���4�����dB��w2���@J���{���,�Jq�l�WQ�����v�����?�cW��(Y��AAx�C|���y�O{R)$�;�S��	���)B�)�<��O'&^������3>T�M�����������I�r���n��&p����n}���|�����lJ�yV���n��qI���4��f�|�����b��.Y���C�R�C���RN���M������i5q����v�z11����)tP| <J����v)Wv=�:(��iN�(�|���EF8��?�?@�"��Y$�))�����w��nTE I�r���^�v�+%�lz�����.��(b�lJ�����|��`G�������8�L7���P8>%v����B�~����7v�'>_�)m�+W#����]�Wz�Z��Q���M�����J������
6%@�����zJ8R�����`P��b��<M|����.�Z����Q&��Qe�\������B� ����Y��5�����y�;W�xlJ�kku���+?�QHK�}�������|B��w2����0�����wU��������x�)��&T�V

�}����D�B�51���W6�'s���Ib�C��K���JTX0%�����c�(�c4����
�$��4��S��"u���2q��M�|i�t9�]fw���S���rC�/�7(�B��M�U���kE�\6���g��������ui�M�+W=���U<k\��aS�����2e��e��+�X���eS��zIi*e�{��-��;�W��B�$�������U��WW|I�R�I�3���u������g~���k�����R���.��A�	6s����[��(��*>��-!�7P=�0(g�7
v��������kA�-����B
�t�R~_)++k�����-�*�#8�i��k�M*��7lJ��*����O�������zv�8�G6�yNq'��F��0"v�A�4��M?(�F�E��Y���d�A=*�@h�N)�B���i�
�����AS�h���.����o!+�n�^����5:��9>���M){_���C����
o���^(������ x�[�s��x���A=z�������Z�b����%�+))���fT*z�$��<x�����6�1�H6>�uS�
���u��A}����k����d��.�%6AB��m���ry�5T���$���Wy�>e���_��K���:dL=�GX������8���*D�Xhj����F�!J?�%�B���ev�;x��w��Co��E�����M){_��u���f@��n��/�^7�-�@�Y&B{��MAAA���o����%KF�9j�(���,�r�QH�_%~�Y��x�y]��56�����)u��AW�x�/x���=��
-d2��e�����B(��ok�Q��`VJOe��������G���/)
�*8Q\��uO'��U�6�����=�N�lx���'=d������.��������X�~�#�[��w����z���Dz�����:��k�B6m��]���� 8�i�?�Z����L$��-[�����)33G����1c�x��X6��\a��8��@^{�������A�J��6�q��o��6?���'`
�B��S�M�v�9�S�M)��vJ=�<�������T���YA��L*o�JJR*f���@e%��&Q*c����W�����i�A�F�1�.�����)�%�������C��y1��p*���zAz�-����(y-�����Qb�,�����}/_��~��i����W�Z����l�(�x�r~��R��b������nJ���q~�����iS<hpQ��&�]�n�+$�aS��[7r��~:O*��~����\.$$J+Z�=��/)!J-��Q����C1E����>)����Bg���6��m�[��i_m�7hJ^ZP�����2�}g^M��}�����T���;6����?P������h��,%�����)��O�:��)!!�>�o��u��
r���^���������7�����?�}}Mq>/��r���"����o4
��T���y~��+�O�6��V�`2���O��=q�U1�#���t�aa\��J�M����{J�>�RQ���\��G��K��C��g�S���^Q{pb��g���y~���U����2{q�B2����������6>9���Mi'�������A��<�S���I_�8�i���Z��m�%�"�v��������r�������_FFFBB��B/7"�d��\�u�����K�n��v����V��C}��H�����B�9p�`i��G��y��9��/G��I9Aiw)^��]����]E��M�EX*,E���wY"�8�k�u����JW�j�&?R*�*���T26�?c��c`R����S�'���.��L�������i���sJ*�l�b����9abIj8K���K��� ��������45i����'��7�0.[�,''g��9���<�w���h�O�(������M������<��S>aS��>��5.�%&�Jd����UF6�����W��L�CR
M�f@<����y��(����_i.TG�Pni���1N��4�Q�iop���x���c��:|��Gz�������z	S�D+E$���*
?(�M��1�:\�X�Bs'��.+�G����3�K=��o�8%6��>p%����w��X=e>���>l[�vW�S�+�lJ���
��F�0�)V��$.I�����y-~���Yb���������bcc����n�:�Q�3.U'O��8a����?"�.��e�U'��r>/��O8���vG�����}�8q2;�S�W(��
8* eA�tE��4��^&\������V�%��wI��Z/D�KQ�>��8����d����s�V��Z�D�ic_n�._���zZ�D����d�8eU{�i��I!�<p)i������h6%�-,u��
��L���L���e�bA��z������)�����=	��M)w��C���I���u�����F�;����el���C���x{'<����-!�_��={���-��?"{��m���:����SX��M�����l���%�4(�+�
�iaho�k=Fq*M]Dq�����GS�C�mCJE�����2�i�-�����Z$��H0H�!6��I�t���*ti���[��G��Snu*���O�p��*��k�(��o���P����M��KZ�a��������I��q9�]f/:���7l���&��x+�SL��M)'O~
����iR���&e�������kA�7�l�v����kJJ
�Q�=&&)��b�o�,p,j���;j�v��B^��M)�TL�7�Zv�>~�D�
�md^��,�yV���`�|>��_�V,B�+C�D���r��@l�^���%TXHMaJ���$���(
m$L�P5a�Q�	��/�R%ke��T��T#��i�'�T�I��r���U>*�.S�0+u)�L�.U���(i�EU�"�]jW�����:q3v8|iZ�\������yyKx|���Q�A��7�6������m{�%��z��p.���|JB	`�!����6l������C
�Q�eS���M����q��\)�|����1���ZL���A�M�}(�+���v��!LV�n��@!#���'R�o+��R-,���ttP������b�#��_i��-���E��vHQa�d���b�
'�(O�i~�w.%���k�-�G�,|�6I�/�����a�E�	��7��V�I�r���JK�)����n����dg,��'���\������%��z�N���^{L	���RW�	�a�*S�^x���p���%���P S�!0(�4�m����-�*p �>z�|K�`�Z^{L����nJ?���<�w�~����*��P�����a`P�!aH�c�i���mg\*�$�Al ���xk+*��d�������[�JQ&�J��7J��\)��5Z��`�h�f���+�~A�J��Y0">b��H����.U/��R�%�\Z�%vL�g��i���Q�c*�&������R>�G;$
���4��]�����e�"�����.Q��Cq]\e�Gz��)���ju��2�8E�Z���El��JfA�P�{�k�� r�v�����2���uz�)_�)M�n����'�[�^�W�aSB��S����|�&*�J�0�B�
����
$���%��K�IN���4Py���'���&���J)J�&JP�!��TQy��Iq9� ���TF�� lB����+v�v<U+ZE#���k�1�G�K�q�'�ft�IS��=A��UJ���f�����~����w��@��?r���w��B��|F�+����Ju"��1=������cJf����e�@����)D%r�����:�Q����l��z��kM���>����x_���j�TD�B�
e���3�IB*Du���Lc�@0��)��o���}�V6�Q����d����r��c/'��(
��OakB~(\��I���*SS&�T>�P	�RUh��R��z�3
QJ3���z������GRw��T�u�h���a����\���Oz=f��].��Q���.�e���q�.�j�x��)�X�)��]���}8/6�b�(��&M	L67��t�Z6�o�Y�a�[��T��)�����S�c~�V�	i�~��c�ex�63Q�Hs�������XZ%�#���>�R����|�kF�
ND5!�����>�k��%w!B$�0�T��E����B� ��rIX)�4���9(���)*����Z�K9B�T�Z�]i6��n��>������P%Iy*l��e[.7*���e�|B)_�/?/\���-�-.]�S��M�[3u#�o<s�<^����>��;�>k�� ���m��Z�O��<���t�^�n��~`S��fz�����}���H/�+�W6�����4(a��	�6�( 6���J����V6�,����c�(�8��
>�ZwD!O�N�t�����)���1��<�D)U�.

D���������[�tIc�;�,U�)�y#vF������1�&�\��2^[�ItM�1E�����[dWw��IO5 �!�2��..��A�r����!��_�8v�YH�
5�����T1y�5�R���]?�q����o��#_
c,�dP,��������3���\1��ON|~aSX��\�Z��~	����f��
5lJ��p��xL)��������h�� E'�TC*�&c�=�_Q��L"�E�R�D�������h�07���1E2���T���Y����\�8x����E4>F$M�k�z
�dL{|	�B�&D�KE��\������$��,Q��.�LQ>��]0�4������2l�s9�]f�������G��p�+��o��k�����lJ=�W�n��k��������/���/�1�R2,�M9����M;F���\]�s�.e�����[U/�\`x�����^�{��]�U��P���.0EE��F9cw��x����.�
�=}����CR�>�Y���xg`vH���'��?3~���6�5E1��/�����2q����tHY�x��
b���.�Q�V���R�f��l�I$��|����>2GX�p�}�3�&u����������j��R��>Y�(�e��|G����SV���eEL]�H���$|����"��������J�&(\pb���>:��pPv��u��<l.w��Pk�r��I�JA�-����]��'��s���{x����+=����.����o<_�Y��������\$�������v��>��c������s��Pz�ZJ�XX6���������*�~J�7����Q�������A]���>�����ex�:��E���e�e�[������8~&y��Q�l����#�j��TD�}R���\N[����''���p��g����k<d��'�.�q�pd�r����^��*��I��+=O��Zq����^���T�7�����',����#�;�*��=BQcm=x���5{�����k���{2����]������N���'/������tJ�;�-?��OSW����
�w����
{q���t!{���;��}����m=@if��0i��z0��Qp�r�'�*��G�]_&����w���U�����_oU����"8E�_�"�N�Bb�'���=�>�����d��Y�^N��������K��w}Y�^T9Q)����T�:QW������k��f�2�B�l
��n� <��Jf�|��g�M����y�cr��}��>��DHJ��U��������N��*8���;p�4Z&a,X�����������d������}��u{�N�j���Ov�\�w��3�%.��b�!8�����?CR�d����_�������?:��Q���H��k;&7k9z���������f�����������K�0��YkO����v�{*��Ke+�
+��C�B��E3�%H�q�O�:
�f)������2{!n�������5����BL��MU���w��w��z��B:��-���u�J>���1/�F�������nC��X;Z�gS��D��n�V����{����Md^�l
���~��p�����v�Lx��R��YD���Bi�y���y�G	 ?���!y����W���B���k��$W
ri����Z�i00���y�{������[�l�Q�?�\������6����7������v�����P!<��.<��\�y&,��d�74[�!y��Cr����-�^SV��n�R�*�]p����U�{�P�I7^H������y$Jx��%)������}[�����2;�Q����<,�7��E8�K.��bSu���q|��i����+�Sn"����&�|^B�c^`��M	,������uM��P��nJ������/o����e+|��*�+��)E�M��:��_�#��S�B�Z�S���?����s��}GO+���Z���O2g��!'�(V�T��������A���eS!)8b}��Oh��g�7�~��p����BCu������Z4�R%h�$��[�)���4����)6��t�8y0a��T��
�_-����S`Y�W���n}��*-$������?0��H<U(�����6��:�K�4��L��v��>){��2���r���^��&r���5�F�����F�������~��������/�������� -%;?��i�&^�k:y����~���Z6�@���Q'W�������z'�k���D�7�{��q���2�B
�2����A]�?�)��A�<ASE'�a8!)���c�*Le�c��h��-��� ���
NZBbH�bS#�AB�O�
�/�/����,���������+�K2�	�� x<<Hu�aS-F���P���b��&��{�x�y4_�1D�3��Q=v:=������vP��R���\����?�JI�>���o�Ra}�tqZ'����Rm��P����e�.o�r9�]f/p-���-.���)��m��Q���M����i��I_���7�.�5�#������P-%���#G���V�Z����;}�W�����g��M���m��u�}6jJ��5
����Ea��M�-}u��*u��=��v�2�B
���2<�@���,
�j�5z��$�	O��zL^�����h��HA��\Q(���@{^�,����I���BR�����G����>�~������.��)FF�����C���K�Ai6��������������5�%W./
N�z��p{��5SW�N[��\�������V�,�)�UE�e��%�nIX�<-U(&�uw�'L�D6M�u��)��F��*�����2{�}�j���?������s�_��baS�w|��s�W��?��O{,]�U�]�:�8{������o��Am���C����OJJ����Y����Y�eS�����j?�o�����~�������-}e����$�q��1������+��Z�ABc��)r�r2�Q>��a���;t�FV�vTV?�N�P�v�!Gq�wm&,&jUf��<7�����i"Db�T��J���.i��wI_!,���T[�����<����3C�l2��K^���Pc�$�PmA�(�B�$.�M��P�}>v!��z�����R@%�T?]S,���RlJzM:$?�-}u�eJi<)U��*U���Rg���S�28^��.� $%0n����!�/.�����r�������P�t�uw��-��^wQ������^��������}���9���/�1�[��
:v�x����N�0�A�qqq���M�[��nh\�������^�lj���F�<�":9c�����cex�6�R�O�)�-�u�������������|����t�?��zG1��$��=�.4��{(�Tp"�?��3�Vh80��asq`�5Dy���!��^�/)��4 q��������-��|��R�������SW�K���k�^�`s7�|���k�3�&.C��.K����������e���w)<q����Z���U�8���'r
S�����%H�
eR0�����)�|S���S���D��Zd����1g����Ag�E��Tk�������/����tS�M
\��n���eL��y�~��a1Utd,E�����1,}�f�:|�0a8Uzz����������OG�T�Q�9C����n*a�������	���_�;��A��ex�6�����	s������s5T��5h�6�?�������J'��&8���r6�#���i�:�-]/���Nh�v:z��~E�V��[�M�2��I�
����</,�Eh������n��Y�����NO_�.��H���v�GC���^����l7��Q�l9���h��m���9u9$�7M�F5f��g��WM�+8|���9aV��1/GL��~�Y�����y���f�}ZA���48|KJ-���\P)�#�s����[�l9q��eS^�>�����C��;%���|����M�xq��{zf=���G��)d��a1Ut�,E����'���+�l��oG����x���s'�@����pFN�9���1�j���"f=�wn~6uy`����D�~'y�
�?�&K�r:�P.ICb�
|�Tn��rtL�b"x��e�%�b)��wb��/a��=gV������3�3�>��������"$Z�Bp"o���-Q�@c�`P���?">�d�LC�tH��4����J�%U��M���R�#�V�E�F�"���Q�bUQy�(�Mur9���t9���T��\K����O:������&�O������6R�\u��~5��(�Hwr?Z��j�y�mT*=N��G�8q���K�������>_MZ�*�H��*1s��Y �R�# �������]�/�
|C���������B4!���S��7%�����Y%"����g�N=U��T�'���X�$%�R��/�3g����;�d���3gfgg'''8��Q?�6�O�`
����!�����wS%r����T��
u�nn��/���Q?SP�������*��nW��B��[������Ql�����bH|�{�IO���b���J���^��i�K
�P/����n>���B`G�����}�%�����}�����j@.^(��&�x,�
UE����.W7��T�����i�c���0���
d/��w�_������)^`�f�Y���b&����i���G�d�@HL$<��p�Lm($��=yf2E�pI����z��q���#�����8�~��*J��T$�
N�u>�FJ���m$w�#:��HKM�dL�MS,��U�I�HS��]��h�~�QQD��,�l���}��R��&b��Q1*A��Ot�{0�,[�l����R`��M�d&#�[w��9�j�����3�������n���P�r�QH.E��]��������������5P�+y��7���MU�����Q�M���Q�uc�����c����w��_%^�^'a������b��s��J:�T������������S��T��� +7)��=�r�JH��pJ��GiHu���E�����M
4�RcS��p���a	U�O�9�����)�U
b^�������G��{��+�G_Y#&-4i�����1��1oE��������#�l7���S���
{���B�]Y'�����yM��Z1�-��l52��	�T��|HV��9�5�?cZ'�w�?pzd�����^�T�Q����J����'�G��%�v�S7��@��*�������w�����aSZ;����0��9�<�(D�$HD�i�R�FH<�B�����}LQ(���><�V�>r��wK�$��)	���_P�&�U@�X1���_���[��6����P��(��Z��I!����`��)\�"(�fnr�^y��4*#m�i���~%�j������;��;h��I���Y��~<��Jy7�JI.���evb*cZ����3�jdv~6���M�����Z���|x��m�#.�6��O
�0��K�Lk� 6
�M�����6����x1t�G�f�J���^���~���
�#�l5�R/C��������SZ��4!��f.��m���F�4<�F�
u��Q\�a3C���l`��Q��2����wM�M�^O��~���+#8j/ttSYU�r���|�U��)3L�|���6\�����1c�u���W����u��I�����0a�B*|�/5K��j3�Z��*��JL�cS�$��<�vd��9H���FVk;<d��!9k�l>��:���\m>0*ci����"?I�Q3&����2����U�R���D��R�6���e��#c�,��^) �o�#{M]�g����O���D����������5�U�OF��s��dp�=-^Q+����D���]�g������<�@��cex���B9�yT��y+v����A��q�x�^4�Q�C����`.%���{h�%����Ro��������Is�M��I�LJ����%�4�)P�J��(\�`
�C��m���;i�C^R�
��<E�����\������Q�u��e�%R�Z�$m��}I�e]~��CG���l����xbx�L�R����w����o���?��cz������l
Y�����?2�c��&�f�{��ac����g��h��SW����V������.��jG��������w������
�P#���T�
c���\��1���%��:���:�_�Wp'����������������76����Y���|>"���*��N��V��M)a��N����a�nz��^7�4d� �{�O�I�:uZ�b��������������.56�65������Oeu+1��M!.���Q�}2��^['6��f9�U������@tJ�)��=[����W�'~�����Y���B�����v#^O1l��'o� ��F�[�\�q����w�M�����*P���a��qs__. ��=�[��i<`z���'d�7����oj���]P�*f��m���8s���	V�W�aSp'�<n� K���\����N��	9Q�B��,����&N����45"e
�R��]��W �r�d�bC���*���T.�a�� ���x�R�U�:��$���YH���>�� i
��f�	RUD��Bu�F��d��V�	�T���'e����p\������6���qU{����M�����8�B���}�Gb���l�ToNg���k�����]S��-�B��Z1i��e�6�a���Cg���x���7�S�n���?54v����o;
-z�!3��~�<�$P���?}�v�������^$2��Q�������W7vR��	w4�gtS�Z�>��o�{�3�&�����
e;/������*'''"""55����\��.5/��{�~���#aI��b�����������=	b�����K��L[���.����N�w���������)WD��pG�/��C��7�����C������P��S#�� �c���Lk�zD���cf'-��d��p-�T
����t�1�j�_�^o�a�#R>��}�N��O�d��Q\��e��.���T������.\�REP�W�aSp��0
E!4�Q����
<DQ}
�s�T�J�w���I(���0�,�h]Q/�zRDH��Ti8o��k��gRTm �,Z
������^�VMGR�0%U}K5U+�h�j��[�
QD��$
lJ
!�JO	�qi�*U&�kJ&e���F���<�a`��h���BB���.�������hR%\�
aS�����w7��7��x%��&/k64+c��G���v8�)����#P^�3l��o����>w:7�
���������LK�Sx)Tn: 4y>��&�2S'������	$1u�{}Z��y���W��y��}�k�U�d���#f�����Z����t�7�,}FLU�>�����k�87#/�>���zKUB	��dffv��5444,,WsX�G�� �b3���BHB�����������:�x}?�z���58=,y��&�?�������o�$�3l5��z1w5��3u����
�X'��`�~S����v��9��j��eS�s�����m�?�jhx�bY��M�1qQ�����������|:����������_��W����q�����l;�U��d���|��M���}_�1�w�}��I��.�e^��M)>����B��F��+�:&LC4K�7�Q#O���0�<�yI��Jp"�9�x��?{�_�q��$����7�1�6`�0�6�L7�����PC���!D�U��{1�k�<��%/y�����G������U�:���jvvfv��S~{��3p+��t������E�����-�6y��]
�GF��)����(����wV�mF^�p��-F��B�,jd��Gi"k�2�	/5�l�mn��E���}�������`���G�E%�R.�i�jl0�������!8*���dzt>	)@�����e��x�����`>���&la|�9�%&�O�34��va����}�o��;�G�Yw[�Y����H
#��	-B���� ��1)MG���fl��)K�%l�\`���
F�p`�w(�0�l9�6b���"�P��������Rs:���uh��	�l�'�?.eZL��7����q.�+`z�^
���/��>���}���d�
��#G�v�yt��Z�XLY
2��eS(���2��.1`G�G�\�)���QXRu����=g�����E�t�x6(��;�S��������=?i1k���m��f���	_���J{M���#���Xxv�������Rl����R:�Z�b��#R��N��c$�=������DG����T�F1������E�t�9y��l*�����
Fl������P���x�'Faq����H8	l�b&6��]X����p!'�A_&{
����e�t���Z�r���]���w�	+(P<P��g�+R)�����T�f3"b���G�R2�R[����<~%�E�*BN�BHU%`e�I#�-65~Y�����T����Y��������������T w����O��i���a��CZ<��.u���a�������q[���Nd���Kd����H����7B����%e�>��x�0uu�pL�1cx.(�/��g�Y�S��}b0S��Y��}���y���$�e��P�������dA��tK�~��q��7������|�:�N`�>��S6�0�9���F�<p��z����8���'��PA�EUcS,r�1wQ�	)5�o��0�&�b<aY1�Lw����g�H�/z/�	��b�:G��A4�[n�1��>q�����1����s_�/������:�W	�
'vSw����{4.�X^�5�/���D"������z����U��_<��d�1�w���gz��;ES�	����A�{E|���%nf~�CeS(�l�o�~���>A!��
����E$Z0
�+0
aA���N,$��\�t+&�AQ����H�le�U�-A"��ErY|��m�%�bm�&��b\��L�Vv[������;��?u�O�[��.���x)_�I����dlD���Kw��q��9,-������x�����Wp�������tv���e#p�����5��xb�Q��hsm�H��xe��o<�	�a����w��f_�!�m��>2�Aw{��
���ZhL���2�k�Z��%3*��;�aJ#����QP�~�Wd����!�h�����[B����"�x���K���{e�Q*�[�jl�����LJ���G��)����MY��'#	ay��Q3�j	��.7����;��!!�I=s����=���7	�J��x��	�!�X;b{�~ai������q5l
�"�����,�*"B'�!���;�`��jd��,��-��C	V!�Ol2c1�(Jf���>%%%XVL��9���Y`M�p�,/����XX��,_�d�;��/���	'�;��B��NQ,�.JX
����T�?�����&�^�����e���������e���{�=3g[����9���a�M6�?��C��xR�a��CB~��8�Rlq��K0uxH��0�S;4�������6�%;���d7�h��
�F��b�����-�fk�.�����c�M��B����r
�;D��B�CV�����_4���?/YK��j���*H��I*�o���6���e��lq��,i��F,�i��0E	W�VY���&CzN-��MN�R��B���}N�3$�*q*�<S8%X�����?�'5'1��,��U�}_�	���ZQ%�7�L�Vb���U,_��UJn=y���)J�G)$'X���v��S�l��d�$���I�rc�������#�
�2�q��Y�b��o}�;eS�x��}�Vv�#;��<�
������T*��Rlj������^����o�eSDE'+�M���W�fS�b�Q����+����=��)���:a��lS%"�A/&�(��:�������Ji2���%�4	2��/��T�P��%a���*/�}/�g1(�DI.~�[��
#l���3�H�%��G�X����pE����OX�}/+�]++K~?6nI�����4c~�v����d���z�T����M����g�^����:L$���X�I����'y�HS$���%�f637u����(op��T�4
ByMU�Ma��gj�cA�-������a� M_������M����M�o������{��lJd5�9�&h	C��U��O~Il3�;Q�t�~:����SJ��|&F��0.��Q/[H���K�d�
���Bx8���2NXE�'W�%��Q&�))�_"I�G������R��1y^)Mh��/�r�h������Oa'�����k������_��8Xn�/������`�(E��<�
LY>l�����4)���<����p�p�q�`��(��PA^VaS|P���$'�f�fw*e
S~��x��95&,�9~:�_����=��)�W�*3�$";���J��aV���$`����r�B���?J�
��qY4)�n�\��1���V��5)And� ���h�'�*�\�jPS,FP���6�2���$c�au[rE.Kg���f/OL�M��~�z����1['`(U2Bq�^EH�e�/YvT���#��Y(���<J����0G���h���	�%��P2�+B�a��MQU�Me�no51�N�Z��:��L�?��e������U6W���=���<�`k�D:���:�`�4�Z���<�-��M����B�C	�>��P����9$b�u���B�R�0��|�&����lqS����R/�W��UH 1�p.Ie�3ec�(	���L9��P��D���;'/X��_6-��UA��H�l�%���le�!���B�)?���C��-a���&���g�S�F�a�d����5��T6��C#������-�J�K6�s��[3d����>��3�[)���
��>
a�[!�W=�B���F,�l�E��-!�B�`>����	?�@�'��[�|9����<>c��Z��4�0	(�:H=�F&�(��I�_���PR2�Jr #$�FD�c�L��l0.}Dxbzz:������SE�\z��������������
LY��?�)[<�]?h���9�~[<s�������TA� /���S��u�_�W�2E�����J�1��U���#�&F������{(��0���s'������*�c���aaaP�MtHx�0"���@4���S������"W����x[�dI��.�`�\����VIb����bhL��~1UM*%}���_����0�����#w&?�_6e��^+d����-]������O�jqo��C�����S�;L��3i��-��V��l�bS�r3�a��������{|q��*ll
 �e��B�e��Mexl���M.^$4F����6���`���+~�R���?p$#e"i���J
�Wr��R8�����rK!P8X�-�����L��x���������w����w`M<�d|.c�?�)K��]g��6f<|��^��7��)���*��T��1��I}rV���3����'Rk�sh!S~��,B�Ss��wGLe������J����-Qh�W���kev�E���c����|.Ai8���^��
�R�<�cK������K,b%[�H����M��_I�\��?�/�(���&,���~1����)���b�h�.��l��������?2�F��"t|y��j������yzF���#"=��W,�]^���*e�*�5 Q�'N��ia����M-^�����Z�6M���~,��q�V��Z���
��n �PX��{(m����"�/�Z����8fO<��g�A�&�(+`{]{-� t�c���u�<�KB�D��/��?�U�����k�F��Mm���	C���8 �r���C)��D�����e�/Vv������l"��m�d�O��,6e�A����MH[����b����VA(�7�`�b�9s�DEEa�����Ccccqb��5����K�R��8�+r���s��3���<:i%2d�&2�=�ta+��l���a9
�edy���� W�v�I�h�e|��kG����g�OV����S��d�dhW���|d)_����
S�8������SI$��0�H�������n�:� 0���/|mw]�'8W�4^���������-�9�C��N������Q:�����'�<t��6�9���VI�(�����>u�T�=����d��mcbb\8?S�_�)>���/h�V�:>�`��l�������u���� ]{h)�P:&l
�$��S+G8O<��Hl��}����4B�D�A�&�Jj��E���
�Be��l�������y��S0c�q�����x�N�l�f�^BkMZ��`�.��l���������##����C�/XN�N� ����&1ho����D���ELg��a0w��/��1vAz��	�����u'�2����,�v=�V�l�a�����$�W6e+�	��p���EV��G��.IM��yd�L!�*��qq�S�����},H�zc����+77��_�J��+���{��ay���#����/����a����3Z#v��F)Sb�P��7R`����O�
B�M���_��/��o���/1r �Z�|�?i��df�:&������L'-:P5�:��:��k����7��=�=���6�X�t�c��l.���mX�%aYB��xL���eS��r$=��������R��N�
��}��R���O���\^����������;�V�6�]�o
��^6%n����ltL��u:L��L�SO�{.�)1X�b��>"�����8��
�S�����aS�D�<qf��e~�I��2l��iq��C��^SDs�Z8�\2lJ��H������b("�S�c(���Dg;"�G�lSsab�[f�+�*����%��MX�Z�����6l�p���*�_��/������Dx��D��<�R�TU�M��X��0�u���;�xk����~sIA(�W�''���(��P�C���cV�jhx��5kRRR�N����l*��]�h<H��]"lU6%D���vDn��
��M�ax �E�=A��)���[������l~z��FG�PB��d��C��I@�����T����>~I������Kp~��
�JQ�J���l�Egg�m7lR�I�<�K/�Bl�6������8h�����q.,���+e�BY��v�����1b��a3f�@�'��S�1i���k�x�9�K�
��^*�K�Q�dS��<U�I�;��s�����a�>��)�H���U�r������S�7{9��'�p*�%���W!Wd�e	�"��bF�'�qtU�/����e�/";_|�B�k�M���X_|VU�M�����������}��������TJ�
�����:�;v`|�������K�2�;o
���5}<����_]{����	�T16ePY;��H7=�@T�C����a���)�!�����4r'�4�����"�\D
���U��������s���Ea�IH�qr��_�O�������S1�)���������1�p�bS<�5��lyfl|���.���%��'��o�(��*����1c�����!�M40���q�bST��;X�,"�2a0/�t"���+�?���]{�Y;hEq?�d��zl�2��5#�����9I.����2��/D��D�8o�C4��C.���v�������l��X�[&R�{s�#�u*`%D���T�C0"Q�	k��d�	��}I!B� Z�L�><���E\j���<��sp�������Up���z���X�),������\34�T�
�)kp��=e�Kcf-]���a�X��?�����*�S@BX=d�8���� w;����M�5���Yxx�����4��������gs����Xv��=v�x����{�9z�x������b��l���(�[��5RY~M]�i�K�uy��=������x(�YIz����>}:���MNRR��]�x�����X=5l��e��C�,'���%T������I��VY@�Oh��I��x��C<:&�oh2])++��)�'�H��J�<i�����\&M���m��L��Z�6�\��a�
xH0k�E��������� ,�qvI���
B�b�M�L�?B	����������4a��a�����Mc���a*�|=u�T�2���q����-�c3��*��s���=�c�.�Qg������)UxN�����MY��7��d�	�C����W�^H;�e!��eu���������m�Z����\���6M�bJv��VA�.p2%8�%��)����+R)~-�eMq���\��/���b�
D�F���n���\�����=��C�_0���
��s
O(n��i�����g�c�V�����7�����)?H� ��Kd����(�@9�)*���^�x`�%U"����y�)D�#a���3w;�aCBcN\d�<���T~'���K�eS��e+���m���W��d�5+/����D�~z���b��=FDD ld�(�caV��FU-`�T���O����:$�Iy}�94	�e������JH�Ih����%��F���������^���jP{�����X��d�l��!Ax�����Hx.CVUeS��'<�������w�����R�����eS�����zI�	��r#�b`��]������3���@�M������f��U����J�)k1rN�����5+[����g��Ek�5�>"�������,�mJ ��&4��q�������U��aS��,�����>	5������G�)	��������"��S��nJr���#�P�#��+�*nc+��{^�3;��Z
����R��<w~�Ua6��Gzf���]�~%TN�=;~�RA� /���,�Q��0�2M��P1�c+�.faaa�����$CY��78�Z�`�4�
��p\
���s��p�fS����u�M��,�����C�I={�� !��`��%n���d�%�E�8i�$80rE(qi���M5@$e����I�S�#Yj;�a�"t"��Q��)I�)��s�����C���`(�R6[������UH^��������Z��k���w�}���&gL�fSy��^���ScN�>�^��b��
�XA� ���l����3�>����`E���	�!�|���D�K:Z�5��#���Gc��d��2�0:#S}���8q������/�(
w�qqq�x:F���^{F�P��h���;O�wI�7l���\N�P�p~ ���
���������{hbbb����8A�`���|tc��^�xt|l	U`�����
��9�S��.�(�E��_I@��1��h�h{Z��!�~�)�?��B��)�v�j�������y�e�E�X�gs$aMF0%�(�Z"�"�h�i@��Y�� �����RC)���y�{��</)%��
_4�N�zC������4�Z�&�����)��r9uBa��Hif�I��M������5��V��
By5�eS��ea�)��iT�b�X0(��	&0�c���B�vTx��f�t>5�$��������y�]�&RA���dM2�Y"�*8�4�zA�j���7�G�k�f����E���U#��XW��f����au�p����bB3�����X���/mvQ���}����!�N]�6r�U�#/n��b/j7��N�W�u��GB�G�O%k��}Z;,���l`�g�Hd���j>"Ii�!�B/<�z�������P�x�^zX�8��1�A�!o�_a;�Dy��b�S ^(k"p'��
h4i��3w��E!L�$�$F,�7���T��aS��Fa�n����6J�>��7�����U�,h���l%��8}#0%~n���E���R^���_�%{����78m��h�0euO>�a��tcS�1Z���=dYid����8������������1�r.i;��������]�P_�n�}}b��~>~�����?�a��{���n+
C��L���5$>4��?��]�L/_S��i�M� T�WS,6%� 3 �8DI8W7�)fUG(w H��B4�s���j�|d����8�B6C�V&���ibsf�4���95B��h��Ni�g���v�������+�M�<3|��g�vX=��/��9t��q�N�>w�K	��>�s�
i����}�#rWr6���v����[y�������865�,�����\e\������������v<�;|�������aTn�����gW�z=q���.���Kq���1u�MI�Ii���	���5j�(���%����c�GI$�<���5,'�4�����_���^�5���ms5�#YB'�>��T,�C�G�E��
�����t|#u
��,�^����w���T^�*B����${�%�(D6�-��gXu,z�y����m}>vsZ�������o�Y��g�L���4��������c7����<�\�-<@���)���.9��q��C��e<9k��5'�d��p��'gn�Hc(N;�^K�3j���N�J��r���k�w^|�^d�s�;��%~�9��|M��y_�{}R�w�a�fq

8$Q�)�M���j@�X�����u�9�Nx6��	�(����`��R�H?
L	�J[��_�i<
g�2F����_��
��Q���F��:����h����.?Vz0=����Yg��|<h��fq;Fo85|����g�,80h�q~��#c��M��l��}	�>���3N����v�����{>��d��F*m�������v�F����{g��>+��Z~��i[���j�^9 t����A$�������������I�T�|dR"�"�bi�h�+�9�����Z�I\I�MYn��-c�`���j�*�T$CY~;m��%��M����-"	��hlD"l��C�D��|��$�������3�d����������P�K�l9�'�q���\�NN{/?�������������3���������g�,?�xTN����W��~���[>z%q��S�Vj6���p
�J>�Eh���C>����g������e�z>~gX����s=�i9���'�F�?�`���,�����U�eG�|M���|{_��x���b�)���7l
�O��L�2��vh�L1�ggg�W+$T[�n�s �)�a�Eru�J2)�@tgS�{�����~QK��M�O��ly�-r��������D��r��������������5�����&��<j�'m�E�U�s.p�Gc6�F��J����;&VfM���!�����)�:�S���a���>���i	�`�z:f��O�������Yq���C1�>�d��p*p�G}V�GA��07���������Aw�������o�~�`�� K�hE4EvS�$�.�������9!%�Ki����)K0e���I�����
�J=DOB�X�'�Q�Ji�{f\JD�\�{���84UER�j^�P��q���l�n����������[u���p�����M������<��ZH���������������B*��9��o>5z��^�?3{�S�;*�����5'���b	�������i���[��h!��{�K�?�.v���Y�\~���c�;?	�<;~���O}��p�9;��g+����e[�U��r����� .9r$~�L1f������j�
�)f^tv0.�Q�(�M��|A��eS�_|�er��S���m���������l
��Xx��
�r��Q?2���}����Yq�O�{���_}l����_tyr�������f�����Fo<�`��3|��3����G+����,9<{��5,s�L|�5o/�,�Zh��S���k�?���������|�L��I{� e|��`P��i�7���-���K�M��sF������?���-�l�f��v2t�P��Xa���ir'��k����[7dV�z�b����Pca���W_�U��M�j*����U9��0����u|,�5~9��;i�����T�����J�t�)���[����X�������>o�p?T��)[�e����w���m<��t�Y��>Xz���Y;?��4#���gg���vS����,��!9���0U/2m�����/=����b����7�.����c�Kc�5o_�%�����or����O=�����^�&����*MA(/�]�[\6Ez�1H8�$0�cZ����X#c%��i8`�CC�������O��� :D�D�9�������h�����C��.��A��,��Y|�L�:;%���1;��}&v;�p:��u'^��u�������]�z1]�c9H����?�]ydR�������}��Z����C��\x��ad6��F���7���I�B����u��C��2t��o����O?k��:��*�S�
�������u����b@�]��{�$���a��h*�DA�iE���>��x
�*E��F�"�'%W�4�M!_B�����V���0WE*e�����-�pz��/��8�	���+W����$�aS2Lau����c6�f�j<{")z��=��aG�E�����7O�����4�f���C������D�8�F������Ma����|&~�'�O��j��swn=3r��'g�2��N����s���=>s�y�[Go���j����=1r���{�z��*W�,��
Ba��62����%L^���M����x��
�)b�0iL�x."����XY/�~��	�	��R&�[|h�{ZO?<t�!i<��d�G������r������������%�~7e�%������_:�R�	�B��mc+��r���Rk��Em����������-kh��H��uC7B9�����C��@~2z���'�E��L�;-��q�>� ��d��`������`���}���E�{9�/"�"^h��L���ce�./���\���.������j�L�(�N��D��/T���-���4��"M�	�'E�~1e�����$�aS��8k��+N|���s�<Q��d�{�N�7N��C��YD3v�)T{�P�h
�x#i/�`v�@����gbw�T�5}���v������_�Yv����H�����&������=}WX��V�=�"���B>I����,8���1F�|'�Z�l��^����N��,Q�`	�N�S����[��<���V�.y���L���
�Z�H*�"��e+&�����o��a���/=�'!�dS�l���_���^��������
�rX��r��&�vp�S� �D���<������6�����>��o�&�wT^+t	9�_J��J�.V����s��� ���%>�,+�Y���o�@��9(0���|@�/b���P[h��+����e������o����8�J�b���@���u^��H��`J�Y�&�OB�
>{M����{�m1e*[~�jP���$>7XJ�G$u��+�����������EC�dhj<k��~b�����|������Mc�[���+�a���f����*��TXv��m'V#�������R�NF!l\�qx�5,7�y�c�a�^sFv��.�Q=�)���vS���l������dm���n�n����%KX1��|F��)[y��lu���8��p@PT���s3��`����R2����e��#�a��*�%��d%�=�Y��$��'��<#���k?�<;a��=�|Lz����_��`S�r�aK�M��7fC���QS"6m��u��)g��<�b���Y*oJ���Q�$�*�P���x�����-~;�[�`!��$(=���:W���k^�P�)�7����5L��%�Q����)��R^<�����)�v���q)�2�\������lJ���m��:����H��8��`���8J�U���e�	��B	��3�0w�����#���>qv�Yk�,��e�C�")3��h���e�Y=���I@�$pFJ<����oJ��C����9-�M�j���k�����w���/ lFlRF�v����+�[��oV56eY��N����:�3O�%��>���zcKd�#*{6�D	q��
�^�/g����G3
�tU�M#�^Uj	��\���G6Y����)���l��
�FS��b����YX�gi���'l
�~���	����(�rrr���E����884�����	�!QC�C�$��[�r���aS~�����Bl��������E��$o�5#�����BBc��/Z��d��K�p���M�H�j��~yjdS�����b[n�)q�i�X�*|O�]�t|�����%���
X�|�%J��P^�P��)��V�T~���������M9;��m�Y���@uj>�a1��%T.�Q�`,��\.]����El9��A������X����c�Y���W���<5:ath���9�C���X����v�Xrt���#W�������F,;�eN���������5�H�m�}�W=,�I���M'�zi��������o�~�v���V���pU�h_�I9�����S�zm��G�]=�gP<��j�A��wa_=>r���+��/���eG������'ry�L�3,$60<.*.)9u��5�v����2�������Z��a�_������R'0l
�$J~-����#���0�<����<R�~�f��Re�����$��u����%�0L=�'~��k�D�p�b<��}D��u�#����pS��y���S6m����P��Q5#r����w�	/_S�������P�;�g�B;��M$H2#&b):�f1�"��7�r�!l6Q�)��d�m�E
��B/�!Gb�\�Tc��Jq��q���4hh��C�
>h��ac�z���atD�I�]��t���[�����zF��3{C�����X�����=��������4Z\c��Z��|�Wcl����JX���W�3���'����c���i�p}�_�i����[�L2gU�����.�9�gPP��a+d_
�������'fC��
=�����������w���u��.�F�
�8u��	}X-d���
2d�����`h������3[�+Z�����2����i�L[%�)��6_a��MA�0�B���3AcmC|�����CXG����)�=�����I��_E�� Pq���������-[��^<)c2�0+�k�-`��S��
�Va^�/y�q�B���(6��8��������H�Ha��$������g���<m����s�N��b��](qI��%�K^(�;xV����	�}�7-&���c&�N�9'8:�WS���
�?r���8_�)����=`h`htH��@�����!�r����r�_4y�4�����
�0w^\|�����f����9#<��E[�iq��F����&i��Ji�4Z��������)ty
l�(+��V��������x5�@�y�-#��#H�TY���2�j=A�,{G�k�!K��Gv��>-���������58>�o)�����L��#�������E�������(����I����9���?�����4D�����<��;322�0����Rx_�SR>>w�g��S(N�>- �����_��_sh�V��^��X�y
Gs*8X`EIM�7�
�rp��M������eC����������T�,���r����YY�����u>[��'a:QffF�>)�B7FI�05��/��-?����{��l(��Q��1�������cF���8=J�U���>��$�I��x������L>)P
aZ���	���R�u,3������m�}>��Od[�W����lR����{�[
�ZE"
�B%~��V
��m&%'���r�-up����n��Ux����^^O]���8W�WV+,l�=d�9a���'O��,�����Ur���N����)��o�s��-[���99�R�<��}[a���k���������9�
w����r�f��%��w�^gT�������"�^�R��{>����[���>������\z�j�NX3��2�f{Y~�-)����P�0�A>�h(~�[I��p@��|5>	Sa^qiT��VJ�.�&�<����I�����E��S ��7:��������]@����b��������h�0>Gq�x
a�o�D�W��K�T4>>�� �F�B�M���(�^	H��`�`��:�&<J��;t�(F�O��,��lp�� Q�������7[z����Ma��[�|}��W�X�y!���v��/�b���
��s���w)G��fY�E�i}k}�d$=�����>/���w�}���H>,��(�b1���0E{`I#����
W��@)1�i�����\�0�J���<*��t/��
���x��Y���#�����8�3�>������Y��`B)1_|������D��a�D��")q�dD���!C�`7.�tE
����lfI���p\-u��
R ��^R[/��@�}�F�a�)��d�������G{`��<;�����O0�������A�Y��"�z
��R��|�}/�}J�FU�|���7����L
0(���E�p����l�����;f��mK����\q������a��A-���o��LF�5�.�F���<���L�2`�����.� �Z��1�������<2K�'F88�}������B�[���5}���z��w��[Wbl�$"/���I�����?��m�q���cGh��o���*���z�h��o�����P.�7�M�]�`���Z��������FL���A\\��c�N�x�q�#j _(�HI��&��-��1���$���T��+����$MFF5�������AO!��.	���RRR�����m�Ja�pI���F��8:d`��>�%b�!�Jw:�`��<��,8�r0�!)�`�����d�5<��x�t���:S�6���,�B�����;Y�z5,�C�<W=��{<���d��L�0�@��2�S�����������{���
�)��)�MS�N�:����{	��~���xYt�"��8����%�I�ga��/a�R��F���UW1�\{���>����z�T����>y�
[H�����^�2�>��V��{/�=z�p���_���n�����-��W^y�=� ��������A�3��/���W\���O3�^0��	(�
{>�X,��P��������z��D�I�<.��2� ���}��5�&(((�>��=<2T��JvY�p!L��]B���d�e�O
��5k���3���(�q+�_7��%p ���P�Ir��T�~��#�tP5(P�g=��G0�� ���"��K8�`!��(��9h� ��� QD%M�k����-@�|IyXIg2DHN�H�h�a	��3M	��E��QU�J�1^s�5Lv�����������;J��IQZHT��x�������T���^|��~���dD%��J~��L<���"1� ��I��3��8��l�r���J���?��XY�����D�Y.�x��:u���L�V �@���/.Y�����!�$�(�9�w��Y!Db������xll,��	��&J�/�0�k/1CxP���V�\��:T,"A�a_�p�E�!i�{����D�{D7G��VyR����PRIJC��x����7���
��;�O�U��x���I$�"��@��A�e�J1pGOJ(0
<
������!����G��
����@�"@��#37w�[�����oN�	H�����	g�T�Z�
1���c�U���[�L$b�K.��	��V�Z�x	���C�Dn����{���%��������7m��;2s����_Lh�o��6�Gg^a�vfdLX��>��,�o Rk�����/Y�j���7o�����cr�)FL��7~��'y�Y(�]�vLlf�# U�Uj��%RnD�.��Pf�js/P����T�/��l�`�X���B�N(8��f�7��@���A'���q;>6��{j�tO��`&����Sw�W����#�=UJ��b�>���� f("�Ab��>�do���h���2L�g)	-�����*:�^o
�G������Pc��F�;�)���X+21������,��������)lBn��v�a&�_~9�)�d��`)��r�yF>�[�|����;��B���� ��.$@k�}Al�)�����m[$�N$!f\B�H�O<����0��� Q&��/�3�Q&I�,�)8R�5`�d��MtQ��AgG�
������Bi��#�Pp(���MQ��Bh�|��v&}�Ma�vo&e0���-���%K�d*��O��;��A���3�6���T�g����<8���A�
{��R�O@x���$�Z#�����)��X��5/Y�WMP����1��1(bLL��}J�]3z�@�������*��8+�&"������OKq=aK\�D<V|���(MH��dD��)_��bLB�/w�29��j���\Eb3ZH6�#/�$��X��2!`���.I��$�S>�9�4^w�u��K�p��
���u��7���l
A����H}��@����K3
A���U��X�Jr�O���@a���F���$����q�+s���>�E�d�+���x�z��5k��o��~�E��8����a��,������F(��!�q^2��^{��n0\K�5P,\�Mq_S������"GBAC��}����={B�D��U>��Su���0l
������B#�Lh�).�j��5\�r����+~�7�Q������R#gie�� �,z�A���4r��S�O��v��i'g����V�����M!�>�[����j�dH�.� ����j���py��W�-�RpG�B�f��������l�H�H�ywgY����"$�������	�B3�%VT�%����&q
����d�� Y21pH��Ma� �RS�|�o���eji.(�.����!�D��tl<���hhgh��ZT���lZK����)H�#����)VFSa��p�!%��3�@NX��)kq�q�}�aa�K� �`�-�0m�=��.B��dS�^@���B{B^�������[S���^n-�������I�9��d���B:w��7R����Q��)��)�*���,>t�e�Z~Ex9~=�P) � ��Zd�B�u1��}>� 
��������}����g�<an-�F�F��A��W�^��bn�S�N��#��W_
����>:XEL�6m��	S�D.a�D�D��%4�����%��<.��s^b�z4�������A�FY�A��4i�GG��5���#����,�k�&S�K/����bU�*�����(V����$:Qw��i�A�U������AX�.�U�[(�U��W�gg�1.}�t�������\����,����3��Pa(
B��`�Dx����"�q>���c`�N�@�*�u�D��|V`�L���e���,8'=�M�	`�%jbb$���J:�:WX��&���UX�sU�5���T�)"5V|�f�_�%��|de,<�����:i��Y���#r�U	g�������Ogi�9����*N�
r�yZ�J��v��ye�s���w�W����U��d���"���l���\68�]��Ae�f�2Yo�(� ���7�i^E@{P��@�[(��"�(���N�e���\ �-E@P��B@'�2@^A.����"�(�@y!�} � ��zE@PE���������*��"�(�� �l�?��>�"PEx��w����<�>�"�(��"�(>G���~�i��*��"�(��"�O8w�`�V�)������z� V�����o3�L���g���x=UE@P�
�@BB���_�Fc��?�������y���.����n9r��b�f�����Y�����YV�zu,n������
���U=v�+vf���.���{�INN�[n���p���s��-��v�J�������^o��[K����S������Y����"�(��"P���g������]h	3����k�7o~���9]�nyy���


"�����4i[������*�@a�W�x`+8R���������K���nH��K�:u���x������~H<r�;���y�����p
��`��+��"�(�������X�!���+�=������\���5k>�������2e��GT����~*���Dm)�<���9n�8N������$P�����}��Ab��l�R��l�CH������/����"�(��"P�6�w�^�U������#��'O2����q������KI3m�4�|���r��N�M
:T"#""�����rZ�V�'�|���?��RM������c�!l���)�4�Nk"5�T|N�:5g��O>���U��a%;
��Rb��m���������#G�/������o�!C� ����\-�S���������Xxd����gY���{�,����]b��LJJ6lX@@@ff��U=U*8��J�y�����m5����u���6%o��V�~�m�UUKV|�����/\��������dg!8lAi��):,:wQ���D L%g����r:~�x1kDtLzL#��9���lN(1N�8!�{��G�.�����<���c=/900�,�b���/�>�1�a�E~�aJ����x��w���Lc�
���M�T�F�j��-��_��E6����M��#��/iD�W^K�EU*&c��M���?���OdS�0����Db������a@�
�5>�o����^z����
��V�_�l`�'O��svv���]�v��������|���Y�Jq)11����9~�8T��!"����'K�^�L��PU����!7�w
�y�>pc��I�E����g+��rG�H��������
�^�P��yNN���6��=	�����<y�dN���N1�1 c��!
�%��:��p���+�BD��S���$�XF��v��c��g/�r-�+S�H�YzIQ5��5�TXg���������[b4Z����P>+��,�+�����5/�� ����|�������G�S������IIv��7�|3p�@J���CI�UQ�&M0J1b�2�.l�������3f���u+�,�M�g{��atO(����m[�cY����1wg�`Q0�W�������y CUb�(� �Y�f�&&���[7�	��i��15_�ho�^�z��`��W_q�,���gO�hMz@�^LXx�]�V��l�IDU�Mn������gX3�p/'A���38|�p��5R�����!��?B�
2�����w.�$��������'"��������>Z�W	�]�c����7�*p���/�@#/S�<	85���M+��  �6u���3��p���	���x ��H.q���+����Y���@�1��2�`�y�������_?J�<�����3���crQ2�"�D$���Q��8��;7BT�����"/�U�=���tI�Y���/�p�������pK�`S�H��7��5��P�'a��II��lL�xXb�

bp�q�FR�d8..���W^	M6������ 6@�+��L�|>�Z�j��ELN6�"x����W��t�����;�>6/���l`��(��"�(e������_]�(l�9����'1&L`�i<�I���c�	���k�8�����M���)��A�Ea�%��$�pdd$��*[� 	N6O��_��!Y`S|�p?>��#I���5k d���1�eX�`�4�"��������k�E�F
��	t�`�
�2j/���/;R�'��>�AY�Mai ��M�6��7B9p]���!D8��P�S��rssI�%1���
��>�]�V�ZIJ~i	"�B�)��\��"�(��"PEpgS2M��K�.eEB6����v�K�Py���J��Q����i�y_�k�M!B�Fi(�`8�c�/�Ki0y�9�_}�U�!=`SP���a�����<�_F��wgSX`� 7�E�����2d����m0%	9�6le	�^&#vn��P�J��!����0��Q���E
����&��^���Ma��0�������J"=����E@P����;�2�*�<L��M��3�:t�KX�`|e�4�TQp0��aSP/�faS0�b�H_L�
�dS,�����e5��M���Nt?�"+++�!qQ��r�,���`P����l
�s	-*
A���� ��8q"�PXo"�6�����@	6�L��P�X�1$0l
-bV�Ze��u�]����#��OR�rQ��m�k@PE@�R0ob�#�����E6�dS��`G��D-����`��aQ��TG
$���]���M���y��g�N���Man
�B�H�E��hM��-�2T
��{�{����-"V���E�)�UB�p$F1���w�����L�`	���x�sY�gdS���
F�-a�-�!l�K ��2.�����z(nMQr�v�f�?����"�(�@UA@<Aa�������4j�M�P��6L���� ��� <�M-aMHH��$B��P��W^!����b!�2��a����]:a,�0�&w"����`�Y��fX�b@��6)	cGM�H�~��i8����B�:�YW���m� N��INS�pOGe�����"f�U��'�'R��g������6T
s)��?i��L���H�4y#�M	��Q6�������,�������"��B���Xd����9X�I.=E@P�*�+R�u��	����S�����p��������;v���X(����Y�f!E�f�p"�zQ�BsJ<�b����!�B5o�<I���yG�F�����KF��R%9e�'/$3�],0qD�����4����'R�|R[dJ��@�� �O�B
I�*C�'f�hK�n�Z!��[t)
�����Q��ez�,�4b��v�"
f�xQ(���!cdw��^���aE@PE@PE@PE@PE@PE@PE@PE@PE@PE@PE@PE@P�
�K~����z�X��
YC�e�p�����(��"�(�@�E�����u|�@�3���&R8��,8���,�si�"�(��"�(U
�?����[���8���	������I��@<��BP��2�h@PE@P*x���&l!�s�N�nr�����i�-�x�/`'<��H%�
�p���]�w�����q��79Q�����6qg�)E�Y;�
��@b���MI���{�����5�(��"�(�@�"ua?������%*�O]D@�����S8 5\�r%� �����{L��NqL<�m��q�L!o��&;�7m��3��@��e��`7��)���-*�4��"�(��"�(�3f\���9eb�z�)l����9_|�E�(�Hv��I�G@��gO��)�b�'�g?6�"cw'v��x~����7�z������*�B,�>V�L
(��"�(��"P������T�tlE��Q��*��������:M2x�����d!�bGT�lJ6�4)U���9��{w	�TVVb4S�E@PE@(w`S�����j��=�=::�S�N���W�fQ���n�	% l���P���)��'��M��(*H��4�(��"�(�@�"���+�x��FsG�0#�i��w�)�s�����	�����F�" ���/��R���)�H���?�2/.��n�J@bf
��"�(��"�(������
��F�����/1��p�
���������$~��I-[�$��?�0.HT����G��}�0M������������~������n���@�xH$�"A����c1���OS�{�QE@PE��2e��S|�m��Z�jJ=����J�K���4iB<�4�!'������^C�?�	�����)�'L�8��{��Y�f�������AsS�u�������k�r��Y�f���F8f�k@PE@P���N2��'����K��3:yHu
��~#�QE@PE@PE@(.?��#i��R���~��5�����qqq��u+:��U|��I�=���������K����"�(��"P�8p���#}���\r�7E�/��/X����a.�^�f�}�N<~����F�= �N�WE@PE��!��PH���!?���RF��g��A�(bH)1�����H�]�H@�o���i.��rSv����L	(�S�!c�%�{����"6M ��}��y��X�� ���3�7w1����H�xrI�)���z�1���N�$�mI��@���,PE@PB��u�^x�5������=��-����QlU��]�u��E#���	8TaK)�?��8�# $����+Y�r��w����d��a�Sq4o����EDD�k���05j�`������7������L!�
dS�f�+��j6�7l���w�}l����sw� b2{�B����j$Nx���$��E;>��	O�
"YDS�zudk���@D�����c�q����T�E:l���d�����>iii���[&�g�I����"�(��"��dz��!������!�_9�l��0�~Y�{��!�� �*�^��0<
.�J�0�N�M�B��MA� ?x�#��C`��9��M�6���N}�t�"X�������)��@��E!��@��M�����Ra�"�F��=���l�.wa�RV���� E���*L������pB@�0wd�4a�������d���8��j
�d�.������(��"�(����`A�S\�:��<5R���Oo��Q:5"�.FN�)��������A��E��L	I[4������)�8���/�)��

ugSP��r����6��O���]�<����w1R&wd�-�			Nk+p`�|�@
�r�)�(6�,�����3g�P��qz,ZN�a�w�q��TE@PE�/�;��u�]�*��N6��'**
�u��6lX��

��k���,��E�@q�v��)�~
4@���c�aS��L9H����S�/vgS��Z�j9����Db�P+�!^��:8�C��M1����M�����������h6���.g������)�a&��.��j@PE@P�(����5j$�dSj��7�\B&S4�B�����*l
��A�>C~U �b�+d;0S�#w6���@�M2��	��799��;�n�$�!;a��aSX:�'�b�����*�M��������e���l� �E@PE�* ����b�#O�I�5�\���H�PiI<�Tf(pW]uURR�0���H~��#�)v3�=��HH�W\qa�6�rK�����&!?�rI.��T�i����z���G!^G�,��!@��_�*);w� a��q	���~+1��KUHH�G�dR��1��>
-�\B�%u[�t)JRs~E`�|I�/O��)��E@P�C�k���n��Q����w�F���8F��;��b`JB����rK���a���jX��|���,����+2����
��{��Gz4tm���B��w�V��v�r�d��@�0x)Y��/����A���cG@rd���"1l����i�h$��^z�%t��@%�p?� ZH���]�q"��<OAb��8�)�.	��v����T��"�(��"� ������!uA�cTil�.&����76���d��A
1\������rXd'�� i�������_-�����s.���vR`
�,�4�	'4k�LF*�t�rw�(�(��mD�(�.�Ii�:4�K�Bm��`n<�9��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"�(��"��+���������(%F����g�}�����t�(��!�z�>���<,v��5�e���f��d���@1n�8}�>��g|��wJ�������O>y�[����O�
���������9S�s�/W'M��~�ob�����c���V��j��)5���*W���%C��������9���;&Y�.]ZD�����

����~g��y����C������1���_��W��%E�sh]��c���&���������W��,G�-:��]�0a���3�<��_-Pt���DV�@zz����y���~�g|��gL���F����/Xr@@��s�NF�x��o������K���;�����3Y	�|JD��d/�,L<)�4
/�2o���
7.�[h����	������^7G�v�<���3g$�RaY��M�������,�\X9���������#F���|��p���u��,:�q�Fi��N�*:��]����y�w�}�<��?�,�-^��DV����y�K/���m��%�y���H�����jTt��~�-u#e�$eff&��������+W:����Q+x_�OJ����A�s��On�}!�t�N�#��?xU(�Z�j��-Z����C����TYt��O?����~�b���RN�2E�@�`M�����g�}V"7m�$�����+ u���%C���P���5�\C��<yr�p�\4Qi��O�����^>�)�W��M�n��<x�n�LL%
����y��o���\SD�����H����<���E�B��\t�E_~��I�T���/��������&��Z�2��("��r��Xz��a�l��-���!PX+*Yie����?���t��S����)���o"��&M���]u����]�$#�D�?^D�S�N5�4�
��_�R�^��.��)��7���{�)p��pi�}����i����[o��!�/�D����������5j4l���~����&Im���^�:���������C��Y�����|�
������_�~��]��`�]����_�^��f��T������}]`��Y�fj���T�I	;v� ;i.���%����5������*���;s;�(-[����{�r�-
6���4W����_�u�������P��w�^s�/���S�N���x��w���|su��u��QJ������|��IF����)�7�������Z�8�{)��		0`��CEDDH!��W�^?���W^Iv����_~�*�HHf�~�u������1� ��\�K�St�<>=�dp0^=�Y[�4���{3<�������V.${�p��H��!;Rt��x��	��w��3�7OA�=|����-[F}x�H3���n�)/��7��/J�wy��W###)����r��i]�����2�����i����(�@�]bb"#�jJ�8q"�^�MI�\8����g�2���L�C���t� ��_���%�,��.m����W^����i�5j��E���0�#3���k�a�)/�FH�$������>���?�����'�r��w��dGVCz^+�a^nrr�K������}�b���C����X��dS��ovZ:�[o�����bF�
��
���/ ����?q��
S�.]�pS�j8O������P�#O�{d�A���#���c7��L^z:��+1�>N��<��2d�Z�i�`��u#�);;�2i$��7Bg�y��=���Q�y����@{��%��:�������]����B�t^�$��>Z�nM$���(�0���huV�[�p�\��y��J�}�E��hQ� �$0�����u�:�G�3L4�}�wm��4�B��h�t�5b��sw��o2�Py�NE9�D���B���A9�����E���R��w�}������r�F�M�n�5D�aD"����H}=aS�(g������|RObs�)5\2���HE6N���k�4�&�aJ�A�5��Cq��2�1&p0���
L����IRr_����G�d2ts��W;iP������S�vs/�b�K2�Ib�ez%��i��95C7Yb�!x5r�H%w��M�$�J�z#"	@ �(45�_�D��F� W�,��K/�*hS�����9^I<�oJR���&�M0������v������f`�;T�v"�<'y"yXl��t&��Nv�h��EHp` "��L�r�����;2C9�IiWL�H�s�)��J�r_!����$���W��>�F�������9���M�o���p6f�A�r@�GL�l
>,�	�3�E��)�ll$�)i�p����il���;�gG3�H����{9:#�������4�G����x"�4���zJ�!/q��m�0�p��Mv	:T������\�W��M1���$m@�'�|h@w�4z1� ir�L�"X�]&�q���T�%>�dt1_����Lg���z��B!L�Dn,�&��B�1v��9�BJ��8[�PiW�@~!9&/q�����rj�}���1$J.��<T&��4d��A�<(�r�M7qw��r#�;���������d�O$�H`����9���*/]DF�(�N@�)��fd0�)��I�p���C�vH!L��H�*�����������x�y���'qnnro�'q����U,�r�#[�#[RdK�U�bU������H��M�
����;���@��H�������8����,fg���S������zdH����*<mDl�4�:t��FR���#].����"�]��FI�F��#KOOG�������g>����|%E��s!���	@g���|�����a������x����������L<�f��D�SI���mJ�G�Mm��w�G��Q��`�2�Xh�vGc�#./��N�������IN��D����� �;:@�7��mQ��t6/>jG�?���u������ ���������P�#�{ (u�Jg�����0����B�%e/a����y3U���`��i�P3>�������
n
�FFF>��\��Ko�c��5�#Z�F��
LKW� Z&���x����7�^�5����+RZ�:�#G��("u��j��:1 .n�O��)*C�#w�0�.:l��+�&��A����5R���Q�`;��h�UB��Qxo���h�?��*�5Y4�����?�������a1����,W9����E��2 J��J���a���w��R6'x�gG�*��
FH\b�V`eM
�+l�����zNEU� "2�\4���.]���	F��Mo=A�e@����C�d�bA���Wo�<]w!�4�m�4��4� a�NX/��(�{�[�zB����
%���N���G,b0$
�R�E-����QWC)�f�����"�xF!�}��.��>0^��Z(>�L4�;~��"��1^ri��F����=z9��TB��&*	���4��x._�\�M�Wo�<Jp�����.<��Pb��u��FW��
��h�"�v�C,�;^����q��</��"Y���X(?>���Gz�����Q�����IR�U$R�])���*!bQnP=?�IQK�l�^IkU�M���jF����7t����J�,�[]��i��������TF�'N�H,��!G�g
���`y�a���B,�;h&�*F@����������$y�..uOC�������!R��T����I��\�-�
�
j��0������pJ��"Q�A&����\%]0��8T��0�>�p%���O�`t ����F.zx�C�V0���T6��]6S
��#:���t��;8�@<I\��I��x������z~�;<i��a���cL�>�q�f�(�)�N����]:r�S�Oa������"��V��*-+J�y4x��I.UB�G��1�.�������w���4�xpAi���!k����������pP��E1�'jv|�������}8>@VI�t�:
[4CJ:|
az�u�tp��( 7_I+�`{��?�Q�e`Ot��HDMp���8�)�T��_���w@G(E��A��=#�*���w|PA>N��������9�v-�tm_4�K�b������?��]���y�r9:-��)�G�[�;.d����`K�����iG���pD'e��X����Nw�?�<�=�;�� L)4��*'j.��A!�D���>S�J���J\:X�ao�G�V������/""��j�����.A�J���K����3�7������X0�RC2���*A�D�i�<)��LD�)C��bv�I��w�%Dg��m�K
�F�4����|��2�OD`����)77At�JH���0����9w�;����q��$c.*����R���%E;�V����K842"������������R�	�M�����[	=�<�/��]�q�����A0���k��/<��0�
�����Brw0��&a�^I����4��,x�r���p�tDSz��N���[c.r ��)h'��^Zy���_���L�&�?bQD�T��
��s��
�=48>FOSN���]��j�����NH����TpNY�F�v�8$������"S�u���q�
j���MA�A��*F20KHkR:��>�����o4
)=j�4���6=�����fa(�-`@�@.@��u4�/U1�U��3A��7G
�����I@
�>^�_h��W�mKWESa�CF@07��2MR�DSzCAJ���P����h
�GP.A)�����E���@\�����<��K����^�B�TC9��4��h�)����;a��Y�&`*�Gt���2���7����+���q�\����Q�E��4��L+&�h*��	�c�;�>�
@-
���U����_4E�`0�j�~���
��j5`-qES��K�"����!�R
4C"��j�����Q5L���:��������]�Q8��No T#����*N���GL\�	:������0�)�R�b���(��A)q���08��J������I?�c����-#)�T�EOg���p��hh������r�I�4��-q(���N� �rJw;A��kM�Yn��C�G���#����,��z�,P5�9\E'�&��FS�X��a�^��_w�,6�=l8�P O$�)�������h�9L&a�(O��
�kQI��h
�N�j i��������T�J�0M��<�	0��H�?�:�Aj���X(*Y���,UJ����)����0��D�J��0�jX������9xW��))+�V!�&����)�&���@(��������]D�>18:��~;Em�����F����`��M����XN��%��w4E��4��g�u�A�".�����d�}���J\����LuI��D��#�����Q�u4�`��������������-I�Xu�R��
�ZXv
�hh��������	4,��&@��D���������J@} o����s�	�`id	��`��)���������U+�4,�^0��
M�r�*��T������i����	]=��|7epCg�4K�����n���;�3��b/Q��a*@�hh��m��1x��7)!q4�WiPk":[���{	rQ���`,�"���e����N}{���kM��W�
I�+��7��d<���U�IO]���C:���g�[���&CD]u6c�����t2C-�;h\�H�(�WRX�"r������A������Z��Q4%=F�{��T��)>~Au;(�����4�I���zo5���4�� ���7�t@�R�Q�E���'UZg��KN7���q���H�b�����hiHS�
M���ZZo��|\������fQ�2�H�b����n�����1�){1?����J�Wcj�'�r����B_��K=�k�8�_pDS$6�JW�F��&�u�K����T �"�>
s�+A>?d���k����/n2qS$��` J�R�B:���9��hJ�x����?FCS ����ID7y��c�DS��������rQs�����5��]v�]6;M����4��a����4�".��Q�nJ���T�b�)Q��iX7�^�5W��Q��eB�?�N��I�p����17�.�}��'nA&C�j�a�@�
�4��4b�u2ckM��-w�$��,[�DS���}�L�������)T���_:R��:
��j;�����C�;J��e_Dx��3�`PAK�2RR���������P��Y��.�e��w��hhJQ�����%�=H��HSn�sg*O���(i��:���w�G���d\���=zG�$��E��PDq�����sv��/���TDt8�b\���%�C��2v/JY�����|���T���=��4I����W3��Q���jY���(3U0�P�EGg����p&�!����T�2��)�FDJ��[�e��+�!��$����y�B���#6��B���,�IY�(C���t�z����D��C3}�Hc������t���9MAn*�����^m��fj���@]�t�C��0naLW��cD����X��'�����;C���yq>8��#O6�	���f��&���^
����>M����2xc�b�����1q�#)��^���f����J_�S]��|�����5LSTT����=x��,�+������q�3�E��%�	��~��!�4�C�s6cNE&�\�H��w�Ow��9{rGS�H��Mc�������y�!q�N�,�U�Yj�bHKf��n$l�Q��
��i�����u��1�!e�f��W��E=F����{i��7J�C}A��,O������MM�G`�%�'��j��u4��\�WT�j�M��=F��9�����������)�1��<t���-��<��0�N���s�S�M	��[�+�[WDu[�:]7����vGN:*�(��%��L��
��
��k�M���p����?�RM�S��S���S�)=�t����I�_�-���^����zS��
Ku�zc��|^4�T�nh��A�������t�k��)�i�d��x�c��O�(��}����X���|��1��*a��zIV�C=��5���hz<����^}BH7���R\u��:4�@W���C�����T`u)�3�}��4g7�k�q�����Y��������#�h�4���
/C2<��[����H�E�!����Q�4iAh�]+s���Sw��^�!� };��<\�-�N�
?�V
���}��
#z'�n���r�:e����H��@#����F��U�0�Sk��^T����:O=�����B����Qy���T�5�Ra��;�<7�9�,��B������N�C�w
"U�\����y9��$���]�T�`�wW�%
1�,P�~[�����U����wMy� q]:<tyr�HR5P_�0��`����(������1J	;G����C��[y},�@����S5
��3�� a�'5�]�qH�nU��%����/�d 8�qU���'���QD}�fj������Y�M����8�q�����)�C��J����t8T����A,�N�.-=!�o\�/���%G���] m5mq���-��?e��	~��V�@E�PS"w�0�{�9����Nu��Y�k���}����7�jq�?��L���t�W\��P��G��C��,�A0nY��FD�A�F*����s/_.}���r/t&���5C� P�e�6J�t���`\�SO���Tvz�aV������B�p����}���!#�\�,/����y�#���j8DA����@�����c��2���R%���^����*0��'>!%�<���a�.s�1/)z"/Dn����)"^^t���~�H�	O�����8R��i�[#<�Y����h��"=%E�����?���+�^��G�����E���,������"j�*�Xx%h�����\��)x9R����#>a�Y ��1�r��!�5�����@�S']�0���	NX�������1O:(�E�(B/�4WN�`@��a�R��J�T��+c���Qt�l�+}�����7AD-�����������b��nG����'C"�i���%�5������I��#�`CGi#����zyQ"2�6��}��pQ`0�7�����BF/a�k,��W6jO����	��i�R�����E���]UxO*���x�Q�3���N�K�7�a#�����uJeP���t����1��O6/����nxR�A�t��<��a�G>�� 1�������7N]	I���R<a��<�
�p_�)�`�IQ��G4�h��vxS�A�}�A��F��:]j(��R6@���&_���?��v��n�Twu�P���EDDal
�/0�t��%b�����'�c3����$#�
j�l��0��C�JY�o0������4
 ���z����z�����
=�����h*�v����MH�U�>K�
v�n���H;u9�d
G��*Cc����QuC2b����a�a�F�,'��������H0&�v�aWuJ�!:LAr��B��W�����Ry����\��������V�W�M���(<}=P��Ic���lZ�e
��E�H^a)�S�9���K
8hk�T!��h p�K�L$�Ez��44�G�*	#��?���7,��%r��bE9�%@������)�
���P]�!
�T�%������&�
?D�*��t�t#�w5�.�`�cm���?��U���4���Q��d����o�P7Cm]	�
<��7-}��% �<���SN�i>����NL��b�����t�M�^�x��i@�(�4E��O����,�3>G�����x	8	hI����w4Y�����z���?����3���	&k
|�$M���x5��V�8��k�F'90W���p�������u���U/�D��>��(�e������j���J�"1������+�o�E����Q�1���0&�c��:!�����su����J�b�Bz	x	��xe�����3��{	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	x	$�V�\9s��S�N%h�}��������b(���z��;�h������cX���������������@�J�+_���������^^^^^^�M���'N��������\��n��E]�v7n\AA��,**����u��x�b��c���}��<x�������,X0h��#G�8����e�����{���s���j���������-��9277���3����<--�_�~���>{�,������K]\'N�=z��1c�;�,�����$�%K��`$����N)L�=�UZZ*O@n��-����2d���.0�!n�.]�XII	�{���x.�y���{����������XC������O���&������>���h���������>w��Q���_��_��oK���?~��'$x�k_����o��?���	����@:��7������������c�I�&��?��/&[T�����6m�-���u��r�-�0������?>33�����:t�n}��x����T��������(�����g�y��?�9��N�z�]w��������t���Os�&Lx�����s������Gy�[��V�N��y��n�����R?��O����)S�����_������:�'�'�H�O��O�����?z	x	x	x	x	x	$���>|X��&Z���������S������>� ��UUU?�0z�G���K��O������w��]�*H�{��m_����	����E�:����+����?��O�r����Tx�\�|�����������8E������E'A��S8���o&]��}�]�@�����ST�Ck�n���'G��q�{�����q!%���K�K�K�K�K�K��HUp�����f�}z�������>�)f���������9A��[��,x������4������Q��:~�xXR�4��0�r�p��?��+�����m����gO��>��O:��#GSRR)GS�B����I�0������(0G�u��p�^CU���c��5P�N����r���K�K�K�K�K�K �%���T��M����#�z���`����0��?m��;�����C����&��EDk����m�b�%�iM�~���0�W5v��`v;w���g*�`0,���)�G�I��#�4��/���h���u�AD�v�Z,��6
�{������������@rK+�������I�	S	3��Z��;6��xr��c����]���#j�����17w��AfZ�����0g��}���
�((�%�
��w�����W��L�Q�R���*����h�� �aO��d�2�syyyJ6Ma�����;���)��<������^^^^^^�A����	l�@�������c��e@>b��4�����Y������`��ad�����fr}T~~>�a2���;�O}�Qee%���g�����������=�O�<YW����/�D;������>�������^{��;��(�bU.�*++��?�C��Gh���mR0���Hn������@��>��+��34Q������
��D������R0n�!�cC�;������Z���C?����+V����t��>�����o;k�\�O@�������k;��?����N���W�*y�C�����/����<��$~��y����_�Q����';v��U�]���K���:�)��B�� 3����n��Kn��_�"|�b�@`M�T��T��7��)���_�N#����A>�x��������������@BK�q4un$�k���Yx	x	x	x	x	x	x	����C��B��Ew��zpI��U�����h		���k��}�^^N,P��r`1�����[-��O�K�K�K�K (OSAix��������������@c%�i�������������4��w{	x	x	x	x	x	x	4V��+1�K�K�K�K�K�K�K (OSAix����������@��_��@1K
�����������(q�Kco��Tc%��{	x	x	x	x	$��ob�����{��u����c�����#��/\�P�	���&OSa��^�)�T����Ry	$���;X�=����:t��������%��W���W,��K �$��j�=_/�d�@��m�4��s|l���W���;�5
�bk�a�����o\��w*)�q�]�d����E�\������!YJ�SNIIaK�d���^��$�i*L :�b�W,��K��`��0�Z�t)���K)�N���
J�Y�y-�7i�$v5e+��I~`���OKK�?~�g���-V���%�G,p���P��y��)<_��ll����,����1���"�a�Xn��}F�*&����^~���C�bv�}��'�������������3l�����<33�*����������!76�o��k�<v�(���D��P��:u�%�'�={�,Y����?~�8zHOS������b2��$�8~X��X��O�+�������{p:r���>�jB��z�.]z��-cuB66�(�ul��c��(�7!XII	@�$f�^7�����WTT\7p����<x�������������=�	���L� ���%���d"I���I���{7��k�����G�T���{��u���g�(�T������nQ�����0	4��Z\eee����~�}\����i�Y*�O$J	�d��I�Q
$���X"	�K&�d�]	�1�ag�����9�����T��%�O�8OS	T����1i51�4���K�g�%I2��i�4������y�j����.�����d���k��X"��K&�d��$ ��`���EO�XI��<M%}-���I��I��&����b�+�x�D���o�����}D��'}�$hq�������
����$�x�|��x����|�d"I��7M��
j7���"�ie��X-����
�WOS7@�>��Z������I�u�o>^,����L$�x��I�0h�����k�O��V�8��-�b�TV��r��ggg�I&k?-d�*��:y����GY�"�y�����:�������I��-U�x�Dz^2�$���&��<��>��/6-�$��B-b�B�Q$Q������Y��U��9������o���+�v��/�M��T�b��-'�j5
8&�6\�x����)x�D���o���A���g��
�����@m��PTSUXS������V��\�L��@ ��g���]7�Z>|����4h��g�*����Y��%��t�F����4I2
�kE4~��~
Go�Wc2N�$���^,����L$�x��`���:Y��%F
�ba�)����,��s}uU�����f��5�������������7n����,�*8e�m����k����;�?�������@��s���������^�����;�4�D� ��-}����=PE/:B�d��I��KL{�D��L$�x�0	�A;�<��c�6m
�������>���]�,M
~������.]�?	��:Ok[��o�,�j����#��[k�t���	 ,��m[�<�����~������e�G���k�-B���`��]A,%&1�\����~ioOSa���)��v,������x,�����}I &�TL2��'���y�D�������|p��Y�K�e�������~�#6�M�����Y�e��}AQwm�+�ZS���`K������D��.i4�|MC�a����}��A��f�j��M���K�����M���U�Pe���f�V�l���;�8�G��;��7uJ��s�w{�_���\���*�d��I��H#�a�X"��K&�d�]	������MII	^bP@�b���h<����=����
�w;����p7{�C��1b��
|���2K{��u(%4�b�>�����@�����s���'�b�
TU�a
na�'M��0�j��]$�
��O��� Q��9�+f���Z���vi�F�F�[I���\7G�$�h
�0^,���%I2��^	������+�
����g8f�2x�S/��k����7h����Z�g��8TI�J�5�� z*)=�N�q�?�W,�$�~�����9t��L��s|�T�����(A�lD���~�i*�
L+`��E7��.��tI���N���[Up���RW��'�B6{��Fh1�4���6�K$�{�D����W/��2�����5�73f�x��GQV�|���Na�<l���F�^�Y��tDK�zo
�����e���p�P�Ofp���5�]:����i��AH���w��i��|"���v��
A}����v���+�g�T?�h��l�Qy��$�fo5�2
��$�`������x�D�������1c����?����/fT8p f����M�:u
utS�<x����s�WfxC�01�O�k��������uOM1�'���p�g��L���5u���4i��Z+�V�L���z�DzF^2�$���$�����K�D���2�8�(�|:���`���q���U,�����W��F�I��<M]�D�8q4uK��������~4�
z�/���{�G�����EA5HLZML2��'���y�D����+^�5���$MO�`��r
Ma�>n��}�����?W�xH��i��59HS�u���/� (�7wI�����GG|��[���WR��I��I�����X"=#/�H���M�@-M�w�\���YG��kZ:�+	Z����VNGSh� (��8�T�Y���Y?c��9�����x0�F�g��B�I��I����O�K���%I2��ip4u����C9;s��G��I 	Z��)�4�t8���UF��2S~�0�P���dR\��%u��}(����oLZML2u��/�H��K&�d��$���._Yp,gmf!6-����B-�iV�#1OS����)�������7���e_�p��z�7S6��Ux���[��4�c�i�E���^,����L$�x��I�������v���EG�����l��&2�_�g��@V��k����~|yyyM�y�4Q�.���RRFNF[���4�\�7h���y����pk�n�V���b�i�E����U^,����L$�x��I @S5�s���)8�
�����Z\aa!��O�8R��Y+J-\�����gk?OS������Xc
��P�Q��*hOe���
�`����l�1-���J��Z��4,��d�p�b��i/�HO�K&�d��$ �b�������d�7D�*K�����'W�<�����'3�=�(�Tu��F����c=�/��Y�;���_��h<MERsz;������!�9���P�V��0{*~��L����w�$��eJ��b2N�$�x,��^,����L$�x��I HS,���
����Z\��������?����^������,n���t��	�0���wo������g�������������M��x�
OSa2l�SGS_zg���8t���[����oOw3}\������??~unQ�!i�B�s�1�b�i<?[�+^,����L$�x�FI���;������E�rWe�_]mT�IX-n�����Y���&�^_�o�����A��Hl���M����31�%Nl���X��9>�8<M���nGSL�������I��>�PY�����e�^�+?33�9����<��:W���8�L[�*~���X"��K&�d�]	�^��[�nl���8���:t���[�i
C����Y�����Z\UiUUI����kJ�PC��������rrC7��r�)�f�tS���o����������
���AM�E��sz'��d4%�r�)�TFsew�� �uG��37�����-F����8�L��y�DzF^2�$���J�!�m��O=�T���a��r���S�>��#l�����M����=���ee�]�H	Fom�foq��K�,9r����4h��}���k�"*)��<y�=��ydDy����W���NAk=z���G_uMI��U�8:�2��.�-7������R�v�!V�9���[��	��j�yv1�4���6�K$�{�D����+V�,��'���n�1������c�������LIT]��Yr~�����F>a)���foq<,��=�Q
��	���+��?�SII�����e�X���� z��
���CT��������P=�3�{��4�`IJ*�L:4�O�l�
O0�Txx��'sZ�4��j�yv1�4���6�K$�{�D����W�-z��'�^*//���s��=�����W_e�d�E.�������L�_�SBk�1X����1T��"���>�H>p��(!_~�eOS��T���������e"����[),������M9�t���8�����>�������n�I����&��$�h
�0^,���%I2��^	0C����&�(���������44��Gx�;w����O,=�����YB�8P�g�{��]=M���NM�T�Y(�p�QRU�S�9h�����bIe&��b-�	F���]�������
�~�
����b�jb�i�>W0/'�0��L�@�i`��������0�Y�n�k�^�~���aU���@��!Cd7ER�������):���[��$hq�
�������J��i`�s����x:e�Y3����=���D��%��J_r���u�gK��I6���>&�&&����#����K&���X�l�k�������C
�DK���a���+��M��)���CMa�~���5<O�5<����/�4}�QHGS�:���u�5��%(2��~�'���nJ�+i�q�"<Z�.�@~�����$^<!&�&&�6�j���^,�d�%I2���PC��	_��L�,0���8�,D�����s4�w�Y�����w���M��$A��4��z�h�0kLuN�������TO�2�'���BB\�E���otM]�=��G^�-^���I�Q��x!�	��Y��_��L�>��,���Mq�.-��]�Y��%��-��Tc��U��d�������{���2J��FaD���p�����K�+����m��o�/&�F�����_[�c�,�-B��y����I�b��)L��d��gyUu��V���Z�A��~��k�0�e�}�q*OS�Y!���������Bs|�Bn�NR=AJ�����X���TX
�S�(���"+��'�p[��
�B���{T����3 �+���2q\��I-���,�$P������+�}8��_���tSd�/�/��2����

{�;v,8��YA�N��|��Q�F��T�R0YOSAiD��JS����)M�	�B��HI3}����+�2�D��i(S�.�\�������3o��3�D���2���d�R����3P3��!&�"P��uz����I����)�2K��9��/��N��L�R2����(��[q�p`��5/��"���M�Yyu��i,������u%����O�!�@Gi�����|��vv�QFY�b:�&kv�F�e�T�aSM���.��x{���
��ML�)e�/��N���f�1y����������e�KS�EIe5kx�;UX��M:���*,k���9��a���/_��_��oW��/>
`�/�G����5��<|
�)Vg��iW=M5c�s4%����#�N������-#�L����*NM;��#�8
6#�f&0��v���36��U$��_L�)e�)��B2�7�z_(��g�����X^27X�I�]�4u���U'�E/o�����=��z$;�HNc�GrX�kg^����O��S�E*�O�.@BI��1l�����b *��'�x��0����z��]��u��H@�uS�m����hVNvP�V
�S��R����D,�k�t������*�}�q[����e��*�y�b��v� 67��1�qJ�.?QP��{�`��� n�^2q�h�`���]y%l���M���2K��,i�����a�)��c+L�X�K�V��<��BM�X"#;U��4��#���c��!p����T�bi��*MY�4T��,�p�-�B�$j��������j2j(������PC�������������n������a��l���|)&��2�0��o�~�v��Y\�>�]^2q�p�h���L���xo�7�R�/���i?��rdgg�wZ�p!@��{�a@N�a���?��Cm�m�6��j��
;�����J��9�r�r�Xa��X�U��T�bi����S�S
��8��T�����]����g��3�,��"$��Z��m�Z��MV�e�JHf5W�m��!.�o�4g����ML�)e� #���nk��g�B��I���dD`,f�F�,�1:�q��1n��X�4E��������B>�Fom�foq��,X�N�0���Z��Tq�
+����������O��{@��O�<���0Be���!�6�>OS���OGSN����v�(�<�!AG��������JJ�|��,��vj�S�Z���� (7!H`�S�����	�M������L���Sr)Q�z�n���g�l�o���dZR�I�6���2?���������������;w���7����C����������S����[���h��q�����cj���:
�WG����1���������1���=Aw�i��"J��)P0�����;�@!+)H����qW���&GJ_����li��*3c�:D1E��@Rd�#W���`��������[M4N�.?��;�_��I,&����/�x~:qU6h�	��`�[�l�����3���?���7l���G�0r0?�����NU�]jU�-����tG,a,y����X�����zS��
�X�U�YW��e^U�"q�P
�&�K��QRa�d�
+MAD_|+�`��b���<�F8B�����R	`Sv�PG���������:�����z���[�x�i�Vs�{T��/;��7mw����p����K&��N��]�vl��
�b�i���{��'�x�������]�z��}Wg����*��s(�r�[�#�[*,f{y����i*���hJVO�Z)h�~�gtS��I�%CA�R�Uk�d�z�0|;���XB/i��G%E���`�@�m�{�G��S��`���e�'�t�������1y.�xvx��������o�>����y��g)*��c�=���1d��{���
�7�9s&�W�U]ZxT�N�^+��nq��1�����y��H_��a��y�M}��i,P�Ex�b����	O�o����< �������&4Z�%�d�1h���($e��.`ea�e��n�c#?<�W��_����K����(�%��e
�x�C<�!&�"n��e�����|'L�X"R��lD�	
�����Av������/0�<h��p��g���L ��t�hNIUD��W�:����L����F�OGS�{c
&O$>���
��&	��X����q�Y�N��k&E\bsH	�-��(4W����������6rd������x�$�r���F���	�V�L�+�����U|�[����gqc�����K�c
��D�+������{�a�7�.0�b�#F�x����P����38������>;,o_^P>�t�T+��*	Z\"Z�����MSE������?��AP�N5:"������S��9x�J�8��
���o�:����=��)�]V�$4�J�(�?RCq���r'����1�L�A��jT�$���v3&���(@>Dl����I�Q�e��B_q���o�n+DL�E�T�J�%��p����iX��;fy8�(]���2$ki�2l�o�����sU�U��PSs��r���9�w��$hq	JS��\[-`�DPqk�y���8�* ��
��L���'�G�b�}�e�%.�H��~&k�d`�~��&�<dA0"rk�;��.Q��"z��pP$�e�����'~���d�#�(*����ugK�h��	T1i5��|���������kx��<&�"8����K&nM|���������M
x{��������W�Uo����E��><�_�Z��I�����J�\��S��B�#�0:���L�V�@�����(�0�b	��6Tc������O������x���"�w
-0er����0����X����5E���I��@��07����D����9A�u*	Ged_���uN{`��#g����*&�F��+���
��;T��?6���Y��u{����I��AS���3v�KK;�I����o���Mg���.�h��$$A�KP�B��fr�R�esLf��������e�Fjk���c@�*������dd�d)���Pct	t�wMk�'`c������[�iR6�N����O�j�ES�vL�pX�f��B(�t�n
���`<e}G�9�v��Y���e�}BNE��cy[���TMU���<�� �n/��}47�`�����)S0�bV�nBDS�&�;��T���[��S.]��?/��eW�>�u���~#�-.i�i>f�333
����k������	$�J��b�%db�C&�!�-L�Y@�V����Af�,�'� �"�H��!�i����j��C\�`"cC�Y���g���,���v�O0S�W�Li�t!�-k�XR���}YD$"{�^�Y��ULZ�����zMf�JL�Z�������X�0:$w/��~������m���W^����%>�z����t���}"����q�.�]���)�����M��)�5���Y�Y�:��I���4���`�a	�D�X���L��v�z',�dL%%0��B0	O�b���:+�2�|R�E��KH���oO�*��	��rh�N����H�>���%��)�^8�B�\
�Y�BZ�RD%�U<��������-<?fT1i5.SL�v��������\pb	�����d|5��)m���.D�����729�(����{���]�vu���K�.���<�G{\�Y<7�����-.�i�P�]����o�`��#��[4�'�U
�p�A0�K:|bf�����?>����'��Xl�)G�Pd���E`��$?�3R4Ye�C��#����H����K��:7glO0S*[J�(�����p���L�g�����e����)�b-6�S
Z�F=�y��N,AO����u�I3]����K=yn���9��9w��:Ma�X�Y2�KC�����E.$`u��l����g�1Pw���}QB��A�Hu��^��
#Y|9�,2�b�H ��s�*G!X��CH�(����|"����0��jz�Z-��HI)w!�UB*�)���#�X�#nb��|0�R�(� +y�������j�h���K�H�+������y���eF�V�V�2�U0��j����������b+�����2�g�^2��\bU��������F��RM�=�*M���������l���wGtM��.���|�l�.o�
���$hq	MS���"KGr�'�a'��B� 
?B	���S.���Q���	#V������^O�Dp!I�\�?�sJ`R��A/�������Ui���������e��3��5���-��<���Vo�<M.0�
��2�������~Nq'�t�����"�fT1i5�LK������N�z���X��L��d��5�1D6l��?���������i�b�i
�x������4=�+ne�evf_|,��g+q$A�Kh��j`��Q�����fH=��������K@�u'x,���
�g��@��Xr�����xJ�d�YY��PDs�)?�i�q�B�$�����_����KD_���p��������34��[�H�95�����#d�����2B��`���E/�$���%�b�MLZM0S���y��N������]����$�%��AP���{����B��o��{���^-..��k��
�%����;eau
M��T��^�����q�{\RL�m�;;�PVv�5��I�H���4%�LB�L������`������G�����}I�d��p�l��i:�M:�&J�b�
��Y����!Y�T���!�Qn��J�h7�#���$�#���� )��	������#����N�&b����4��}�oe�|�G��}'h������V����g��}�������xA�A�A��Aq��^2q�0��(,�\�>�s��!|Q�
���g�y������l��������>�6�`��e��h
����9������/�*U,����U,��;����e���$hq	MSW�.EYu !J������~����Z%�k]�n�`Vp2�PY+�;{��z�t��DV"(�n�f�C6;R#�wz�������
�Xt1����ws����9��?�/_yu�F�@�)-\D\mCC�"�;{P�����'�s$���������4]wg
`I:��qK=���`�t�3ve�47�����3�����Zu��i���;(���w{��: 	�L4���M�{���
��X�����	o��6R��0��C�������a���W��������A����j-�^��EO��*�g��M��sO��;��+$����%
�]������~�yX"WT�-'��:�n�=O����s�����?�w���"��GF�9�����d�=w����K������������[��UT�9�#CDWz/��[r^�7d����c��^���=��$��{N=0l9Y<4bEy�E�Ahe����:�W
�tJ�VX^ysG3���c���|yVE1.T_�~����K�q�|�����G���h��,��xi�����/���#�h��E�S���GF\z}������xBLZMX�,6��t��%��w�v*a�Xn|[���d�������<B�6m>���M�6����)?����=JI>�{�94W�g��5�x�����{�����?���+W��n�O�>'O�dG?����(����u�(���$l�9�z(�L��|���.�[\yy9�1~��G�s���-&�
����{N��:��+��;x��-�����������Z�H��{J�_�z�o}`��g���\���'rK��4e�7������.i��]����M��8����MF��a��������l����k��9�_V��>���_�E���wftG���,��1�����f����4�%��������W�+����J����Y�{O����7u�������9x��2�P�O[����#9�������3��q�	�OY��%p����d����O�pu����N�ac���Z�M����e���-8�s��uuYa7L,aW[���Lk~��{�ody�A�������N��@)^�	F�������<�����Q\��	G�(S�N��H��7�X�|9��;,!/V\������q����h'����%��!�[s|k���1c8��4$�Fc'�`����{5(]M�y;�UU��s.t�w�	�f�F3�R4�r�
��Q+�F�9����p��=j�^ ��������]Ty���)����������~�`��#$xg�y�V�y;I��L��Z�?�s�6
�Y<tV��_�)W��7gl��5����L��-M
%<�q���;OR������q/"���7�����v�wk�7��C������	D������$�4��aU�T1i5a��U�J*������7���sD^2�8	`^�X���Y?�����o�U,��������D�`�3u�G��,�����_k��������Db�D=uz�i�Es����Z����KK��a��K��%�	���<���<�|�� ����	,����]��R`���=�Q�����w2A&�Rt���%{����D80p�b�f�:b���~7u��'o@����Uh���_����#V�l>��W�^������so�>�V-rZ�]�F������������~�����m'(6e�"�3������"��]���*P[�B��;=�rw���P��2�����4�VP�L,�TdR^�y�?�������L�
�]t�������W�w���&��)�z��K�^��BO&qX`�����~���&�M���a8�`��];�������~��@)8J3}c��
K��������	�5�?�e_���/�5M�%A�Kh+t���"�
�4o�Rf��Q�R�#��s���AtS�F����p�����C�N,8q$�a�$-&�F7�>�S��wa
�u�"��9�+�0���r@���w���'Mp�����9�$���z�Z�C5��v�&hJss���Y��
kL\����g�Z��C?�
M�o?D�4���X�s���W����A9��0����(��Nm�V������������i�-��VS����Vt�x�x������i;O8&���jVs&�%���L�������zh��#����
*����O�4�	A�(��l�;�r�Fk����V�BC��PO��x���������l�A�TVY��C�����.Y}���%4M���m'h���51*@hr>�#<�;AS!,c��7j���g�
o�S.�t������$Ah��IFO���-�>��*�$���e8g��?!m&�Pl�j�����)���oSF��M$.�"���F��>Hj��S��N�Z�;�q��cl =b�H~��WrS<B�V)g�����;�#���/�y<�E��b�j�iY���d.^��q�baF����;��g����LB<�P���7CS����M:t����a�MQ�>�M���_��h�[�x���Ww�:���^wt�.��|��V��g����)�CP�0�[h����ZP�	���{�[����rP����R�a�s����g�1%�4,h==~-�k��MI75y���������`5<����7c�L"����#�������q�K�7�V���")���3�� 7q������2�g�F,�	}~�y����=��w�n�B$�e��q�*8��c0��2�bUp�D�!S�%3���h�JLuS����W��m�r�����(�������?�}���?]\e
J��6"��</����/�$���<���?�t�=����Cav~�������B=��'��g�����#f�>&����2�w��|&�}QB��F�G���
+n&�~�!��:���-:�S�J2�����,�`(�B��1���E�����jw.����K��4�����`��""���s���V�y&z�4T:d:m�Q��y��=}$��|ED�
^��W��P���{b��q�m]�T`��[�~��`�������M��j�D�6C/�L$<��vg��y7���;����>2�Cx�2�b��Rv�>�����G�Ja�%�2�?�|k����
^�����O8_r�#>&�6�)^|<����7&G�$��Z�Ax���`.Y4K,u�EEE:�76�F�)��TyiE������������U\H=��-���h[�>	Z\B���@�Jl������>U�&�|%���cyX��9���sE�y`��(@G�;\B�D����U�6��@,�Xf�\m?��-�B��
f����Y�t>�wf��&������;�m��%�����B�pJ1E^�����W����a
��/��B\n���kY��Ta9�q��%���$��T,3���ZZ��S���U�������� �]����DI��K��I�*�K�Y��,B�]VVF7�d45�����w]k�(,�����r���I�������i!���d�)��8�x�������fm�BJ��WXo{����D���%4M1���(�P?����U���]R��m��RC�f���_?h'<
Yia�����������������K���j^��>�af�������{w���sw<<b�)�5L�R������>w;+s�7x)�C��2a����5R��?d���������4Eh�{a���'�gi�7R6��!qo����n�(���\E���n�����u��ee�9>�2?�V���Lx;�)�
IF����V���O_�~=����o�����c��m_(��D�N�.�\t4wk����BS����O�����L	��&{�bv����F~�z*������~�fA��Y���]/	Z\B�`%�uX���,�%-\ig��h2,d��d\D\������
��Q�X2j,�dm���`�I��"s�^e%�]��q��Lm	M�!���G��l���2�(�Mj��Xc��IL� M��qd{A����&e�H��)r#;�h�j��I|����{�M��2��~Y��vR�M�������\wu�����kN�8Q����I����S��i!���d�KSH�<�|�w����G�����*������,dm���[��D�)0��^,P�
X(P�0Z p���/�9���%�5"�mt8��iY�XY	�n��}g��A�Z}�59o�j�2cS#��������E�8�Y��������bAD���wd
��>Q*����@��UZ�kn\0���5�5��`$h���%F�J�����z��>}���'K���`e{�I�Q�k�����~X&\��UO�>�����Gs3�TQ�H���H�y�$�cJ�BFCS��������]��3��<���x��]f�Z�*.I����E�RR
\���cyB���9
Z0����b �'�H�|���0�,���z���~��,�YPV���[3�@)�&
�J!���9%!5~J���4�����H���;��M��|�gY�#)��>V�D0�I� 525E�>-�����D�������R3��?v��0���	���c��������R���I�Q��7�F=5��#X�E��������W����8b�,B�^2	�������Sy�r�#3Y�sc���G���g�����O%A�Kt��= ��������!&$"�
��y��M�E4��0	�*�V-�Xx#
?���d�;���u.
+�G	�4��Q�N���0��,8R�Z-���QiC�DH
C�q�k}l.07����v
�d�[q���P\HC����s���N4z��%KX��b��SI�~/&�F��+8����w����������T\yqef���������=1y����K&nM|,�����^w�������f��������._�����Q{0X�����%4M�z'XB�
H��80�)���l����b���D!C> ����R %��"V�S�d6ns�/t*�!s�4�d�RJ�N
A�Z�t�dm�P<.��������������p�Z�@�Fi���-�|M����-�)����(����N�<���Y��������@LZ�2E�����;F�����������y%L��(9�|}T�w�g�p�����L�<��/[�������^9r$�����Y>���_n��-��G��"zue��~:�a��'��/\>PT�-��d��8	Z\B�H��I!d���	?��!��%
c����,|5�E#�t�c�	����3E��I9�*�$��Th�rS��< 9�Q��>Y=)s/f�j��=�	,�"
�JqG"(<u#����r���k"�x����|�	�t��=~�8{z�����^��I�q�V�U���S(�.]0;�1�w����cy;rJ�
9���}����������i��Zg�<~���XU��'�p���Z�z�SO=�.~\b?\���q�������N��g]���Wv���Z[�d������Ec��z\�4���	$v�bC�4Ha���������"�KN}��I�������	TMYD1�#�%i��Y�:NP��>)� G~��Br�T":C;����#�M��+�������X�T�D�M<T�������g��;:��6l��v��y��^��Q���TX��V�2�|���5'PO�����]�*[q����+O���>'��g�O�d|�F�F<����-�1bD����M�X�{����O�<���DS���6����r����'z�e�Q��,�,�(�M��s�*���W��1��LO�8��kW�*�����������%��
��u���NrR������������2��|��R,����h���+���^�b��r	��E2[��E9�O`��<Vs�P*<A5��3z����	`��#/���h
�i�P����G�|m\�x�Ywu�6b��e�������%��9���c�j��������~�����v�������W� #%��E��
�%����x��������Hl���2�0%%��W_UQ1�x��W���^:[<���C�A��i�&"�Y�;�k���;|���������z${���� �$�#�[V�k���6m��JS�c: ���3o(B�`8�R��B ��y�i�(��������,��lSK&����D��Dl
�d�[
Y����Yp2�g'��h�Y���9�a&#��oK^k�%���4	����$����)5�N��n}Le~���i)�y�h������R�1i5�LQO�\wr������H�=����K+�c�V5�K�u��v�d>�[ItTR�<��)����MAS������/�,��<��#����CG��;,�����+~���{�u���26��=�]t.���O:C$t�c$����P�n���@��r`I(��/2>��;!����K�c���X�2$�b7 da�$k��#2!�2��9)�S`�1Q�kuG�@k"Q�"���/�k"q��qpI<fu\�n[$��:����Q����Q��`_�8���1���e�
XQ����V������=��3}���u](�����O2�WRy����I��abI�;j�[��i.I&}:�&MbF;�_���+V�`���c��������|�I�{��w��oM
2�K�����3�c=5������M=nl;�U9�-
U��M�*�0 ����F��G�"�2��!�����S�@���HJ'CJ!�"��1���Th��/e��[�'B��`���D4�d����@"#���X �K�H`�kbY�2G)�k���B6T�T!���2�r�F��������RYYY4�l(�{����e�Y���g&��v��X�A	�.]��_��}�[�d_�X��Tk>��i�O�Q���{��o���}DD���V4t�s�����G�Kl�ES�v������D�����<�?�Xn���|�K������J�<P%+#�Ghah$��nir�n��K����U@O0D'��A!M`�:�� "P�F�E�^����J'�GR�bq	7G~&}[�P�p3��bJQ����H�$�O�N�`�����8p V�Ra]SLZM�LY%o����?�?�7��|��U�d_��l��K�}�P��%�a��S/�V���v��>�q����@S�|���wO��Gf�\�z"wG����-.�i
f�`�E�l�����U.�h���������@���2T&h�hY��]�EJ��� �������BvYx��"qW���6kcY/�0J���������^&#Kh.�N��Q&�����T�	�g���a��c���=�d�
X���}QLZM�LY%/_~�)?7pO�NK����.Zv<��\��3���DPW,�~G�U~/����OGh2M�5�������}�xp���\g8	Z\B�\a���,P
J$;��5p�S~r�oG�G�Z��p�??�!���)(�a-�h�R���u$�I��k3��P`�JEFxmvC�\�-���v�"Sf�CD��qq��������7�RL��if����_��b;��Qc�j���b���Sw��0���g/\�r��|~F����$�v&��"X��u�[a����`�/�Z�7�	Ee�az�.�y�3iXO��/kB"q%	Z\B�T-BX�lH?� {n�����.!��FFD�krn!e��#Y�2���W�^8dR������P0R�*���d9�x\5�R�������'�1pJ���:�H"�TI�d2�e�A���?��t���{�bsC��V�VS�Wj���=�����m������s��_{����,�����%�o;����BH>H#$`ij���'����^\��ozO[rpqfAiU�YO%A�KP�����[����v�
&� 
A@�|��N��b�3�����"�%�0F�d���8�-�Js����<QF��������2Rj'�.�k��E%�><�A.��C2"�O�����'N���%�~1|�����3f����H�����y��}e���=�����K���e&��|����$��U����K&nE���������75Gz���]7��v���T���G|x����{�K�l��$hq�HS�`���5����3~�x����������~@��M��vtUt��1�gA�!(���y��T�R��A������?aa�u��R�	$&��D�-���4�iyN����j	���������a����M��^2I�XcxS��
��������y_�91��R��67"o:3������z�(������%"M�i��T�9���]���_���7�����D�1��$
ip�j8�-3'te�����D2�d���"����a
E0��X..i��U����
�}�CJ��'��?�eOb�_L��TLZM�b����~4a������|��3E���1J���4dbI�;j�b{�4�$}:����!C��Ys�SM��W3�5k��)��(��Y�wx�#3g�?�R����T���)f�������;����2J7����������2�����m��>1IWK>v*���*7��F(e�������t�3s|�@/"����v!�w�:r��/F�R,uo�{�#&���L/�]�5y��/�z���G���z�T����EoX,�����^2�������b��qk�\�)X_��'5~�f����jWs�t�������;'N�� 3?�"y�FO���4���|�����a������M�(��UZn�S*2�����w��������%R�=��`Ns���?GY".��:�~���3g����GY�7N�Ah�s�I��n���J���n��:������sK����I�K�K�c�n�K�ir��"I����Z���5�����r�x&��X�1��:�/���.m\tx���uG�++���3	Z\��T�J��� ��{�n���E�,d���"	Z�*d���|�I&���������7�vv������mH��20������������y��S�������T�� &�����������F�:b������Ve�UT����m�����	i&G/��x�7�.�"�6lSa��9rd��U8�i��RSq�f��5)�����Y�_5���4��(�Z�?g����V�*M
U�8OSa������dy.R� M��G�c�O��U")h�OJ'
7@E0x�����$]�3�BJ�;;��7j"[A�_s�Z!�Q
����D�ieq��!���s�����3�`����e�z]�h�r����d�����M�����1���w�}l(������������>�R��5E�k6>Y3��k&}�f�]5g�\���:S�g�L[zh���U%	TI��<M+4nGSR@��� TFQ�"Q��	��`U�K=4�J*�S�T�@Z��`-����K4(���6i��i���x�BBp���VM�|2Sp�`�c���l��Sw�+���E��/$��gX
�F,aQZ���L+y��6Y����B7E:�;wFC��������u#o���q����80��_W����)�0L���5Y��	P����U�:pfl��K�,�P��S~I�����g���yV��~	8��fI������B	�=��W�B�P@8��*l�,���DP�@j��,t9����3}�k�������~1��>����(3e��������>e���XO�_%��}Q������~^2I������$�O{��!=V���0����k{���������������x6�lzv��d�����lz�u�l�)�{�����+R��EQD�� ����������<���y��;�w����s��i�����{���W0+��a54��D�U~(�����?�DM)+3�U�y����_;o���5��L�����A����D]�t6U�'�dSP��A�*9�i+�����y��v�/U��2�T�	:|I�%~F!�"�q���9�����N������A.��n���Tnn.C�I�4�65u������&�;~QO��Q;33���U��]wXj/���
2�wO�G,J����aG>y��a���Z���Z�h�`j���/�����l
i[}�,��y��P���
����\��~j����v 6O),Y�������&�������ui�#�u�8&bmZ�~��MU{NO��l�r�dP�&c�)�4w2<$���+���i�Y"
a-�"��Si�M��U���%S�������9-^�.�d����J����X6���Go�K��.�WO�����}��A����{�t��)���~����iS4�R��/?~|�=}�Ql�`S�-|M��jc���Z��Z|�r�F���Js�dgj
I�C�7{��!W]�����fiCe#�4R�T�G��S�M��P)�Wr%_�<
�)��I�$3���4A���T�D���a�^�c
�E2�_
wZ�b%N�X!����F�f����|�&��^��"7����=���K}B�����nW,������PY�m���!��ccc[�j�q����)�w��+�/�������Z������*�Gm��Zv���o��*b�*���i
���%d�����u��<~��O��c�M'��8�B���d�)��L�������@�8�8��3~+��5q��&TDj���A�G�HC0�k�}���9��c���8.����bbbb��,� ���MFM}+-H+�6z����9p�O���,�Sv�{����r[��� cg7�A��`
���
Tx���q
��V�%��?w�=�'�/T�����*'��E�2K�D�8�GO�t����_������
�"`#N�T}�4�M6N�����)~5q�!����s)���\+�H�����X�/L���/�E^�}��]2b�ooo�M|����h�Qs����������-��}<+��������,u���:� ��o_l�9��������Y����I�������7����*6��#2�kO����.�7�����N�&�F�&���6U���c�)�b�R�����j��PiA���E?E�`V��t���T$�5����{_����s-���;r��X�w����5��65'+-��=9R�S��������7�pz^Q�=�o����^X6����&{k�i����t�+TN�
��S�0S��Z��
�ZV�Q����H�O7����x���U'�WM4;q������l
*���IN���Uz<����O�@��}F�ZH�6����P)}J�O��X��}����_L�a���9Y�G�U��� =~G���M�������Wlg��lr/���
S
26�.������P%*f����Zv�Zt�r��xO��!;�"3��em�m���
�f�.�]����8aS��&�zj��d*��A�*Ui��1�����H'�+�p��4��;��U�:����L4����gki��~1u��&��d�.��-��XU�Y��6E{���r{�����R�O��^�y�&dl����nm6u
��*c�:�Oy�wb���.���[����<���.���[VL��M���8aS�}�L6e��p*��k��VR����:��U�%M�P�qU�N���U�Z?�<`��Y�~1�5������W��&��d��m��������J�Z�-�JrK��9���&4)&-����M�E�G�	�2M��4�&5�:	J�*Ld����K���7h�gX\B����F�aD��f�8/�/�)h#N�T}0�M=9�Beh�{�#:��s�����]K�8�)iN�y�:
�����O+��a����0�_u��yM?�MF��JQ�E-R�S+oT>���A*#P����(��Hd�wH���c	Q�%vr_lr/��#J�fq��Q#�M�B���"������_}pO��w�^����]�w�5�~�
��
M���'l������`A�'�|O9xb@eP)�Q�\�kO�p$T~����`J�0D'�V�q���n�.j����F���S��`���TG����)����U���*j5P?�rK�%e���[��p�By�=�S�����/)���N7V?�MUv���J�-\u4�����u��-��H�����K����{uA��H�N�[Mc�Q�;q�����3��l�P��������)NaP�oC�7�]3+X��*CNU�����_�0e���W��/�L���u���������J������\nTk_V�#T���oe������[������-zCty�W��/,��|G2���o��7&���8�.��H�
����=�9k��o}<����_=������%�`��h*~>�`�	�����6Ui�G�/�LU���#i���Ai��O��s������X��-�>��x�e�=\���U�(�MFM
���$M�lW�*�'��
j�*l���P\��(�8�Y�}$i���]|c����^S�fW,���
�PA�a����q_s������n��`[�i�����h	���B�������2U�TvlM��YS�����ony��k�>o�������u]�c�G+��`�	����`�)����-�!U��	*��6�"l
��%8<JK���h�
g����b��MF�i+e���*i���Cy=�X���-2J�E����y���R���G������k�����fL�NK�o���d���Z�[P��={�n����w���+�zPPP��-��m��_��j��)Z�J�+��8�~<4�����'2�S�;��vt���pz���>��Xy�Z��
��<2���~���6U���dSz���$��6�"�����q�
>}��!��~����_�jn�~1���v�o+�pz���3T�*�����X����!���H�O3��U�Xb���x~�_|������-\����MQ��(��X��<2��
?��n����o������e��qff&��/^��Efxv=^�p���tV9��R�K�E���&$eT�'�I/aw��o\]6��/�V��i�������?)�������>���������P����1���^����J�R�)-�2��!N���9�,���`_��8p���~1��f���ch�QS�JO���x��A�k����T�Q!�U�~U^hv���Dxf�wH�����������^�������X�N�4�67Ns����j��f������O>��6��Br����#Gj���a�BCC��n�ogUNq����#�~���'	Ki������~8����'����T~o*��/�xu�I���9����_5��Y�8��E����
��T���kA�dS��P����Np*�oH��8cw2���Y2�b����B�[��J���MFM=*�S�E{�:�[�>��nSk^R����
����������qs6�}x��gt:X��HqV|��kmE	2��.5�6N�6������ol*77�lj��9����m��o�;v:t���������l��$������X��ve���w�3�\0d�5c�r�[����~Sk���g��t�r��p}|x�!��
Se�C�.�u�Y�8���{===�w����Y�p�e�dS���3�08���|�����S��G!�z|�[�QW�\�h���b���d���R|E��E�WkX�w�Z����J��Py1'*J2
�v&f���]��q����=9u��=�)Me�LoG�a�c��?� ���a#��w�/��Be�&���+����oqq1z�N�:i~��T�k�,Q`�_tV����+��v&d��2c8������yiN��|��/W�z�ge�� �H�i�V?��]�V��V�]�yV��VaSU�vc�,?U�e}�	���KOOM_}��2���?�Q,�����4����z�RO�-~����o�=�Swm2j��R���S��P!���C��N��/�	�����]��~	����>�wMf��{&l�)3��Kc�~#Hy�������X�i�����������F����������4h���@@��w���K�Q�F��������7o�
�hQ%������2W�%zMM�-�X�\�U�4��y�4`����h���Q��_
Y�*j�
�����\oTN6����������T��bx�c��,��hH���8�B�zS�tn�)����2U����<
9���*��q��K��[�����:����g�V�]���9�JY������*z�����Fs�U�>]��>������BS�9�{c����z��8��F���9�tN��C�M?� ���Q�i!�Q�,���
Q!!!�����n������{���&��4hx�������G�m�KK�-*;��#�����Q��F\7���}��<f����W-�8e�PQ�
�T�6����_�����������2�q����
��"f���*/���8���'l��C�dS��N6��48G�gls����
pc�}��aNNK�m��+5$�|�4��a}{��m2j�Q�	U^��c��M*x�a��~{���A[����qh}��O��wM^�������S3O#`z�UX����i�d���i~���>���,��+)N��<z�5,qK\Z\N!�U'��*})�N�=q�����z���O{x�w'o&������2������s�k�C'(�V����VC-��2��2��:��������M�f����+r�`�
���
;q��,����M6�����fS�H��~|����V�:f�����]�����������R)
�MF�5+E�W������'v�����}����E�;z0j�������5b���vM����S�'��i�	�
��U2
�y]ddS��Pj~�b�G�q���m���9E�LR_*L/d��W;�I�Nt� ���>2u���;NKg93.bpc�4;Q�J�U~�at1G��Mm�Xy?�\���\��\j�,����sj�U��*|�J�U�N�8VOC��c
Y%���<<���{�L6eMi����m�*}���Cl��������_�,��{'�uD�&����B��"�>q�/3��?��OX��=��/ygo�Wf�������-=c�����^S�ul�m�5�c�I�6F�i�)
�)%�h��TaIxQ����V�&��z����������d��]9h�]\�v����3;���2���f�K�R�(Ee�c^*t���`�:�y���_-�_]�\�?9�v�P����U�:�uH&��,)V%�:I�l|C�W/l�:&���l���b�c��X�wjDU����|��%xu���h7f�5��U�������9�y���ez<�mp��G#������co�;����'���c�nNKM��6 ,M��g�A�lP�<�G�)�)�j8[��I�b��KX�wD���������rS�DJ�RNbN�{�o'�O�@	8����>���m<����3�`V�e��b�C�?��	���{����Z�OV��ON���UK.7(��K
?��2���5aT�2�e��j�QU�bxi@�UQT�.��kU�������_6U_LM6�.�)�<�P���*��}?b���+�~1J���PJ����M�S
])Fj��2}B�t��������-?�~W�q��r�~A��gI�.U�h�]L���*�5�SA���f�����4�p�������M����I��������$�����V��Zk���{���_������:���#��r��}�������R���Jq��������w�����K�W�n��U@W�����Zu�r�V-�VX��,�3���|�P��U�~4����*n�J�S���q
��,�k��ZXg�S��cN�-aS���c�dS�o���{�M!�zr����`(�����X���<�9���M�S�P)a8����s�������������\�����
��P���&/k[~h�:�e��X[6T�fL#�b����L��_M����M�Hb��S���6:�����6*��i���[%��2�;��
G�n?�Myx
2+������~����:�������A		����**7���F�a������S�uVa���>jwK�����r�U��F-E�u�Z|�0���j��j�K��;z�}]��!�uV��s���JVe9�3xS����nQ�����'�6e><u�l
���=<J����q����Y�b���$� ��T���0jjLg��������R��,���=I8��.�IM	��>f�������������l����w���������(���:wN�
2��d��@�cS��	�z��)����f��In8��8�_��91�����J�+P�������_�}���O�o��k��l�j�?FNy`��{N��<����,8~W<��z�i������:����S�����o���*�[E�7�Y�������Q8��1��������U��s���Uy?b,<��X�j�{��1��|����=u�QfV��	3Vp�������xT���u����Ia����������Z��KD����>�h��R???�`��<�R���>y�l��#@fQ����Xl��8�WTZZ^�V�5v��>2a���V�����/Xx���W�I����,��P���P}�8���h��kC=� ����y���M�w�Y���<)�($-�0i�2��p��>:u���C)9��RJ���+z�NA'y�9	�	��������O|�����b����t������s�q>]}���
CExl����y������eA���Gfp-]��D�D+� Z���P�kT�RC�4H��W[�T�_7�Z���\�S���\i�h�R�o�%j�����hW?�_A��e��������F��]9�#�T�\����Sw��s�UQ|rB��!�MY����&����O�wi5j��/&**
�m��R�`h��TcV�(�-);���62eCt���|�������5�^�WO�k��:���"����W���j��j���W���T�Xu�������Gc���]�r�������0��3�^�]{��8l��
Kt�p$�"@s0���2+�Un����}#��������Fe�'g���W�(�8���2T|���G�c��p:�e���\��2_��!�A?8����v�0���><u�+���\���g�������D������/1�HZN\v>6,A�/eQ!k���iC�a\=�"vP/_Q�o�bU����Tn���m��2��D����P{Z��_;_�M�<k��C�\�W+��V\��^iH�p����0��}�P�c{h�{���?������O_�qp�S���j�z���ASy{{���?!!7����y;m����M�S�\)�iTTV���4�����3��]F������.9�������|���?��p�^�w���VmxEy��\oQ�����*���O�U9!uq&|OV#�r-�UA�V�7�z�QC����_�J���G��L��G��_t�������k&�n�XNN���-��������x������ /YEe8T?��x<{k|:^����I�����Ua�^��]m�Mg�s`J������������������=�6/��{���v���g�N�:�zr��;'��n����8�+�d��K�l��������L����/�_��2����7�^�}������{�G����%�I����HJxjN|n������������bh;+V�0��]8�*(/��(N�(��������]���"��D���O�=�����'v~ub�{j�+)+�E����q�0�����#��X��c����������q����Q+6yO��R�ub^���V3�`c�[<�x%y%�!�\����[�N�����,��9��D��:�K�V�V��[a����m_����*e���C)�VVx�lK�OGS�(�4�;���1g���]�������O,A��������]�vl���{�)S� ��l�RBb��t��_�n���>U"����7k������WT���g��%�Ge�d��O�b�e���u1i�#R<�&c������UG�=�R|bN�M�Z��1�����������Ss�f��=f���{]�n]�}���3�p����Io-���l�����{��[��4f�
�n=�����0j����]?z��cG�4���In�����4������7���N�/]�����m<}:����������;v���=! `Z���AZ���**�+��oL����M	1[v&�?���x���#����'5E���:hyz�A�	�/�@```DD��/F�T��{�&���J����N��`RVzke�i^iJp���{�d�����������'�H�Q�e��<U�h�������z@���\�P�8`y��[�ZA������o9��V��Uc5� ��p7���m������.�����
:�?��c��������/�����#����#����- m�v�'���lD�x�b*�cG����������#���GB������A����{�p����=���6��c��
�S����~�����7�O��?m�������o��}3v��0s���~{fn���v����'�n���o��M�l��x��E���[?~��q��:��u��3n�O�~s;�9d���C�Fr��y�+��Z6��R�nK�.�i����/�n�C�'�Z�sh3��������mm�`x��#�/��id����,�y��n������a8�l��n8l�q�}����
B}o�M�S6�T#�����"!�hwb&��~�i���,���/e��,8����������q��3�:���pVLZE~�!��	66�^�V;~Pk_P�w;a��G����{���:4TE;���U�^G~eCX���4rzA��o��A�~��g$6�����s���#��I!��+{��M?��"''�o��AV�.]�����[7JC��4���V)��Q~�O�,��������w������{��z��i��}�N:vn��s������_��U�:���S����:uj��s�����tn���vn�������������[��������m-��\�<���m�.m����_W������������;Z�f�0�����|}@��
B}o�M�S6���T,L	7|����4�V1�������
Q[Gl]��"����5n��7�����S��+��Y�c�L�L=|�
�mh�>o�+$Wnw*�����O��N*d����U-8l��JM��a��QM"N�i��94v����Ka�������x5888��5���C�����s����9(���+��7\�z�RA%F�d�?�9Y~�TA������6�6"�B������&����lRi�[�0���)%��9��RY���X���T��u(l0�|(9hI�G���M}�����_����}������c7���/�����Tx\�1���v�l�W?l������_��CQhx�����xW���p�B�����U�)� ����U�V�j�j���~�-[``�>s�L(
;�!��m������X��e4P�A,-��X/;���
��K�E��l��u-Zm�l��S �p�����p5DvY�I"U��.;�p���{�&���!,����x~���l����d�Df��W�*L�J�Kr�rpQ�?������'f��y�����}m�O'��s�'�M�k���*p�[Qjl���
ws�!*e��]�B'~�}����<�V�7W��[y>��<g8
�����R#V6�dl~�����z����Z������pHT��=�,YB�M�6���~C���z��Xl��5���o��q��Q�0`�����^�q�G)��]
�	i3:Y���|��
�LA%�P���Y�H��@l���I�U:ny�9U\n���,�����S��J�M�/2vn�L����e�f�������������w�����e�,��g��e���%��d������s/.|���Uh�R!z�
qT=�����7�]�<�jj�XBd�� c[���v�\�}��AAA�pg��e�"�
���)��z��!c���e���m��A�V�G&�3f������[>�EGG��������
Z]�a>|8bI��x��l��hH�v��M�S6���7�U�^�ME��B���X,%>��{<����V��JrK����'�pc�������4����k��'�-�l������l����,�,:)�:�vg`/��i��W���v�#C�����5BA�@>�@p��W�+V�������4D�!THu0���(�2!!�v��
OY�
=�`�����|�[>j��S��&N�F�V�d����eKn4
_��u�/�	���{�&�V�{�18L(./�,,��,����!�0���J�R=(�_a6{�V���U������P���;��������r���t�[��1f����8�����f���a^a����kcl�P�0��%M�*�m�SA������[���3�\\B�����&�0�]�����-bi�x���=�p���E~�X��`�\���g7}�����
"5��_}�~���u������lRi}ovS�N���'�bU�1&���q��_�-�i���1Y��%��`��N(v�)-(-�(�\Eo��;c�o7���/����Q�5�oC�];����O�t��y��k��
�KH��+4�����h����U`�B4�������,�qzC C���czdu�����X�8{�l���(_�8J��e�SpN�B��/D�,�|������V'����e�Z����Z)��#`���M*=�;[�V���'�������Q���8Nb��M1i,I��\�C�����-(��*?%?#,-fCt��@�V��ct�U���0��acn3����_����K�����<����Y�&YN�/Y///��a��s���<�x�`�38�G}����wg]T�Q����������3���u�q;w�D��_��{��uQ5f�����X��������"�Z�f��	�9���Z��������lR���e�U0�������*4#�bS�Z��[yF_c����B�����0�*�+)+*,+�1�Uvlf���0��=S���������_�?��Ic�9���r����A��I�z!����,(c�o�z��KbD"Hu��#�`,��K����M�3R#�S(�X�W��uI�GK#G��>��;�<��y��T����/{2�;}hC�����9���{�!*�c���
��Y?�wF���f�P�F�+)g�����06�J����v�^�x�:��q�8;aA��b��'��M�:�����������������������#���x������������r���M����������Qc�a���]��Fa7�6� W�������������������;�e�%d��M��f�`�gD��������&&mc|�_|��d��y�[��i+���y���������MF��)V<��d
�u+�iY�uv��D��e|�aR�*.!>6.>*.>26>4:�pdl�����(������~�#��:�j_�����G5,�v-�}h���������>������rp��s�����w�����3�w�X�u����gn�<c����'MY7q�����N�;a���>���0��W��~6���co�������:�����������]W���|���;��o\���8����
~'t]i�uY9�����S~w���mZ�U3�x���1����A�#�����o���z?��2��)��
��+����dl�����k�wZ�m;���_A�Y `��"5�������H�2C�+�Y��=��F�
��%��*�dz�����p���?bX���3���vF�$� ���Z�]Xj�E"A�@��N7���������M��s\�����qXj�E"A�HLL�������g4n������@�  C�F��a�HA@8p~�v���������&M�tFo�2���Wh4d����R#,)���(�YrP�� WgT��\t.�K^A����Z#�K��H�  42252�R� pv�P�7��FX$R��p�N8;d�����R#,)����E��T��@������
1b�����5�c���0��Y�o�^���w�&�}�-�8@����e$6�DZ��a�Db�/_n���a,F��[7d�gg�3&�cZ���S��5&��"����V�������D�_�.��7�d�-��i1U�� `-�u.�>R� `"�j��C���/�������?��1�6t@�j��+,��=]{�������g�%��O?���������+��������k�����{VV������H�0��%��?��?��sU*�K������/-a���%�K��%K(����$~��'����/kX_|q�^��U��� p��'�����G`�����V�Zeff�l��7��������8��Z#��
�/����?���������������D������I���'���Fd���	o������~k���a�4]y����3��W����|M�+W��+W��d�U�!�7�x#��H}�",b�?�����EN�sG�G������G���_����L��
����#���#U�jU�������z���w����s��=�����w�}_}��9z�h@`�{�����^2�u�r�
����;o������*	�)Ou�>}^�<���?h����������a�/��������6m����7�|Q��������������YH<c�(274��L����\E����o�t��555Ug��c��]�~=Y(P'vww�I~��g��P�Q��m�,[E�
�rpp0P8��w�5�H@�#<�uL,����n���g�1��AA�-��
�P�^{�t�u��. ��m L���,��������Ozg����w�}7BT���C�,**�\J&���R]����������g���%Flu�m�!+{����C��5�6�����o�����zJS,J���_o��f��$X
�q��nv�����/E�r�-���_����g$�F.�NB�.�z�jaS��sg
A
JXA���\qX�@)J�Wx��n��k�������eL��e���������	]L�}_�t)�E"���dS���fDEE�UJ3h�&6U�r:u�T2��'-���"�������A���I�*���?�\�:t��������{�����RJJ�%�\�%���E"^�Y��'~����M�n��%~��;����U���l
a2��(�,V��U����JQR� `���_�z����>����������V�P�R{�E�)�	y��G	����Z4j����i_|�E��n�������j���<�f�Ze�[Z�l��������m���9#�Bxe������7dJ������m���9�L���*������}�1u��W��G��M�lf9v�)�t�b����eS��('..�,G��U����JQR� `�|��',���������������*C�*��
���<<<������urr��&�*((�{  B1��c$��I���������.U����A��=���)�$b��>#���J��744�*�uXF�Nv��I<FPfJ������9�ii������t��w�5���@�������|a��>F	X�Ok�&�v�����RtLa9m4�S�@��Z���WXL6��`(��ya������ Wt��XMT�@�x2uvP$@��)��8�����:�J@�����n�����Z4}�l
��8�UZ�����5�)l�i��O>�
� ]�R,M���=zhz�������U�1w�u�cKcuxy���0��P�.�`E	r�E�G���eJi��]"��eG��i�
&��o�5ZO��*C���
^��;��b
E7��;m4e������)��D��c1�gY:���W���N�m6tjT�B���'`�8�~���=��VR�v�b.8�
��M��4q�D��#c�e� �����_~I�1�m���,t��7����_�\�����k�,b�NF|rZ�!m�������I@{p3���TPSU�S� ��a���A�������������������Z#��
������Y���C�H��c���q%>
����
,P�����f�@!X�eQf^l��X0��n�-dS���Q����e�����������aLn�;�R`` z=�|}}Q�Q�UQ����E�<M�HO7���"(�\�LD.���B�8�z�_DO�o	�A��4�Y�d�����E�EIJl��U��i�  �C@���!#��@�B@�j���A@,�)�
	��  �� P_�M�1I/�
�J�����h�fH���  U6U9�,���,vi�-�b��A���T�C���4Nf��*���7��U�V��[�Oi��n�Rsjn�h�FJQ��  h�M�� 4x���2��j!!!�I,wb%�I8%
^z��z(��,�!88��za�6����	��,�'��J�����9}��W����.  ����S=��Y_\P/v�c�<a@c�6�,i���B)JaS&l���&�o��xbT�S-t�@:��p��_ �{��GX���7o�v��/�m�~�W�m�F���}����8���obccG�AXoN�b|���b���W����p�mVXw?y�d@L��P
A�D����<��  �\�0�d��up��qyA���Ql�M�� D��|�=7�k��4x8d�v��q��Xh��7D��M��������L-�C�D�Mo��7n�8w�\�"?E�=��7<�<��6�]�2A���I���%�  4�M�m2PN!��;������q���?���N���W�:
�����C�����j6�{M�l���j����l���9e����
��c$�&�b��.���;v4f��.A@8`��8;.]��M��6h��7������Y��A
���
�2���S'�`Ce�4�@����g����EA�))��-Ny�B��]Ys���~A@A����)�;������G�d�[�����n��q�}�8bbb`���?z�h���0����c3�*���  ��  �C���o�;�����>��mN�����b�Nz��u�r���$0��a(�n��H� E:����%,�8�O��(��4�uL��
������(�K�_A@A@�z��	%��\w9�K/����n
�p'��������������h��S������/�*T{�O����  �� P<M������M�<�S���*�����H@A@A@A@A@A@A@A@A@A@A@A@A@��@DDN}�w�3&�W<��=KVV-a���Y�;�%s�v�f��q�������kFJ@A@+"��5k����u����;�c����X�.���q\�bw��MK�}�,3��=�K��`Y%\ZZ�N�]t)���{�[��0UP�SA@A@8w����zOJ����z��s/����z�t	h�p`` -�8qb��-������U�������_�]����ogN7o�|��/��  ��Y �|����{C3^y��D8r�H���9MMM�����c���y(��O��c3����#uu���0��_�j�)���P���]����W�|���_~�evV7n����f��������cG///��;^�S�a��^�z���l�,�,�2����B��#G�E������$,��  ��� ��Rl������|�Atg�M�����\s�SO=�%�Uf�t~��
�D$�Q����������/�����; ]O?�4�Z%�B�w���S����J9�8edd�y����i�hA�%��������^��;���x�d�)����U9h�>PY"��>}�M7�D��b��"�A@A@�iii0�E�8��P���[o���������e���xAL	������������C�%FY!!!�aS�>�0���o���0���
6�X������y�p�
TMi�A�u����6�@�@�������W�R;v� FA@A@��@�l
��k�fT�i6��O����!*O>�$�)-�B
�f^�?�<�����6u��������Y?��s��L-���7�b�d�)�K����ewb`Sl��n�USWHJ6rE3H�����.���,G��  ��  �;5�)�o�d-�1���	�:Ai�,!n2��;w�Y����TZ���Hm7ef��G5����{��f���[)v��&��I1,64�`�El
kyV#V9L�f��JFDjd���,D��  ��  X������iM_]��H�8��x��a�~�0A� -���Gu��7������T��-)����)�l�xl����.�|,((�vV�k������+���t�Z�F]�T~A@A@����v�m:t�Dn�0J��W��I�:+�,����OH��{�����+���n���K.���{�A����?�`����B���Q��V�<����������m��EcH������������*��_=!����S*b���j�[dG8v����?��z��SA�t��+��  �������>����������~���.����"q������U�|o��dd�vM���K����_����!BXR��[�>}��yQ��;U�V���bY���K-�}||�[o������=.��
��������G�5k��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  M
�'N���Z6)==���C�1��������D��ijj*�U�[RRT=��1G�����b�M��������sl�����.�p�������z����;v�����������#
�@VV�d�Z��322����g�2�9WO)1��������o�a����[_z��������?�����e�Y�����O���4�9�����W^y�Y[��������{]R6�4O=��������^{m�si�b���������u��4i�;��s�u�M�6�z�u��=���p���$FOO�_|�z��-[F|���o����97�tS�.]


�g�A��A������w�^��yyy7�x����;u�dFx������,c��0a�7�|��B����*������{�Z��tU�0��^��c�96���nKJJ�K!���?��j]�����>Z�xq�����.]Z�%���C`��M���n���>>>��=�d&������_�j�H@8`�<��s��9s&Ce���_o��a���_�~����]RZ����/X����u�.���
��� ���APXX�����[7���k<h����\����aTWQQ�Vh��Ud$�a�Y6�L�HNf���u����R��TTT:&SVV�m�63��-[L�@�f���4[_��d4S�/Z�U]�'ty���4�L`���
���)22FQE�J`������,������/_n���aK6O��y���sM5.�����dH5������
B��K�M?~�P��^�������
$|�
7�jSp������]������5&@�����K)�0��1�&j�eT��-b�0�-A��o��Y���Y��G}�y������)�eh33`��z��4����5��y��p�/�O�1c�X�)�2�w�}�W���Kb����'��������xc���_��7��d�)�s8m��:t��/������t�������f�!
�������4.���/�����>�����1.�����%Z�j�}���:t����F�1x��?��V�}��S�L�%_~��?��c�-:v�x���2�P������g�F�8
~���)�,�����%��P$=��:
t��jV���_�����
�����y����o$����Y������)���{l��Qm�����;�hHI����{���\s�>������������?:r��%4o��!?���w���i�~?�0��#G>��# lhL6��F�~�����'#��B�.7�����;��{��g������_?n:(�r`S��
��888��I��337����o��%~���|�w���p�k���l[��Z�li��$R8`����/��v��1E�e�V��x�	��O0&a�*��	�������}��'��'&�����9P�1�172�0�b�AJ>o��f"�P�1����(�O�>�z�" � p~"�8������={L�[�$*&�Bbs�Y���m%����^M)����.�`���f-��%���9s���6��?��1�/a���
����
��yk�L�j�{M_2CCC�����>���5�B���CKQ`J�W<�����o�Bitv��$������g*.Au0�!@�W�X�#+��}���4���ILLd�"@Q��
)�*p�O��L{$}�#��W^yE��#R��5�FX�������#b�-��2����0�!���)����Z�TL(36�$����Hi�Mqk���xDj�J���<V9���N�o-l��pN07K@8������_��o:bf0�����#�xDI|w�/�Y�+�	��K�iVo �f:5��ja8kl�2����M1����.J~��
�����.#p���'�(
D&��j6�����/6�������*�\�!fQ������a��?��r�X-l
)d�,cx-�f���<d�4���:S(�����G`�X�� ��A���A�����kWS���`�av��������{��Hl�t>����+��0��!I��|$�0�tD�)�����#b:r38$B{}P, P0"���0=��@3`�)b�9����L���r��zI���;f^��nl��`	u��?������m�l�H���o����O>!	"�*G�U�l
�7k�� p^!�b0����S���0�3_C������R�A�K��Dk�4�)l�9��g�����|�	��M����fE�������!0���_h�4�M�]�%5��h6�E��p�y`�8�e|-l�e�)�1����M�a�����d���~u61@WE��0���KU���bC�������5H��d�� ��o@H��Q�&0�L�D�9���D��,�Z�W�����*l�*LV8T��	������ �U�C�=���N����4�3���M�0�G:���M�M3">S�H$R2�Ma�f&C��Z��r-�O�fS�`>(#X
��A�)��Z��)���z�Z
�K��]"�2%�t�0�
��aLL��tU@k��ki`����]aSX�������_�j��3�,��Q<�r$,�W��6e��Ax�Yv��UW]�_��Mq�&����������#�z�-3;�f�3
 ��)�����o�*l
2��{�'�	�C������
B����S�g��AK��a���u��?>�:��%@�c��=4�b�3�|H� �X�Ss���x-���4��1��-�x:�����2���}u��M����^��d�6�����1���i��BP�)�G�[���)R��u��%�+��)��L;&�9zf@(��l�3Q`!�)�P|K��P/�N��)f>�L�?	>�����)>'��,��W��y�oS��`�\��#o�&O�I�^�����y�"tB���9�fS�!![FD^�� I�e�XiS+"�0r��0)�����L����51&�#��%��d�dw:��+D=���:@��NJ���K7eq��i��I��c���#���Iiii���n� �����F0�������]����t�*&.�n[�\mH��Z��TZ��LfKb`��X�F�d��0.��S�|�>�FNQ,�U���C���-jM31�7K��>:�Ul30wGEK����!!��H�V���������5�H�H���1W%S�����*��@W���lm���UA��`&am��/&�L0��x'��LFOk|�2�����������WO�������������i
�D��i�l��#=�X�2N9�T�b�9�����KX8O�m�@���D7dJ��3F4�A#����*X+�bC������MD<�n��,�D	ogSOGv����^���a2Xe����.AFH3+P����wK=P���t��{!H/h�4��s��^��L&I.l���x�R�h���������5�F�+�AXo��l���-�1|-b��KaS�%?�E	������9JC��0�i��������/1�j�jYa�Bxx ���@��GL�0a"��O3z@>W�S���] �7/T��>"�4���������a��b�Nu(O�Fif�=�?)�8w��������U���UA�.�����
������{Z5O��.`h3�2?Xj��^������@c����1>~�[���`ff���2�3���'LT�P�������yC��#�=����{7a\���A������{J>��uZ"��{���F7�u�M�y�$A@A�
�D�L�Jd-�h�Y pfe.�� BCWKz��D7�!��;�n:�4;��q����f��U��  ��U`?+�P�a�����Rl/tX��>��x��y��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��  ��y���!��.
endstream
endobj
8 0 obj
106401
endobj
10 0 obj
<< /Length 11 0 R /Type /XObject /Subtype /Image /Width 794 /Height 595 /ColorSpace
/DeviceGray /Interpolate true /BitsPerComponent 8 /Filter /FlateDecode >>
stream
x���� ����Pa��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`��0`�������E
endstream
endobj
11 0 obj
2084
endobj
12 0 obj
<< /Length 13 0 R /N 3 /Alternate /DeviceRGB /Filter /FlateDecode >>
stream
x��wTS����7��" %�z	 �;HQ�I�P��&vDF)VdT�G�"cE��b�	�P��QDE���k	��5�����Y������g�}��P���tX�4�X���\���X��ffG�D���=���H����.�d��,�P&s���"7C$
E�6<~&��S��2����)2�12�	��"���l���+����&��Y��4���P��%����\�%�g�|e�TI���(����L0�_��&�l�2E�����9�r��9h�x�g���Ib���i���f���S�b1+��M��xL����0��o�E%Ym�h�����Y��h����~S�=�z�U�&���A��Y�l��/��$Z����U�m@���O� ������l^���'���ls�k.+�7���o���9�����V;�?�#I3eE����KD����d�����9i���,������UQ��	��h��<�X�.d
���6'~�khu_}�9P�I�o=C#$n?z}�[1
���h���s�2z���\�n�LA"S���dr%�,���l��t�
4�.0,`
�3p� ��H�.Hi@�A>�
A1�v�jp��z�N�6p\W�
p�G@
��K0��i���A����B�ZyCAP8�C���@��&�*���CP=�#t�]���� 4�}���a
�����;G���Dx����J�>����,�_��@��FX�DB�X$!k�"��E�����H�q���a����Y��bVa�bJ0��c�VL�6f3����b���X'�?v	6��-�V`�`[����a�;���p~�\2n5��������
�&�x�*����s�b|!�
����'�	Zk�!� $l$T����4Q��Ot"�y�\b)���A�I&N�I�$R$)���TIj"]&=&�!��:dGrY@^O�$� _%�?P�(&OJEB�N9J�@y@yC�R
�n�X����ZO�D}J}/G�3���������k���{%O���w�_.�'_!J����Q�@�S���V�F���=�IE���b�b�b�b��5�Q%�����O�@���%�!B��y���M�:�e�0G7����������	e%e[�(�����R�0`�3R��������4������6�i^��)��*n*|�"�f����LUo����m�O�0j&jaj�j��.�����w���_4��������z��j���=����U�4�5�n������4��hZ�Z�Z��^0����Tf%��9�����-�>���=�c��Xg�N��]�.[7A�\�SwBOK/X/_�Q��>Q�����G�[��� �`�A�������a�a��c#����*�Z�;�8c�q��>�[&���I�I��MS���T`����k�h&4�5�����YY�F��9�<�|�y��+=�X���_,�,S-�,Y)YXm��������k]c}��j�c��������-�v��};�]���N����"�&�1=�x����tv(��}���������'{'��I���Y�)�
����-r�q��r�.d.�_xp��U���Z���M���v�m���=����+K�G�������^���W�W����b�j��>:>�>�>�v��}/�a��v���������O8�	�
�FV>2	u�����/�_$\�B�Cv�<	5]�s.,4�&�y�Ux~xw-bEDC��H����G��KwF�G�E�GME{E�EK�X,Y��F�Z� �={$vr����K����
��.3\����r�������_�Yq*������L��_�w���������+���]�e�������D��]�cI�II�OA��u�_��������)3����i�����B%a��+]3='�/�4�0C��i��U�@��L(sYf����L�H�$�%�Y�j��gGe��Q������n�����~5f5wug�v����5�k����\��Nw]�������m mH���F��e�n���Q�Q��`h����B�BQ��-�[l�ll��f��j��"^��b����O%����Y}W�����������w�vw�����X�bY^����]��������W��Va[q`i�d��2���J�jG�����������{���������m���>���Pk�Am�a����������g_D�H���G�G����u�;��7�7�6������q�o���C{��P3���8!9������<�y�}��'�����Z�Z�������6i{L{������-?��|�������gK�����9�w~�B������:Wt>�������������^��r�����U��g�9];}�}���������_�~i���m��p�������}��]�/���}�������.�{�^�=�}����^?�z8�h�c���'
O*��?�����f������`���g���C/����O����+F�F�G�G�����z�����������)�������~w��gb���k���?J���9���m�d���wi�������?�����c�����O�O���?w|	��x&mf������
endstream
endobj
13 0 obj
2612
endobj
9 0 obj
[ /ICCBased 12 0 R ]
endobj
3 0 obj
<< /Type /Pages /MediaBox [0 0 842 595] /Count 1 /Kids [ 2 0 R ] >>
endobj
14 0 obj
<< /Type /Catalog /Pages 3 0 R /Version /1.4 >>
endobj
15 0 obj
(��T\015y�g*�-[�)
endobj
16 0 obj
(Mac OS X 10.9.2 Quartz PDFContext)
endobj
17 0 obj
(Mitsumasa KONDO)
endobj
18 0 obj
()
endobj
19 0 obj
(Keynote)
endobj
20 0 obj
(D:20140315084800Z00'00')
endobj
21 0 obj
()
endobj
22 0 obj
[ () ]
endobj
1 0 obj
<< /Title 15 0 R /Author 17 0 R /Subject 18 0 R /Producer 16 0 R /Creator
19 0 R /CreationDate 20 0 R /ModDate 20 0 R /Keywords 21 0 R /AAPL:Keywords
22 0 R >>
endobj
xref
0 23
0000000000 65535 f 
0000112464 00000 n 
0000000185 00000 n 
0000112068 00000 n 
0000000022 00000 n 
0000000167 00000 n 
0000000289 00000 n 
0000000378 00000 n 
0000106977 00000 n 
0000112032 00000 n 
0000106999 00000 n 
0000109275 00000 n 
0000109296 00000 n 
0000112011 00000 n 
0000112151 00000 n 
0000112215 00000 n 
0000112249 00000 n 
0000112301 00000 n 
0000112335 00000 n 
0000112354 00000 n 
0000112380 00000 n 
0000112422 00000 n 
0000112441 00000 n 
trailer
<< /Size 23 /Root 14 0 R /Info 1 0 R /ID [ <2755800f09253e1e6518a310694a293f>
<2755800f09253e1e6518a310694a293f> ] >>
startxref
112639
%%EOF
#45Mitsumasa KONDO
kondo.mitsumasa@gmail.com
In reply to: Mitsumasa KONDO (#44)
Re: gaussian distribution pgbench

Oh, sorry, I forgot to write URL referring picture.

http://en.wikipedia.org/wiki/Normal_distribution
http://en.wikipedia.org/wiki/Exponential_distribution

regards,
--
Mitsumasa KONDO

2014-03-15 17:50 GMT+09:00 Mitsumasa KONDO <kondo.mitsumasa@gmail.com>:

Show quoted text

Hi

2014-03-15 15:53 GMT+09:00 Fabien COELHO <coelho@cri.ensmp.fr>:

Hello Heikki,

A couple of comments:

* There should be an explicit "\setrandom ... uniform" option too, even
though you get that implicitly if you don't specify the distribution

Indeed. I agree. I suggested it, but it got lost.

OK. If we keep to the SQL grammar, your saying is right. I will add it.

* What exactly does the "threshold" mean? The docs informally explain

that "the larger the thresold, the more frequent values close to the middle
of the interval are drawn", but that's pretty vague.

There are explanations and computations as comments in the code. If it is
about the documentation, I'm not sure that a very precise mathematical
definition will help a lot of people, and might rather hinder
understanding, so the doc focuses on an intuitive explanation instead.

Yeah, I think that we had better to only explain necessary infomation for
using this feature. If we add mathematical theory in docs, it will be too
difficult for user. And it's waste.

* Does min and max really make sense for gaussian and exponential

distributions? For gaussian, I would expect mean and standard deviation as
the parameters, not min/max/threshold.

Yes... and no:-) The aim is to draw an integer primary key from a table,
so it must be in a specified range. This is approximated by drawing a
double value with the expected distribution (gaussian or exponential) and
project it carefully onto integers. If it is out of range, there is a loop
and another value is drawn. The minimal threshold constraint (2.0) ensures
that the probability of looping is low.

I think it is difficult to understand from our text... So I create picture
that will help you to understand it.
Please see it.

* How about setting the variable as a float instead of integer? Would

seem more natural to me. At least as an option.

Which variable? The values set by setrandom are mostly used for primary
keys. We really want integers in a range.

I think he said threshold parameter. Threshold parameter is very sensitive
parameter, so we need to set double in threshold. I think that you can
consent it when you see attached picture.

regards,
--
Mitsumasa KONDO
NTT Open Source Software Center

#46Fabien COELHO
coelho@cri.ensmp.fr
In reply to: Mitsumasa KONDO (#44)
Re: gaussian distribution pgbench

Nice drawing!

* How about setting the variable as a float instead of integer? Would

seem more natural to me. At least as an option.

Which variable? The values set by setrandom are mostly used for primary
keys. We really want integers in a range.

I think he said threshold parameter. Threshold parameter is very sensitive
parameter, so we need to set double in threshold. I think that you can
consent it when you see attached picture.

I'm sure that the threshold must be a double, but I thought it was already
the case, because of atof, the static variables which are declared double,
and the threshold function parameters which are declared double as well,
and the putVariable uses a "%lf" format...

Possibly I'm missing something?

--
Fabien.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#47Mitsumasa KONDO
kondo.mitsumasa@gmail.com
In reply to: Fabien COELHO (#46)
Re: gaussian distribution pgbench

2014-03-15 19:04 GMT+09:00 Fabien COELHO <coelho@cri.ensmp.fr>:

Nice drawing!

* How about setting the variable as a float instead of integer? Would

seem more natural to me. At least as an option.

Which variable? The values set by setrandom are mostly used for primary
keys. We really want integers in a range.

I think he said threshold parameter. Threshold parameter is very sensitive
parameter, so we need to set double in threshold. I think that you can
consent it when you see attached picture.

Oh, sorry.. It is to Heikki. Not to you...

I'm sure that the threshold must be a double, but I thought it was already
the case, because of atof, the static variables which are declared double,
and the threshold function parameters which are declared double as well,
and the putVariable uses a "%lf" format...

I think it's collect. When we get double argument in scanf(), we can use
%lf format.

Possibly I'm missing something?

Sorry. I think nothing is missing.

regards,
--
Mitsumasa KONDO

#48KONDO Mitsumasa
kondo.mitsumasa@lab.ntt.co.jp
In reply to: Fabien COELHO (#43)
Re: gaussian distribution pgbench

(2014/03/15 15:53), Fabien COELHO wrote:

Hello Heikki,

A couple of comments:

* There should be an explicit "\setrandom ... uniform" option too, even though
you get that implicitly if you don't specify the distribution

Indeed. I agree. I suggested it, but it got lost.

* What exactly does the "threshold" mean? The docs informally explain that "the
larger the thresold, the more frequent values close to the middle of the
interval are drawn", but that's pretty vague.

There are explanations and computations as comments in the code. If it is about
the documentation, I'm not sure that a very precise mathematical definition will
help a lot of people, and might rather hinder understanding, so the doc focuses
on an intuitive explanation instead.

* Does min and max really make sense for gaussian and exponential
distributions? For gaussian, I would expect mean and standard deviation as the
parameters, not min/max/threshold.

Yes... and no:-) The aim is to draw an integer primary key from a table, so it
must be in a specified range. This is approximated by drawing a double value with
the expected distribution (gaussian or exponential) and project it carefully onto
integers. If it is out of range, there is a loop and another value is drawn. The
minimal threshold constraint (2.0) ensures that the probability of looping is low.

* How about setting the variable as a float instead of integer? Would seem more
natural to me. At least as an option.

Which variable? The values set by setrandom are mostly used for primary keys. We
really want integers in a range.

Oh, I see. He said about documents.

+       Moreover, set gaussian or exponential with threshold interger value,
+       we can get gaussian or exponential random in integer value between
+       <replaceable>min</> and <replaceable>max</> bounds inclusive.
Collectry,
+       Moreover, set gaussian or exponential with threshold double value,
+       we can get gaussian or exponential random in integer value between
+       <replaceable>min</> and <replaceable>max</> bounds inclusive.

And I am going to fix the document more easily understanding for user.

Regards,
--
Mitsumasa KONDO
NTT Open Source Software Center

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#49KONDO Mitsumasa
kondo.mitsumasa@lab.ntt.co.jp
In reply to: KONDO Mitsumasa (#48)
1 attachment(s)
Re: gaussian distribution pgbench

Hi Heikki-san,

(2014/03/17 14:39), KONDO Mitsumasa wrote:

(2014/03/15 15:53), Fabien COELHO wrote:

Hello Heikki,

A couple of comments:

* There should be an explicit "\setrandom ... uniform" option too, even though
you get that implicitly if you don't specify the distribution

Fix. We can use "\setrandom val min max uniform" without error messages.

* What exactly does the "threshold" mean? The docs informally explain that "the
larger the thresold, the more frequent values close to the middle of the
interval are drawn", but that's pretty vague.

There are explanations and computations as comments in the code. If it is about
the documentation, I'm not sure that a very precise mathematical definition will
help a lot of people, and might rather hinder understanding, so the doc focuses
on an intuitive explanation instead.

Add more detail information in the document. Is it OK? Please confirm it.

* Does min and max really make sense for gaussian and exponential
distributions? For gaussian, I would expect mean and standard deviation as the
parameters, not min/max/threshold.

Yes... and no:-) The aim is to draw an integer primary key from a table, so it
must be in a specified range. This is approximated by drawing a double value with
the expected distribution (gaussian or exponential) and project it carefully onto
integers. If it is out of range, there is a loop and another value is drawn. The
minimal threshold constraint (2.0) ensures that the probability of looping is low.

It make sense. Please see the attached picutre in last day.

* How about setting the variable as a float instead of integer? Would seem more
natural to me. At least as an option.

Which variable? The values set by setrandom are mostly used for primary keys. We
really want integers in a range.

Oh, I see. He said about documents.

The document was mistaken.
Threshold parameter must be double and fix the document.

By the way, you seem to want to remove --gaussian=NUM and --exponential=NUM
command options. Can you tell me the objective reason? I think pgbench is the
benchmark test on PostgreSQL and default benchmark is TPC-B-like benchmark.
It is written in documents, and default benchmark wasn't changed by my patch.
So we need not remove command options, and they are one of the variety of
benchmark options. Maybe you have something misunderstanding about my patch...

Regards,
--
Mitsumasa KONDO
NTT Open Source Software Center

Attachments:

gaussian_and_exponential_pgbench_v12.patchtext/x-diff; name=gaussian_and_exponential_pgbench_v12.patchDownload
*** a/contrib/pgbench/pgbench.c
--- b/contrib/pgbench/pgbench.c
***************
*** 98,103 **** static int	pthread_join(pthread_t th, void **thread_return);
--- 98,106 ----
  #define LOG_STEP_SECONDS	5	/* seconds between log messages */
  #define DEFAULT_NXACTS	10		/* default nxacts */
  
+ #define MIN_GAUSSIAN_THRESHOLD		2.0	/* minimum threshold for gauss */
+ #define MIN_EXPONENTIAL_THRESHOLD	2.0	/* minimum threshold for exp */
+ 
  int			nxacts = 0;			/* number of transactions per client */
  int			duration = 0;		/* duration in seconds */
  
***************
*** 169,174 **** bool		is_connect;			/* establish connection for each transaction */
--- 172,185 ----
  bool		is_latencies;		/* report per-command latencies */
  int			main_pid;			/* main process id used in log filename */
  
+ /* gaussian distribution tests: */
+ double		stdev_threshold;   /* standard deviation threshold */
+ bool        use_gaussian = false;
+ 
+ /* exponential distribution tests: */
+ double		exp_threshold;   /* threshold for exponential */
+ bool		use_exponential = false;
+ 
  char	   *pghost = "";
  char	   *pgport = "";
  char	   *login = NULL;
***************
*** 330,335 **** static char *select_only = {
--- 341,428 ----
  	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
  };
  
+ /* --exponential case */
+ static char *exponential_tpc_b = {
+ 	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+ 	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setrandom aid 1 :naccounts exponential :exp_threshold\n"
+ 	"\\setrandom bid 1 :nbranches\n"
+ 	"\\setrandom tid 1 :ntellers\n"
+ 	"\\setrandom delta -5000 5000\n"
+ 	"BEGIN;\n"
+ 	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ 	"UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;\n"
+ 	"UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;\n"
+ 	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+ 	"END;\n"
+ };
+ 
+ /* --exponential with -N case */
+ static char *exponential_simple_update = {
+ 	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+ 	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setrandom aid 1 :naccounts exponential :exp_threshold\n"
+ 	"\\setrandom bid 1 :nbranches\n"
+ 	"\\setrandom tid 1 :ntellers\n"
+ 	"\\setrandom delta -5000 5000\n"
+ 	"BEGIN;\n"
+ 	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ 	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+ 	"END;\n"
+ };
+ 
+ /* --exponential with -S case */
+ static char *exponential_select_only = {
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setrandom aid 1 :naccounts exponential :exp_threshold\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ };
+ 
+ /* --gaussian case */
+ static char *gaussian_tpc_b = {
+ 	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+ 	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setrandom aid 1 :naccounts gaussian :stdev_threshold\n"
+ 	"\\setrandom bid 1 :nbranches\n"
+ 	"\\setrandom tid 1 :ntellers\n"
+ 	"\\setrandom delta -5000 5000\n"
+ 	"BEGIN;\n"
+ 	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ 	"UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;\n"
+ 	"UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;\n"
+ 	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+ 	"END;\n"
+ };
+ 
+ /* --gaussian with -N case */
+ static char *gaussian_simple_update = {
+ 	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+ 	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setrandom aid 1 :naccounts gaussian :stdev_threshold\n"
+ 	"\\setrandom bid 1 :nbranches\n"
+ 	"\\setrandom tid 1 :ntellers\n"
+ 	"\\setrandom delta -5000 5000\n"
+ 	"BEGIN;\n"
+ 	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ 	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+ 	"END;\n"
+ };
+ 
+ /* --gaussian with -S case */
+ static char *gaussian_select_only = {
+ 	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+ 	"\\setrandom aid 1 :naccounts gaussian :stdev_threshold\n"
+ 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+ };
+ 
  /* Function prototypes */
  static void setalarm(int seconds);
  static void *threadRun(void *arg);
***************
*** 373,378 **** usage(void)
--- 466,473 ----
  		   "  -v, --vacuum-all         vacuum all four standard tables before tests\n"
  		   "  --aggregate-interval=NUM aggregate data over NUM seconds\n"
  		   "  --sampling-rate=NUM      fraction of transactions to log (e.g. 0.01 for 1%%)\n"
+ 		   "  --exponential=NUM        exponential distribution with NUM threshold parameter\n"
+ 		   "  --gaussian=NUM           gaussian distribution with NUM threshold parameter\n"
  		   "\nCommon options:\n"
  		   "  -d, --debug              print debugging output\n"
  		   "  -h, --host=HOSTNAME      database server host or socket directory\n"
***************
*** 469,474 **** getrand(TState *thread, int64 min, int64 max)
--- 564,642 ----
  	return min + (int64) ((max - min + 1) * pg_erand48(thread->random_state));
  }
  
+ /* random number generator: exponential distribution from min to max inclusive */
+ static int64
+ getExponentialrand(TState *thread, int64 min, int64 max, double exp_threshold)
+ {
+ 	double		rand;
+ 
+ 	/*
+ 	 * Get user specified random number in this loop. This loop is executed until
+ 	 * the number in the expected range. As the minimum threshold is 2.0, the
+ 	 * probability of a retry is at worst 13.5% as - ln(0.135) ~ 2.0 ;
+ 	 * For a 5.0 threshold, it is about e^{-5} ~ 0.7%.
+ 	 */
+ 	do
+ 	{
+ 		/* as pg_erand48 is in [0, 1), uniform is in (0, 1] */
+ 		double uniform = 1.0 - pg_erand48(thread->random_state);
+ 		/* rand is in [0 LARGE) */
+ 		rand = - log(uniform);
+ 	} while (rand >= exp_threshold);
+ 
+ 	/* rand in [0, exp_threshold), normalized to [0,1) */
+ 	rand /= exp_threshold;
+ 
+ 	/* return int64 random number within between min and max */
+ 	return min + (int64)((max - min + 1) * rand);
+ }
+ 
+ /* random number generator: gaussian distribution from min to max inclusive */
+ static int64
+ getGaussianrand(TState *thread, int64 min, int64 max, double stdev_threshold)
+ {
+ 	double		stdev;
+ 	double		rand;
+ 
+ 	/*
+ 	 * Get user specified random number from this loop, with
+ 	 * -stdev_threshold < stdev <= stdev_threshold
+ 	 *
+ 	 * This loop is executed until the number is in the expected range.
+ 	 *
+ 	 * As the minimum threshold is 2.0, the probability of looping is low:
+ 	 * sqrt(-2 ln(r)) <= 2 => r >= e^{-2} ~ 0.135, then when taking the average
+ 	 * sinus multiplier as 2/pi, we have a 8.6% looping probability in the
+ 	 * worst case. For a 5.0 threshold value, the looping proability
+ 	 * is about e^{-5} * 2 / pi ~ 0.43%.
+ 	 */
+ 	do
+ 	{
+ 		/*
+ 		 * pg_erand48 generates [0,1), but for the basic version of the
+ 		 * Box-Muller transform the two uniformly distributed random numbers
+ 		 * are expected in (0, 1] (see http://en.wikipedia.org/wiki/Box_muller)
+ 		 */
+ 		double rand1 = 1.0 - pg_erand48(thread->random_state);
+ 		double rand2 = 1.0 - pg_erand48(thread->random_state);
+ 
+ 		/* Box-Muller basic form transform */
+ 		double var_sqrt = sqrt(-2.0 * log(rand1));
+ 		stdev = var_sqrt * sin(2.0 * M_PI * rand2);
+ 
+ 		/* we may try with cos, but there may be a bias induced if the previous
+ 		 * value fails the test? To be on the safe side, let us try over.
+ 		 */
+ 	}
+ 	while (stdev < -stdev_threshold || stdev >= stdev_threshold);
+ 
+ 	/* stdev is in [-threshold, threshold), normalization to [0,1) */
+ 	rand = (stdev + stdev_threshold) / (stdev_threshold * 2.0);
+ 
+ 	/* return int64 random number within between min and max */
+ 	return min + (int64)((max - min + 1) * rand);
+ }
+ 
  /* call PQexec() and exit() on failure */
  static void
  executeStatement(PGconn *con, const char *sql)
***************
*** 1312,1317 **** top:
--- 1480,1486 ----
  			char	   *var;
  			int64		min,
  						max;
+ 			double		threshold = 0;
  			char		res[64];
  
  			if (*argv[2] == ':')
***************
*** 1357,1367 **** top:
  			}
  
  			/*
! 			 * getrand() needs to be able to subtract max from min and add one
! 			 * to the result without overflowing.  Since we know max > min, we
! 			 * can detect overflow just by checking for a negative result. But
! 			 * we must check both that the subtraction doesn't overflow, and
! 			 * that adding one to the result doesn't overflow either.
  			 */
  			if (max - min < 0 || (max - min) + 1 < 0)
  			{
--- 1526,1536 ----
  			}
  
  			/*
! 			 * Generate random number functions need to be able to subtract
! 			 * max from min and add one to the result without overflowing.
! 			 * Since we know max > min, we can detect overflow just by checking
! 			 * for a negative result. But we must check both that the subtraction
! 			 * doesn't overflow, and that adding one to the result doesn't overflow either.
  			 */
  			if (max - min < 0 || (max - min) + 1 < 0)
  			{
***************
*** 1370,1379 **** top:
  				return true;
  			}
  
  #ifdef DEBUG
! 			printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getrand(thread, min, max));
  #endif
! 			snprintf(res, sizeof(res), INT64_FORMAT, getrand(thread, min, max));
  
  			if (!putVariable(st, argv[0], argv[1], res))
  			{
--- 1539,1601 ----
  				return true;
  			}
  
+ 			if (argc == 4) /* uniform */
+ 			{
+ #ifdef DEBUG
+ 				printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getrand(thread, min, max));
+ #endif
+ 				snprintf(res, sizeof(res), INT64_FORMAT, getrand(thread, min, max));
+ 			}
+ 			else if ((pg_strcasecmp(argv[4], "gaussian") == 0) ||
+ 				 (pg_strcasecmp(argv[4], "exponential") == 0))
+ 			{
+ 				if (*argv[5] == ':')
+ 				{
+ 					if ((var = getVariable(st, argv[5] + 1)) == NULL)
+ 					{
+ 						fprintf(stderr, "%s: invalid threshold number %s\n", argv[0], argv[5]);
+ 						st->ecnt++;
+ 						return true;
+ 					}
+ 					threshold = strtod(var, NULL);
+ 				}
+ 				else
+ 					threshold = strtod(argv[5], NULL);
+ 
+ 				if (pg_strcasecmp(argv[4], "gaussian") == 0)
+ 				{
+ 					if (threshold < MIN_GAUSSIAN_THRESHOLD)
+ 					{
+ 						fprintf(stderr, "%s: gaussian threshold must be more than %f\n,", argv[5], MIN_GAUSSIAN_THRESHOLD);
+ 						st->ecnt++;
+ 						return true;
+ 					}
  #ifdef DEBUG
! 					printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getGaussianrand(thread, min, max, threshold));
  #endif
! 					snprintf(res, sizeof(res), INT64_FORMAT, getGaussianrand(thread, min, max, threshold));
! 				}
! 				else if (pg_strcasecmp(argv[4], "exponential") == 0)
! 				{
! 					if (threshold < MIN_EXPONENTIAL_THRESHOLD)
! 					{
! 						fprintf(stderr, "%s: exponential threshold must be more than %f\n,", argv[5], MIN_EXPONENTIAL_THRESHOLD);
! 						st->ecnt++;
! 						return true;
! 					}
! #ifdef DEBUG
! 					printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getExponentialrand(thread, min, max, threshold));
! #endif
! 					snprintf(res, sizeof(res), INT64_FORMAT, getExponentialrand(thread, min, max, threshold));
! 				}
! 			}
! 			else /* uniform with extra arguments */
! 			{
! #ifdef DEBUG
! 				printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getrand(thread, min, max));
! #endif
! 				snprintf(res, sizeof(res), INT64_FORMAT, getrand(thread, min, max));
! 			}
  
  			if (!putVariable(st, argv[0], argv[1], res))
  			{
***************
*** 1903,1911 **** process_commands(char *buf)
  				exit(1);
  			}
  
! 			for (j = 4; j < my_commands->argc; j++)
! 				fprintf(stderr, "%s: extra argument \"%s\" ignored\n",
! 						my_commands->argv[0], my_commands->argv[j]);
  		}
  		else if (pg_strcasecmp(my_commands->argv[0], "set") == 0)
  		{
--- 2125,2158 ----
  				exit(1);
  			}
  
! 			if (my_commands->argc == 4 ) /* uniform */
! 			{
! 				/* nothing to do */
! 			}
! 			else if ((pg_strcasecmp(my_commands->argv[4], "gaussian") == 0) ||
! 				 (pg_strcasecmp(my_commands->argv[4], "exponential") == 0))
! 			{
! 				if (my_commands->argc < 6)
! 				{
! 					fprintf(stderr, "%s(%s): missing argument\n", my_commands->argv[0], my_commands->argv[4]);
! 					exit(1);
! 				}
! 
! 				for (j = 6; j < my_commands->argc; j++)
! 					fprintf(stderr, "%s(%s): extra argument \"%s\" ignored\n",
! 							my_commands->argv[0], my_commands->argv[4], my_commands->argv[j]);
! 			}
! 			else /* uniform with extra argument */
! 			{
! 				int arg_pos = 4;
! 
! 				if (pg_strcasecmp(my_commands->argv[4], "uniform") == 0)
! 					arg_pos++;
! 
! 				for (j = arg_pos; j < my_commands->argc; j++)
! 					fprintf(stderr, "%s(uniform): extra argument \"%s\" ignored\n",
! 							my_commands->argv[0], my_commands->argv[j]);
! 			}
  		}
  		else if (pg_strcasecmp(my_commands->argv[0], "set") == 0)
  		{
***************
*** 2180,2195 **** printResults(int ttype, int normal_xacts, int nclients,
  						(INSTR_TIME_GET_DOUBLE(conn_total_time) / nthreads));
  
  	if (ttype == 0)
! 		s = "TPC-B (sort of)";
  	else if (ttype == 2)
! 		s = "Update only pgbench_accounts";
  	else if (ttype == 1)
! 		s = "SELECT only";
  	else
  		s = "Custom query";
  
  	printf("transaction type: %s\n", s);
  	printf("scaling factor: %d\n", scale);
  	printf("query mode: %s\n", QUERYMODE[querymode]);
  	printf("number of clients: %d\n", nclients);
  	printf("number of threads: %d\n", nthreads);
--- 2427,2483 ----
  						(INSTR_TIME_GET_DOUBLE(conn_total_time) / nthreads));
  
  	if (ttype == 0)
! 	{
! 		if (use_gaussian)
! 			s = "Gaussian distribution TPC-B (sort of)";
! 		else if (use_exponential)
! 			s = "Exponential distribution TPC-B (sort of)";
! 		else
! 			s = "TPC-B (sort of)";
! 	}
  	else if (ttype == 2)
! 	{
! 		if (use_gaussian)
! 			s = "Gaussian distribution update only pgbench_accounts";
! 		else if (use_exponential)
! 			s = "Exponential distribution update only pgbench_accounts";
! 		else
! 			s = "Update only pgbench_accounts";
! 	}
  	else if (ttype == 1)
! 	{
! 		if (use_gaussian)
! 			s = "Gaussian distribution SELECT only";
! 		else if (use_exponential)
! 			s = "Exponential distribution SELECT only";
! 		else
! 			s = "SELECT only";
! 	}
  	else
  		s = "Custom query";
  
  	printf("transaction type: %s\n", s);
  	printf("scaling factor: %d\n", scale);
+ 
+ 	/* output in gaussian distribution benchmark */
+ 	if (use_gaussian)
+ 	{
+ 		printf("standard deviation threshold: %.5f\n", stdev_threshold);
+ 		printf("access probability of top 20%%, 10%% and 5%% records: %.5f %.5f %.5f\n",
+ 			(double) ((erf (stdev_threshold * 0.2 / sqrt(2.0))) / (erf (stdev_threshold / sqrt(2.0)))),
+ 			(double) ((erf (stdev_threshold * 0.1 / sqrt(2.0))) / (erf (stdev_threshold / sqrt(2.0)))),
+ 			(double) ((erf (stdev_threshold * 0.05 / sqrt(2.0))) / (erf (stdev_threshold / sqrt(2.0)))));
+ 	}
+ 	/* output in exponential distribution benchmark */
+ 	else if (use_exponential)
+ 	{
+ 		printf("exponential threshold: %.5f\n", exp_threshold);
+ 		printf("access probability of top 20%%, 10%% and 5%% records: %.5f %.5f %.5f\n",
+ 			(double) (-exp(-exp_threshold * 0.2) + 1),
+ 			(double) (-exp(-exp_threshold * 0.1) + 1),
+ 			(double) (-exp(-exp_threshold * 0.05) + 1));
+ 	}
+ 
  	printf("query mode: %s\n", QUERYMODE[querymode]);
  	printf("number of clients: %d\n", nclients);
  	printf("number of threads: %d\n", nthreads);
***************
*** 2319,2324 **** main(int argc, char **argv)
--- 2607,2614 ----
  		{"unlogged-tables", no_argument, &unlogged_tables, 1},
  		{"sampling-rate", required_argument, NULL, 4},
  		{"aggregate-interval", required_argument, NULL, 5},
+ 		{"gaussian", required_argument, NULL, 6},
+ 		{"exponential", required_argument, NULL, 7},
  		{"rate", required_argument, NULL, 'R'},
  		{NULL, 0, NULL, 0}
  	};
***************
*** 2598,2603 **** main(int argc, char **argv)
--- 2888,2913 ----
  				}
  #endif
  				break;
+ 			case 6:
+ 				use_gaussian = true;
+ 				stdev_threshold = atof(optarg);
+ 				if(stdev_threshold < MIN_GAUSSIAN_THRESHOLD)
+ 				{
+ 					fprintf(stderr, "--gaussian=NUM must be more than %f: %f\n",
+ 							MIN_GAUSSIAN_THRESHOLD, stdev_threshold);
+ 					exit(1);
+ 				}
+ 				break;
+ 			case 7:
+ 				use_exponential = true;
+ 				exp_threshold = atof(optarg);
+ 				if(exp_threshold < MIN_EXPONENTIAL_THRESHOLD)
+ 				{
+ 					fprintf(stderr, "--exponential=NUM must be more than %f: %f\n",
+ 							MIN_EXPONENTIAL_THRESHOLD, exp_threshold);
+ 					exit(1);
+ 				}
+ 				break;
  			default:
  				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
  				exit(1);
***************
*** 2795,2800 **** main(int argc, char **argv)
--- 3105,3132 ----
  		}
  	}
  
+ 	/* set :stdev_threshold variable */
+ 	if(getVariable(&state[0], "stdev_threshold") == NULL)
+ 	{
+ 		snprintf(val, sizeof(val), "%lf", stdev_threshold);
+ 		for (i = 0; i < nclients; i++)
+ 		{
+ 			if (!putVariable(&state[i], "startup", "stdev_threshold", val))
+ 				exit(1);
+ 		}
+ 	}
+ 
+ 	/* set :exp_threshold variable */
+ 	if(getVariable(&state[0], "exp_threshold") == NULL)
+ 	{
+ 		snprintf(val, sizeof(val), "%lf", exp_threshold);
+ 		for (i = 0; i < nclients; i++)
+ 		{
+ 			if (!putVariable(&state[i], "startup", "exp_threshold", val))
+ 				exit(1);
+ 		}
+ 	}
+ 
  	if (!is_no_vacuum)
  	{
  		fprintf(stderr, "starting vacuum...");
***************
*** 2820,2836 **** main(int argc, char **argv)
  	switch (ttype)
  	{
  		case 0:
! 			sql_files[0] = process_builtin(tpc_b);
  			num_files = 1;
  			break;
  
  		case 1:
! 			sql_files[0] = process_builtin(select_only);
  			num_files = 1;
  			break;
  
  		case 2:
! 			sql_files[0] = process_builtin(simple_update);
  			num_files = 1;
  			break;
  
--- 3152,3183 ----
  	switch (ttype)
  	{
  		case 0:
! 			if (use_gaussian)
! 				sql_files[0] = process_builtin(gaussian_tpc_b);
! 			else if (use_exponential)
! 				sql_files[0] = process_builtin(exponential_tpc_b);
! 			else
! 				sql_files[0] = process_builtin(tpc_b);
  			num_files = 1;
  			break;
  
  		case 1:
! 			if (use_gaussian)
! 				sql_files[0] = process_builtin(gaussian_select_only);
! 			else if (use_exponential)
! 				sql_files[0] = process_builtin(exponential_select_only);
! 			else
! 				sql_files[0] = process_builtin(select_only);
  			num_files = 1;
  			break;
  
  		case 2:
! 			if (use_gaussian)
! 				sql_files[0] = process_builtin(gaussian_simple_update);
! 			else if (use_exponential)
! 				sql_files[0] = process_builtin(exponential_simple_update);
! 			else
! 				sql_files[0] = process_builtin(simple_update);
  			num_files = 1;
  			break;
  
*** a/doc/src/sgml/pgbench.sgml
--- b/doc/src/sgml/pgbench.sgml
***************
*** 307,312 **** pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
--- 307,329 ----
       </varlistentry>
  
       <varlistentry>
+       <term><option>--exponential</option><replaceable>threshold</></term>
+       <listitem>
+        <para>
+          Run exponential distribution pgbench test using threshold parameter.
+          This threshold controls the distribution of access frequency on the
+          <structname>pgbench_accounts</> table.
+          The larger the threshold, the records closed to the beginning
+          of pgbench_accounts table become more access frequency. The smaller
+          the threshold, the smoother the access pattern distribution.
+          The threshold must be more than 2 for client performance. 
+          When set, this option applies to all test variants
+          (<option>-N</> for skipping updates, or <option>-S</> for selects).
+        </para>
+       </listitem>
+      </varlistentry>
+ 
+      <varlistentry>
        <term><option>-f</option> <replaceable>filename</></term>
        <term><option>--file=</option><replaceable>filename</></term>
        <listitem>
***************
*** 320,325 **** pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
--- 337,362 ----
       </varlistentry>
  
       <varlistentry>
+       <term><option>--gaussian</option><replaceable>threshold</></term>
+       <listitem>
+        <para>
+          Run gaussian distribution pgbench test using threshold parameter.
+          This threshold controls the distribution of access frequency on the
+          <structname>pgbench_accounts</> table.
+          The larger the threshold, the records in the center of values in the table
+          become more access frequency and pointed, and the less frequent accessed
+          values close to the beginning and end of pgbench_accounts table become
+          more less access frequency. Which means that distribution of frequent
+          access range becomes more narrower. The smaller the threshold, the smoother
+          the access pattern distribution. The deviation threshold 
+          must be more than 2, for client performance. When set, this option
+          applies to all test variants (<option>-N</> for skipping updates,
+          or <option>-S</> for selects).
+        </para>
+       </listitem>
+      </varlistentry>
+ 
+      <varlistentry>
        <term><option>-j</option> <replaceable>threads</></term>
        <term><option>--jobs=</option><replaceable>threads</></term>
        <listitem>
***************
*** 748,755 **** pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
  
     <varlistentry>
      <term>
!      <literal>\setrandom <replaceable>varname</> <replaceable>min</> <replaceable>max</></literal>
!     </term>
  
      <listitem>
       <para>
--- 785,792 ----
  
     <varlistentry>
      <term>
!      <literal>\setrandom <replaceable>varname</> <replaceable>min</> <replaceable>max</> [ uniform | [ { gaussian | exponential } <replaceable>threshold</> ] ]</literal>
!      </term>
  
      <listitem>
       <para>
***************
*** 757,769 **** pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
        between the limits <replaceable>min</> and <replaceable>max</> inclusive.
        Each limit can be either an integer constant or a
        <literal>:</><replaceable>variablename</> reference to a variable
!       having an integer value.
       </para>
  
       <para>
        Example:
  <programlisting>
! \setrandom aid 1 :naccounts
  </programlisting></para>
      </listitem>
     </varlistentry>
--- 794,834 ----
        between the limits <replaceable>min</> and <replaceable>max</> inclusive.
        Each limit can be either an integer constant or a
        <literal>:</><replaceable>variablename</> reference to a variable
!       having an integer value. Default random distribution is uniform.
!      </para>
! 
!      <para>
!       Sets gaussian or exponential with threshold double value,
!       you can get gaussian or exponential random in integer value between
!       <replaceable>min</> and <replaceable>max</> bounds inclusive.
!       The threshold controls the distribution pattern. Without these options,
!       you can get uniform random in interger value between <replaceable>min</>
!       and <replaceable>max</> bounds inclusive.
!      </para>
! 
!      <para>
!       In gaussian option, the larger the threshold, the more frequent accessed
!       values close to the middle of the interval become more access frequency
!       and are pointed, and the less frequent accessed values close to the
!       <replaceable>min</> and <replaceable>max</> become more less access
!       frequency. Which means that distribution of frequent access range becomes
!       more narrower. The smaller the threshold, the smoother the access pattern
!       distribution. The minimum threshold is 2.0, for client performance.
!      </para>
! 
!      <para>
!       In exponential option, the threshold controls the distribution
!       pattern: the larger the threshold, the values close to
!       <replaceable>min</> become more access frequency, and the frequent
!       accessed values close to <replacebble>max</> become less access frequency.
!       The smaller the threshold, the smoother the access pattern distribution. 
!       The minimum threshold is 2.0, for client performance.
       </para>
  
       <para>
        Example:
  <programlisting>
! \setrandom aid 1 :naccounts gaussian 5
  </programlisting></para>
      </listitem>
     </varlistentry>
#50Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: Fabien COELHO (#43)
Re: gaussian distribution pgbench

On 03/15/2014 08:53 AM, Fabien COELHO wrote:

* Does min and max really make sense for gaussian and exponential
distributions? For gaussian, I would expect mean and standard deviation as
the parameters, not min/max/threshold.

Yes... and no:-) The aim is to draw an integer primary key from a table,
so it must be in a specified range.

Well, I don't agree with that aim. It's useful for choosing a primary
key, as in the pgbench TPC-B workload, but a gaussian distributed random
number could be used for many other things too. For example:

\setrandom foo ... gaussian

select * from cheese where weight > :foo

And :foo should be a float, not an integer. That's what I was trying to
say earlier, when I said that the variable should be a float. If you
need an integer, just cast or round it in the query.

I realize that the current \setrandom sets the variable to an integer,
so gaussian/exponential would be different. But so what? An option to
generate uniformly distributed floats would be handy too, though.

This is approximated by drawing a
double value with the expected distribution (gaussian or exponential) and
project it carefully onto integers. If it is out of range, there is a loop
and another value is drawn. The minimal threshold constraint (2.0) ensures
that the probability of looping is low.

Well, that's one way to do constraint it to the given range, but there
are many other ways to do it. Like, clamp it to the min/max if it's out
of range. I don't think we need to choose any particular method, you can
handle that in the test script.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#51Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: KONDO Mitsumasa (#49)
Re: gaussian distribution pgbench

On 03/17/2014 10:40 AM, KONDO Mitsumasa wrote:

By the way, you seem to want to remove --gaussian=NUM and --exponential=NUM
command options. Can you tell me the objective reason? I think pgbench is the
benchmark test on PostgreSQL and default benchmark is TPC-B-like benchmark.
It is written in documents, and default benchmark wasn't changed by my patch.
So we need not remove command options, and they are one of the variety of
benchmark options. Maybe you have something misunderstanding about my patch...

There is an infinite number of variants of the TPC-B test that we could
include in pgbench. If we start adding every one of them, we're quickly
going to have hundreds of options to choose the workload. I'd like to
keep pgbench simple. These two new test variants, gaussian and
exponential, are not that special that they'd deserve to be included in
the program itself.

pgbench already has a mechanism for running custom scripts, in which you
can specify whatever workload you want. Let's use that. If it's missing
something you need to specify the workload you want, let's enhance the
script language.

The features we're missing, which makes it difficult to write the
gaussian and exponential variants as custom scripts, is the capability
to create random numbers with a non-uniform distribution. That's the
feature we should include in pgbench.

(Actually, you could do the Box-Muller transformation as part of the
query, to convert the uniform random variable to a gaussian one. Then
you wouldn't need any changes to pgbench. But I agree that "\setrandom
... gaussian" would be quite handy)

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#52KONDO Mitsumasa
kondo.mitsumasa@lab.ntt.co.jp
In reply to: Heikki Linnakangas (#50)
Re: gaussian distribution pgbench

(2014/03/17 17:46), Heikki Linnakangas wrote:

On 03/15/2014 08:53 AM, Fabien COELHO wrote:

* Does min and max really make sense for gaussian and exponential
distributions? For gaussian, I would expect mean and standard deviation as
the parameters, not min/max/threshold.

Yes... and no:-) The aim is to draw an integer primary key from a table,
so it must be in a specified range.

Well, I don't agree with that aim. It's useful for choosing a primary key, as in
the pgbench TPC-B workload, but a gaussian distributed random number could be
used for many other things too. For example:

\setrandom foo ... gaussian

select * from cheese where weight > :foo

And :foo should be a float, not an integer. That's what I was trying to say
earlier, when I said that the variable should be a float. If you need an integer,
just cast or round it in the query.

I realize that the current \setrandom sets the variable to an integer, so
gaussian/exponential would be different. But so what? An option to generate
uniformly distributed floats would be handy too, though.

Well, it seems new feature. If you want to realise it as double, add
'\setrandomd' as a double random generator in pgbebch. I will agree with that.

This is approximated by drawing a
double value with the expected distribution (gaussian or exponential) and
project it carefully onto integers. If it is out of range, there is a loop
and another value is drawn. The minimal threshold constraint (2.0) ensures
that the probability of looping is low.

Well, that's one way to do constraint it to the given range, but there are many
other ways to do it. Like, clamp it to the min/max if it's out of range.

It's too heavy method.. Client calculation must be light.

I don't
think we need to choose any particular method, you can handle that in the test
script.

I think our implementation is the best way to realize it.
It is fast and robustness for the probability of looping is low.

If you have better idea, please teach us.

Regards,
--
Mitsumasa KONDO
NTT Open Source Software Center

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#53KONDO Mitsumasa
kondo.mitsumasa@lab.ntt.co.jp
In reply to: Heikki Linnakangas (#51)
Re: gaussian distribution pgbench

(2014/03/17 18:02), Heikki Linnakangas wrote:

On 03/17/2014 10:40 AM, KONDO Mitsumasa wrote:

By the way, you seem to want to remove --gaussian=NUM and --exponential=NUM
command options. Can you tell me the objective reason? I think pgbench is the
benchmark test on PostgreSQL and default benchmark is TPC-B-like benchmark.
It is written in documents, and default benchmark wasn't changed by my patch.
So we need not remove command options, and they are one of the variety of
benchmark options. Maybe you have something misunderstanding about my patch...

There is an infinite number of variants of the TPC-B test that we could include
in pgbench. If we start adding every one of them, we're quickly going to have
hundreds of options to choose the workload. I'd like to keep pgbench simple.
These two new test variants, gaussian and exponential, are not that special that
they'd deserve to be included in the program itself.

Well, I add only two options, and they are major distribution that are seen in
real database system than uniform distiribution. I'm afraid, I think you are too
worried and it will not be added hundreds of options. And pgbench is still simple.

pgbench already has a mechanism for running custom scripts, in which you can
specify whatever workload you want. Let's use that. If it's missing something you
need to specify the workload you want, let's enhance the script language.

I have not seen user who is using pgbench custom script very much. And gaussian
and exponential distribution are much better to measure the real system
perfomance, so I'd like to use it command option. In now pgbench, we can only
measure about database size, but it isn't realistic situation. We want to forcast
the required system from calculating the size of hot spot or distirbution of
access pettern.

I'd realy like to include it on my heart:) Please...

Regards,
--
Mitsumasa KONDO
NTT Open Source Software Center

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#54Fujii Masao
masao.fujii@gmail.com
In reply to: KONDO Mitsumasa (#53)
Re: gaussian distribution pgbench

On Mon, Mar 17, 2014 at 7:07 PM, KONDO Mitsumasa
<kondo.mitsumasa@lab.ntt.co.jp> wrote:

(2014/03/17 18:02), Heikki Linnakangas wrote:

On 03/17/2014 10:40 AM, KONDO Mitsumasa wrote:

By the way, you seem to want to remove --gaussian=NUM and
--exponential=NUM
command options. Can you tell me the objective reason? I think pgbench is
the
benchmark test on PostgreSQL and default benchmark is TPC-B-like
benchmark.
It is written in documents, and default benchmark wasn't changed by my
patch.
So we need not remove command options, and they are one of the variety of
benchmark options. Maybe you have something misunderstanding about my
patch...

There is an infinite number of variants of the TPC-B test that we could
include
in pgbench. If we start adding every one of them, we're quickly going to
have
hundreds of options to choose the workload. I'd like to keep pgbench
simple.
These two new test variants, gaussian and exponential, are not that
special that
they'd deserve to be included in the program itself.

Well, I add only two options, and they are major distribution that are seen
in real database system than uniform distiribution. I'm afraid, I think you
are too worried and it will not be added hundreds of options. And pgbench is
still simple.

pgbench already has a mechanism for running custom scripts, in which you
can
specify whatever workload you want. Let's use that. If it's missing
something you
need to specify the workload you want, let's enhance the script language.

I have not seen user who is using pgbench custom script very much. And
gaussian and exponential distribution are much better to measure the real
system perfomance, so I'd like to use it command option. In now pgbench, we
can only measure about database size, but it isn't realistic situation. We
want to forcast the required system from calculating the size of hot spot or
distirbution of access pettern.

I'd realy like to include it on my heart:) Please...

I have no strong opinion about the command-line option for gaussian,
but I think that we should focus on \setrandom gaussian first. Even
after that's committed, we can implement that commnand-line option
later if many people think that's necessary.

Regards,

--
Fujii Masao

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#55Tom Lane
tgl@sss.pgh.pa.us
In reply to: KONDO Mitsumasa (#53)
Re: gaussian distribution pgbench

KONDO Mitsumasa <kondo.mitsumasa@lab.ntt.co.jp> writes:

(2014/03/17 18:02), Heikki Linnakangas wrote:

On 03/17/2014 10:40 AM, KONDO Mitsumasa wrote:
There is an infinite number of variants of the TPC-B test that we could include
in pgbench. If we start adding every one of them, we're quickly going to have
hundreds of options to choose the workload. I'd like to keep pgbench simple.
These two new test variants, gaussian and exponential, are not that special that
they'd deserve to be included in the program itself.

Well, I add only two options, and they are major distribution that are seen in
real database system than uniform distiribution. I'm afraid, I think you are too
worried and it will not be added hundreds of options. And pgbench is still simple.

FWIW, I concur with Heikki on this. Adding new versions of \setrandom is
useful functionality. Embedding them in the "standard" test is not,
because that just makes it (even) less standard. And pgbench has too darn
many switches already.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#56Robert Haas
robertmhaas@gmail.com
In reply to: Mitsumasa KONDO (#44)
Re: gaussian distribution pgbench

On Sat, Mar 15, 2014 at 4:50 AM, Mitsumasa KONDO
<kondo.mitsumasa@gmail.com> wrote:

There are explanations and computations as comments in the code. If it is
about the documentation, I'm not sure that a very precise mathematical
definition will help a lot of people, and might rather hinder understanding,
so the doc focuses on an intuitive explanation instead.

Yeah, I think that we had better to only explain necessary infomation for
using this feature. If we add mathematical theory in docs, it will be too
difficult for user. And it's waste.

Well, if you *don't* include at least *some* mathematical description
of what the feature does in the documentation, then users who need to
understand it will have to read the source code to figure it out,
which is going to be even more difficult.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#57KONDO Mitsumasa
kondo.mitsumasa@lab.ntt.co.jp
In reply to: Tom Lane (#55)
Re: gaussian distribution pgbench

(2014/03/17 22:37), Tom Lane wrote:

KONDO Mitsumasa <kondo.mitsumasa@lab.ntt.co.jp> writes:

(2014/03/17 18:02), Heikki Linnakangas wrote:

On 03/17/2014 10:40 AM, KONDO Mitsumasa wrote:
There is an infinite number of variants of the TPC-B test that we could include
in pgbench. If we start adding every one of them, we're quickly going to have
hundreds of options to choose the workload. I'd like to keep pgbench simple.
These two new test variants, gaussian and exponential, are not that special that
they'd deserve to be included in the program itself.

Well, I add only two options, and they are major distribution that are seen in
real database system than uniform distiribution. I'm afraid, I think you are too
worried and it will not be added hundreds of options. And pgbench is still simple.

FWIW, I concur with Heikki on this. Adding new versions of \setrandom is
useful functionality. Embedding them in the "standard" test is not,
because that just makes it (even) less standard. And pgbench has too darn
many switches already.

Hmm, I cooled down and see the pgbench option. I can understand his arguments,
there are many sitches already and it will become more largear options unless we
stop adding new option. However, I think that the man who added the option in
the past thought the option will be useful for PostgreSQL performance
improvement. But now, they are disturb the new option such like my feature which
can create more real system benchmark distribution. I think it is very
unfortunate and also tending to stop progress of improvement of PostgreSQL
performance, not only pgbench. And if we remove command line option, I think new
feature will tend to reject. It is not also good.

By the way, if we remove command line option, it is difficult to understand
distirbution of gaussian, because threshold parameter is very sensitive and it is
also very useful feature. It is difficult and taking labor that analyzing and
visualization pgbench_history using SQL.

What do you think about this problem? This is not disscussed yet.

[mitsu-ko@pg-rex31 pgbench]$ ./pgbench --gaussian=2
~
access probability of top 20%, 10% and 5% records: 0.32566 0.16608 0.08345
~
[mitsu-ko@pg-rex31 pgbench]$ ./pgbench --gaussian=4
~
access probability of top 20%, 10% and 5% records: 0.57633 0.31086 0.15853
~
[mitsu-ko@pg-rex31 pgbench]$ ./pgbench --gaussian=10
~
access probability of top 20%, 10% and 5% records: 0.95450 0.68269 0.38292
~

Regards,
--
Mitsumasa KONDO
NTT Open Source Software Center

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#58KONDO Mitsumasa
kondo.mitsumasa@lab.ntt.co.jp
In reply to: Robert Haas (#56)
Re: gaussian distribution pgbench

(2014/03/17 23:29), Robert Haas wrote:

On Sat, Mar 15, 2014 at 4:50 AM, Mitsumasa KONDO
<kondo.mitsumasa@gmail.com> wrote:

There are explanations and computations as comments in the code. If it is
about the documentation, I'm not sure that a very precise mathematical
definition will help a lot of people, and might rather hinder understanding,
so the doc focuses on an intuitive explanation instead.

Yeah, I think that we had better to only explain necessary infomation for
using this feature. If we add mathematical theory in docs, it will be too
difficult for user. And it's waste.

Well, if you *don't* include at least *some* mathematical description
of what the feature does in the documentation, then users who need to
understand it will have to read the source code to figure it out,
which is going to be even more difficult.

I had fixed this problem. Please see the v12 patch. I think it doesn't includ
mathematical
description, but user will be able to understand intuitive from the explanation
of document.
Regards,
--
Mitsumasa KONDO
NTT Open Source Software Center

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#59KONDO Mitsumasa
kondo.mitsumasa@lab.ntt.co.jp
In reply to: KONDO Mitsumasa (#57)
Re: gaussian distribution pgbench

And I find new useful point of this feature. Under following results are
'--gaussian=20' case and '--gaussian=2' case, and postgresql setting is same.

[mitsu-ko@pg-rex31 pgbench]$ ./pgbench -c8 -j4 --gaussian=20 -T30 -P 5
starting vacuum...end.
progress: 5.0 s, 4285.8 tps, lat 1.860 ms stddev 0.425
progress: 10.0 s, 4249.2 tps, lat 1.879 ms stddev 0.372
progress: 15.0 s, 4230.3 tps, lat 1.888 ms stddev 0.430
progress: 20.0 s, 4247.3 tps, lat 1.880 ms stddev 0.400
LOG: checkpoints are occurring too frequently (12 seconds apart)
HINT: Consider increasing the configuration parameter "checkpoint_segments".
progress: 25.0 s, 4269.0 tps, lat 1.870 ms stddev 0.427
progress: 30.0 s, 4318.1 tps, lat 1.849 ms stddev 0.415
transaction type: Gaussian distribution TPC-B (sort of)
scaling factor: 10
standard deviation threshold: 20.00000
access probability of top 20%, 10% and 5% records: 0.99994 0.95450 0.68269
query mode: simple
number of clients: 8
number of threads: 4
duration: 30 s
number of transactions actually processed: 128008
latency average: 1.871 ms
latency stddev: 0.412 ms
tps = 4266.266374 (including connections establishing)
tps = 4267.312022 (excluding connections establishing)

[mitsu-ko@pg-rex31 pgbench]$ ./pgbench -c8 -j4 --gaussian=2 -T30 -P 5
starting vacuum...end.
LOG: checkpoints are occurring too frequently (13 seconds apart)
HINT: Consider increasing the configuration parameter "checkpoint_segments".
LOG: checkpoints are occurring too frequently (1 second apart)
HINT: Consider increasing the configuration parameter "checkpoint_segments".
LOG: checkpoints are occurring too frequently (2 seconds apart)
HINT: Consider increasing the configuration parameter "checkpoint_segments".
progress: 5.0 s, 3927.9 tps, lat 2.030 ms stddev 0.691
LOG: checkpoints are occurring too frequently (2 seconds apart)
HINT: Consider increasing the configuration parameter "checkpoint_segments".
LOG: checkpoints are occurring too frequently (1 second apart)
HINT: Consider increasing the configuration parameter "checkpoint_segments".
LOG: checkpoints are occurring too frequently (2 seconds apart)
HINT: Consider increasing the configuration parameter "checkpoint_segments".
progress: 10.0 s, 4045.8 tps, lat 1.974 ms stddev 0.835
LOG: checkpoints are occurring too frequently (2 seconds apart)
HINT: Consider increasing the configuration parameter "checkpoint_segments".
LOG: checkpoints are occurring too frequently (1 second apart)
HINT: Consider increasing the configuration parameter "checkpoint_segments".
LOG: checkpoints are occurring too frequently (2 seconds apart)
HINT: Consider increasing the configuration parameter "checkpoint_segments".
progress: 15.0 s, 4042.5 tps, lat 1.976 ms stddev 0.613
LOG: checkpoints are occurring too frequently (2 seconds apart)
HINT: Consider increasing the configuration parameter "checkpoint_segments".
LOG: checkpoints are occurring too frequently (2 seconds apart)
HINT: Consider increasing the configuration parameter "checkpoint_segments".
LOG: checkpoints are occurring too frequently (2 seconds apart)
HINT: Consider increasing the configuration parameter "checkpoint_segments".
progress: 20.0 s, 4103.9 tps, lat 1.946 ms stddev 0.540
LOG: checkpoints are occurring too frequently (1 second apart)
HINT: Consider increasing the configuration parameter "checkpoint_segments".
LOG: checkpoints are occurring too frequently (2 seconds apart)
HINT: Consider increasing the configuration parameter "checkpoint_segments".
LOG: checkpoints are occurring too frequently (2 seconds apart)
HINT: Consider increasing the configuration parameter "checkpoint_segments".
progress: 25.0 s, 4003.1 tps, lat 1.995 ms stddev 0.526
LOG: checkpoints are occurring too frequently (2 seconds apart)
HINT: Consider increasing the configuration parameter "checkpoint_segments".
LOG: checkpoints are occurring too frequently (1 second apart)
HINT: Consider increasing the configuration parameter "checkpoint_segments".
LOG: checkpoints are occurring too frequently (2 seconds apart)
HINT: Consider increasing the configuration parameter "checkpoint_segments".
progress: 30.0 s, 4025.5 tps, lat 1.984 ms stddev 0.568
transaction type: Gaussian distribution TPC-B (sort of)
scaling factor: 10
standard deviation threshold: 2.00000
access probability of top 20%, 10% and 5% records: 0.32566 0.16608 0.08345
query mode: simple
number of clients: 8
number of threads: 4
duration: 30 s
number of transactions actually processed: 120752
latency average: 1.984 ms
latency stddev: 0.638 ms
tps = 4024.823433 (including connections establishing)
tps = 4025.777787 (excluding connections establishing)

In '--gaussian=2' benchmark, checkpoint is frequently happen than '--gaussian=20'
benchmark. Because former update large range of records
so that fullpage write WALs are bigger than later. Former distribution updates
large range of records, so that fullpage-write WALs are bigger than later
distribution. Such benchmark was not able to come out by the past pgbench at all.

I think that this feature will be also useful for survey new buffer-replace
algorithm and checkpoint strategy, so on. If we remove this option, it is really
dissapointed..

Regards,
--
Mitsumasa KONDO
NTT Open Source Software Center

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#60Heikki Linnakangas
hlinnakangas@vmware.com
In reply to: KONDO Mitsumasa (#59)
Re: gaussian distribution pgbench

On 03/18/2014 11:57 AM, KONDO Mitsumasa wrote:

I think that this feature will be also useful for survey new buffer-replace
algorithm and checkpoint strategy, so on.

Sure. No doubt about that.

If we remove this option, it is really dissapointed..

As long as we get the \setrandom changes in, you can easily do these
tests using a custom script. There's nothing wrong with using a custom
script, it will be just as useful for exploring buffer replacement
algorithms, checkpoints etc. as a built-in option.

- Heikki

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#61Fabien COELHO
coelho@cri.ensmp.fr
In reply to: KONDO Mitsumasa (#49)
5 attachment(s)
Re: gaussian distribution pgbench

Please find attached an updated version "v13" for this patch.

I have (I hope) significanlty improved the documentation, including not so
helpful mathematical explanation about the actual meaning of the threshold
value. If a native English speaker could check the documentation, it would
be nice!

I have improved the implementation of the exponential distribution so as
to avoid a loop, which allows to lift the minimum threshold value
constraint, and the exponential pgbench summary displays decile and
first/last percent drawing probabilities. However, the same simplification
cannot be applied on the gaussian distribution part which must rely on a
loop, thus needs a minimal threshold for performance. I have also checked
(see the 4 attached scripts) the actual distribution against the computed
probabilities.

I disagree with the suggestion to remove the included gaussian &
exponential tests variants, because (1) it would mean removing the
specific summaries as well, which are essential to help feel how the
feature works; (2) the corresponding code in the source is rather
straightforward; (3) the tests correspond to the schema and data created
with -i, so it makes sense that they are stored in pgbench; (4) in order
for this feature to be used, it is best that it is available directly and
simply from pgbench, and not to be thought for elsewhere.

If this is a commit blocker, then the embedded script will have to be
removed, but I really think that they add a significant value to pgbench
and its "non uniform" features because they make it easy to test.

If Mitsumasa-san aggrees with these proposed changes, I would suggest to
apply this patch.

--
Fabien

Attachments:

test-exp.sqlapplication/x-sql; name=test-exp.sqlDownload
gaussian_and_exponential_pgbench_v13.patchtext/x-diff; charset=us-ascii; name=gaussian_and_exponential_pgbench_v13.patchDownload
diff --git a/contrib/pgbench/pgbench.c b/contrib/pgbench/pgbench.c
index 7c1e59e..eb1ecb3 100644
--- a/contrib/pgbench/pgbench.c
+++ b/contrib/pgbench/pgbench.c
@@ -41,6 +41,7 @@
 #include <math.h>
 #include <signal.h>
 #include <sys/time.h>
+#include <assert.h>
 #ifdef HAVE_SYS_SELECT_H
 #include <sys/select.h>
 #endif
@@ -98,6 +99,8 @@ 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 MIN_GAUSSIAN_THRESHOLD		2.0	/* minimum threshold for gauss */
+
 int			nxacts = 0;			/* number of transactions per client */
 int			duration = 0;		/* duration in seconds */
 
@@ -169,6 +172,14 @@ bool		is_connect;			/* establish connection for each transaction */
 bool		is_latencies;		/* report per-command latencies */
 int			main_pid;			/* main process id used in log filename */
 
+/* gaussian distribution tests: */
+double		stdev_threshold;   /* standard deviation threshold */
+bool        use_gaussian = false;
+
+/* exponential distribution tests: */
+double		exp_threshold;   /* threshold for exponential */
+bool		use_exponential = false;
+
 char	   *pghost = "";
 char	   *pgport = "";
 char	   *login = NULL;
@@ -330,6 +341,88 @@ static char *select_only = {
 	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
 };
 
+/* --exponential case */
+static char *exponential_tpc_b = {
+	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+	"\\setrandom aid 1 :naccounts exponential :exp_threshold\n"
+	"\\setrandom bid 1 :nbranches\n"
+	"\\setrandom tid 1 :ntellers\n"
+	"\\setrandom delta -5000 5000\n"
+	"BEGIN;\n"
+	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+	"UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;\n"
+	"UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;\n"
+	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+	"END;\n"
+};
+
+/* --exponential with -N case */
+static char *exponential_simple_update = {
+	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+	"\\setrandom aid 1 :naccounts exponential :exp_threshold\n"
+	"\\setrandom bid 1 :nbranches\n"
+	"\\setrandom tid 1 :ntellers\n"
+	"\\setrandom delta -5000 5000\n"
+	"BEGIN;\n"
+	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+	"END;\n"
+};
+
+/* --exponential with -S case */
+static char *exponential_select_only = {
+	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+	"\\setrandom aid 1 :naccounts exponential :exp_threshold\n"
+	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+};
+
+/* --gaussian case */
+static char *gaussian_tpc_b = {
+	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+	"\\setrandom aid 1 :naccounts gaussian :stdev_threshold\n"
+	"\\setrandom bid 1 :nbranches\n"
+	"\\setrandom tid 1 :ntellers\n"
+	"\\setrandom delta -5000 5000\n"
+	"BEGIN;\n"
+	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+	"UPDATE pgbench_tellers SET tbalance = tbalance + :delta WHERE tid = :tid;\n"
+	"UPDATE pgbench_branches SET bbalance = bbalance + :delta WHERE bid = :bid;\n"
+	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+	"END;\n"
+};
+
+/* --gaussian with -N case */
+static char *gaussian_simple_update = {
+	"\\set nbranches " CppAsString2(nbranches) " * :scale\n"
+	"\\set ntellers " CppAsString2(ntellers) " * :scale\n"
+	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+	"\\setrandom aid 1 :naccounts gaussian :stdev_threshold\n"
+	"\\setrandom bid 1 :nbranches\n"
+	"\\setrandom tid 1 :ntellers\n"
+	"\\setrandom delta -5000 5000\n"
+	"BEGIN;\n"
+	"UPDATE pgbench_accounts SET abalance = abalance + :delta WHERE aid = :aid;\n"
+	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+	"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid, :delta, CURRENT_TIMESTAMP);\n"
+	"END;\n"
+};
+
+/* --gaussian with -S case */
+static char *gaussian_select_only = {
+	"\\set naccounts " CppAsString2(naccounts) " * :scale\n"
+	"\\setrandom aid 1 :naccounts gaussian :stdev_threshold\n"
+	"SELECT abalance FROM pgbench_accounts WHERE aid = :aid;\n"
+};
+
 /* Function prototypes */
 static void setalarm(int seconds);
 static void *threadRun(void *arg);
@@ -373,6 +466,8 @@ usage(void)
 		   "  -v, --vacuum-all         vacuum all four standard tables before tests\n"
 		   "  --aggregate-interval=NUM aggregate data over NUM seconds\n"
 		   "  --sampling-rate=NUM      fraction of transactions to log (e.g. 0.01 for 1%%)\n"
+		   "  --exponential=NUM        exponential distribution with NUM threshold parameter\n"
+		   "  --gaussian=NUM           gaussian distribution with NUM threshold parameter\n"
 		   "\nCommon options:\n"
 		   "  -d, --debug              print debugging output\n"
 		   "  -h, --host=HOSTNAME      database server host or socket directory\n"
@@ -469,6 +564,73 @@ getrand(TState *thread, int64 min, int64 max)
 	return min + (int64) ((max - min + 1) * pg_erand48(thread->random_state));
 }
 
+/* random number generator: exponential distribution from min to max inclusive.
+ * the threshold is so that the density of probability for the last cut-off max
+ * value is exp(-exp_threshold).
+ */
+static int64
+getExponentialrand(TState *thread, int64 min, int64 max, double exp_threshold)
+{
+	double cut, uniform, rand;
+	assert(exp_threshold > 0.0);
+	cut = exp(-exp_threshold);
+	/* erand in [0, 1), uniform in (0, 1] */
+	uniform = 1.0 - pg_erand48(thread->random_state);
+	/* inner expresion in (cut, 1] (if exp_threshold > 0),
+	 * rand in [0, 1)
+	 */
+	assert((1.0 - cut) != 0.0);
+	rand = - log(cut + (1.0 - cut) * uniform) / exp_threshold;
+	/* return int64 random number within between min and max */
+	return min + (int64)((max - min + 1) * rand);
+}
+
+/* random number generator: gaussian distribution from min to max inclusive */
+static int64
+getGaussianrand(TState *thread, int64 min, int64 max, double stdev_threshold)
+{
+	double		stdev;
+	double		rand;
+
+	/*
+	 * Get user specified random number from this loop, with
+	 * -stdev_threshold < stdev <= stdev_threshold
+	 *
+	 * This loop is executed until the number is in the expected range.
+	 *
+	 * As the minimum threshold is 2.0, the probability of looping is low:
+	 * sqrt(-2 ln(r)) <= 2 => r >= e^{-2} ~ 0.135, then when taking the average
+	 * sinus multiplier as 2/pi, we have a 8.6% looping probability in the
+	 * worst case. For a 5.0 threshold value, the looping proability
+	 * is about e^{-5} * 2 / pi ~ 0.43%.
+	 */
+	do
+	{
+		/*
+		 * pg_erand48 generates [0,1), but for the basic version of the
+		 * Box-Muller transform the two uniformly distributed random numbers
+		 * are expected in (0, 1] (see http://en.wikipedia.org/wiki/Box_muller)
+		 */
+		double rand1 = 1.0 - pg_erand48(thread->random_state);
+		double rand2 = 1.0 - pg_erand48(thread->random_state);
+
+		/* Box-Muller basic form transform */
+		double var_sqrt = sqrt(-2.0 * log(rand1));
+		stdev = var_sqrt * sin(2.0 * M_PI * rand2);
+
+		/* we may try with cos, but there may be a bias induced if the previous
+		 * value fails the test? To be on the safe side, let us try over.
+		 */
+	}
+	while (stdev < -stdev_threshold || stdev >= stdev_threshold);
+
+	/* stdev is in [-threshold, threshold), normalization to [0,1) */
+	rand = (stdev + stdev_threshold) / (stdev_threshold * 2.0);
+
+	/* return int64 random number within between min and max */
+	return min + (int64)((max - min + 1) * rand);
+}
+
 /* call PQexec() and exit() on failure */
 static void
 executeStatement(PGconn *con, const char *sql)
@@ -1312,6 +1474,7 @@ top:
 			char	   *var;
 			int64		min,
 						max;
+			double		threshold = 0;
 			char		res[64];
 
 			if (*argv[2] == ':')
@@ -1357,11 +1520,11 @@ top:
 			}
 
 			/*
-			 * getrand() needs to be able to subtract max from min and add one
-			 * to the result without overflowing.  Since we know max > min, we
-			 * can detect overflow just by checking for a negative result. But
-			 * we must check both that the subtraction doesn't overflow, and
-			 * that adding one to the result doesn't overflow either.
+			 * Generate random number functions need to be able to subtract
+			 * max from min and add one to the result without overflowing.
+			 * Since we know max > min, we can detect overflow just by checking
+			 * for a negative result. But we must check both that the subtraction
+			 * doesn't overflow, and that adding one to the result doesn't overflow either.
 			 */
 			if (max - min < 0 || (max - min) + 1 < 0)
 			{
@@ -1370,10 +1533,63 @@ top:
 				return true;
 			}
 
+			if (argc == 4) /* uniform */
+			{
+#ifdef DEBUG
+				printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getrand(thread, min, max));
+#endif
+				snprintf(res, sizeof(res), INT64_FORMAT, getrand(thread, min, max));
+			}
+			else if ((pg_strcasecmp(argv[4], "gaussian") == 0) ||
+				 (pg_strcasecmp(argv[4], "exponential") == 0))
+			{
+				if (*argv[5] == ':')
+				{
+					if ((var = getVariable(st, argv[5] + 1)) == NULL)
+					{
+						fprintf(stderr, "%s: invalid threshold number %s\n", argv[0], argv[5]);
+						st->ecnt++;
+						return true;
+					}
+					threshold = strtod(var, NULL);
+				}
+				else
+					threshold = strtod(argv[5], NULL);
+
+				if (pg_strcasecmp(argv[4], "gaussian") == 0)
+				{
+					if (threshold < MIN_GAUSSIAN_THRESHOLD)
+					{
+						fprintf(stderr, "%s: gaussian threshold must be more than %f\n,", argv[5], MIN_GAUSSIAN_THRESHOLD);
+						st->ecnt++;
+						return true;
+					}
+#ifdef DEBUG
+					printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getGaussianrand(thread, min, max, threshold));
+#endif
+					snprintf(res, sizeof(res), INT64_FORMAT, getGaussianrand(thread, min, max, threshold));
+				}
+				else if (pg_strcasecmp(argv[4], "exponential") == 0)
+				{
+					if (threshold <= 0.0)
+					{
+						fprintf(stderr, "%s: exponential threshold must be strictly positive\n,", argv[5]);
+						st->ecnt++;
+						return true;
+					}
+#ifdef DEBUG
+					printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getExponentialrand(thread, min, max, threshold));
+#endif
+					snprintf(res, sizeof(res), INT64_FORMAT, getExponentialrand(thread, min, max, threshold));
+				}
+			}
+			else /* uniform with extra arguments */
+			{
 #ifdef DEBUG
-			printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getrand(thread, min, max));
+				printf("min: " INT64_FORMAT " max: " INT64_FORMAT " random: " INT64_FORMAT "\n", min, max, getrand(thread, min, max));
 #endif
-			snprintf(res, sizeof(res), INT64_FORMAT, getrand(thread, min, max));
+				snprintf(res, sizeof(res), INT64_FORMAT, getrand(thread, min, max));
+			}
 
 			if (!putVariable(st, argv[0], argv[1], res))
 			{
@@ -1903,9 +2119,34 @@ process_commands(char *buf)
 				exit(1);
 			}
 
-			for (j = 4; j < my_commands->argc; j++)
-				fprintf(stderr, "%s: extra argument \"%s\" ignored\n",
-						my_commands->argv[0], my_commands->argv[j]);
+			if (my_commands->argc == 4 ) /* uniform */
+			{
+				/* nothing to do */
+			}
+			else if ((pg_strcasecmp(my_commands->argv[4], "gaussian") == 0) ||
+				 (pg_strcasecmp(my_commands->argv[4], "exponential") == 0))
+			{
+				if (my_commands->argc < 6)
+				{
+					fprintf(stderr, "%s(%s): missing argument\n", my_commands->argv[0], my_commands->argv[4]);
+					exit(1);
+				}
+
+				for (j = 6; j < my_commands->argc; j++)
+					fprintf(stderr, "%s(%s): extra argument \"%s\" ignored\n",
+							my_commands->argv[0], my_commands->argv[4], my_commands->argv[j]);
+			}
+			else /* uniform with extra argument */
+			{
+				int arg_pos = 4;
+
+				if (pg_strcasecmp(my_commands->argv[4], "uniform") == 0)
+					arg_pos++;
+
+				for (j = arg_pos; j < my_commands->argc; j++)
+					fprintf(stderr, "%s(uniform): extra argument \"%s\" ignored\n",
+							my_commands->argv[0], my_commands->argv[j]);
+			}
 		}
 		else if (pg_strcasecmp(my_commands->argv[0], "set") == 0)
 		{
@@ -2161,6 +2402,17 @@ process_builtin(char *tb)
 	return my_commands;
 }
 
+/* compute the probability of the truncated exponential random generation
+ * to draw values in the i-th slot of the range.
+ */
+static double exponentialProbability(int i, int slots, double threshold)
+{
+	assert(1 <= i && i <= slots);
+	return (exp(- threshold * (i-1) / slots) - exp(- threshold * i / slots)) /
+		(1.0 - exp(- threshold));
+}
+
+
 /* print out results */
 static void
 printResults(int ttype, int normal_xacts, int nclients,
@@ -2180,16 +2432,62 @@ printResults(int ttype, int normal_xacts, int nclients,
 						(INSTR_TIME_GET_DOUBLE(conn_total_time) / nthreads));
 
 	if (ttype == 0)
-		s = "TPC-B (sort of)";
+	{
+		if (use_gaussian)
+			s = "Gaussian distribution TPC-B (sort of)";
+		else if (use_exponential)
+			s = "Exponential distribution TPC-B (sort of)";
+		else
+			s = "TPC-B (sort of)";
+	}
 	else if (ttype == 2)
-		s = "Update only pgbench_accounts";
+	{
+		if (use_gaussian)
+			s = "Gaussian distribution update only pgbench_accounts";
+		else if (use_exponential)
+			s = "Exponential distribution update only pgbench_accounts";
+		else
+			s = "Update only pgbench_accounts";
+	}
 	else if (ttype == 1)
-		s = "SELECT only";
+	{
+		if (use_gaussian)
+			s = "Gaussian distribution SELECT only";
+		else if (use_exponential)
+			s = "Exponential distribution SELECT only";
+		else
+			s = "SELECT only";
+	}
 	else
 		s = "Custom query";
 
 	printf("transaction type: %s\n", s);
 	printf("scaling factor: %d\n", scale);
+
+	/* output in gaussian distribution benchmark */
+	if (use_gaussian)
+	{
+		printf("standard deviation threshold: %.5f\n", stdev_threshold);
+		printf("access probability of top 20%%, 10%% and 5%% records: %.5f %.5f %.5f\n",
+			(double) ((erf (stdev_threshold * 0.2 / sqrt(2.0))) / (erf (stdev_threshold / sqrt(2.0)))),
+			(double) ((erf (stdev_threshold * 0.1 / sqrt(2.0))) / (erf (stdev_threshold / sqrt(2.0)))),
+			(double) ((erf (stdev_threshold * 0.05 / sqrt(2.0))) / (erf (stdev_threshold / sqrt(2.0)))));
+	}
+	/* output in exponential distribution benchmark */
+	else if (use_exponential)
+	{
+		int i;
+		printf("exponential threshold: %.5f\n", exp_threshold);
+		printf("decile percents:");
+		for (i = 1; i <= 10; i++)
+			printf(" %.1f%%",
+				   100.0 * exponentialProbability(i, 10, exp_threshold));
+		printf("\n");
+		printf("highest/lowest percent of the range: %.1f%% %.1f%%\n",
+			   100.0 * exponentialProbability(1, 100, exp_threshold),
+			   100.0 * exponentialProbability(100, 100, exp_threshold));
+	}
+
 	printf("query mode: %s\n", QUERYMODE[querymode]);
 	printf("number of clients: %d\n", nclients);
 	printf("number of threads: %d\n", nthreads);
@@ -2319,6 +2617,8 @@ main(int argc, char **argv)
 		{"unlogged-tables", no_argument, &unlogged_tables, 1},
 		{"sampling-rate", required_argument, NULL, 4},
 		{"aggregate-interval", required_argument, NULL, 5},
+		{"gaussian", required_argument, NULL, 6},
+		{"exponential", required_argument, NULL, 7},
 		{"rate", required_argument, NULL, 'R'},
 		{NULL, 0, NULL, 0}
 	};
@@ -2598,6 +2898,25 @@ main(int argc, char **argv)
 				}
 #endif
 				break;
+			case 6:
+				use_gaussian = true;
+				stdev_threshold = atof(optarg);
+				if(stdev_threshold < MIN_GAUSSIAN_THRESHOLD)
+				{
+					fprintf(stderr, "--gaussian=NUM must be more than %f: %f\n",
+							MIN_GAUSSIAN_THRESHOLD, stdev_threshold);
+					exit(1);
+				}
+				break;
+			case 7:
+				use_exponential = true;
+				exp_threshold = atof(optarg);
+				if(exp_threshold <= 0.0)
+				{
+					fprintf(stderr, "--exponential=NUM must be more 0.0\n");
+					exit(1);
+				}
+				break;
 			default:
 				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
 				exit(1);
@@ -2795,6 +3114,28 @@ main(int argc, char **argv)
 		}
 	}
 
+	/* set :stdev_threshold variable */
+	if(getVariable(&state[0], "stdev_threshold") == NULL)
+	{
+		snprintf(val, sizeof(val), "%lf", stdev_threshold);
+		for (i = 0; i < nclients; i++)
+		{
+			if (!putVariable(&state[i], "startup", "stdev_threshold", val))
+				exit(1);
+		}
+	}
+
+	/* set :exp_threshold variable */
+	if(getVariable(&state[0], "exp_threshold") == NULL)
+	{
+		snprintf(val, sizeof(val), "%lf", exp_threshold);
+		for (i = 0; i < nclients; i++)
+		{
+			if (!putVariable(&state[i], "startup", "exp_threshold", val))
+				exit(1);
+		}
+	}
+
 	if (!is_no_vacuum)
 	{
 		fprintf(stderr, "starting vacuum...");
@@ -2820,17 +3161,32 @@ main(int argc, char **argv)
 	switch (ttype)
 	{
 		case 0:
-			sql_files[0] = process_builtin(tpc_b);
+			if (use_gaussian)
+				sql_files[0] = process_builtin(gaussian_tpc_b);
+			else if (use_exponential)
+				sql_files[0] = process_builtin(exponential_tpc_b);
+			else
+				sql_files[0] = process_builtin(tpc_b);
 			num_files = 1;
 			break;
 
 		case 1:
-			sql_files[0] = process_builtin(select_only);
+			if (use_gaussian)
+				sql_files[0] = process_builtin(gaussian_select_only);
+			else if (use_exponential)
+				sql_files[0] = process_builtin(exponential_select_only);
+			else
+				sql_files[0] = process_builtin(select_only);
 			num_files = 1;
 			break;
 
 		case 2:
-			sql_files[0] = process_builtin(simple_update);
+			if (use_gaussian)
+				sql_files[0] = process_builtin(gaussian_simple_update);
+			else if (use_exponential)
+				sql_files[0] = process_builtin(exponential_simple_update);
+			else
+				sql_files[0] = process_builtin(simple_update);
 			num_files = 1;
 			break;
 
diff --git a/doc/src/sgml/pgbench.sgml b/doc/src/sgml/pgbench.sgml
index 4367563..3a561a9 100644
--- a/doc/src/sgml/pgbench.sgml
+++ b/doc/src/sgml/pgbench.sgml
@@ -307,6 +307,21 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
      </varlistentry>
 
      <varlistentry>
+      <term><option>--exponential</option><replaceable>threshold</></term>
+      <listitem>
+       <para>
+         Run exponential distribution pgbench test using this threshold parameter.
+         The threshold controls the distribution of access frequency on the
+         <structname>pgbench_accounts</> table.
+         See the <literal>\setrandom</> documentation below for details about
+         the impact of the threshold value.
+         When set, this option applies to all test variants (<option>-N</> for
+         skipping updates, or <option>-S</> for selects).
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
       <term><option>-f</option> <replaceable>filename</></term>
       <term><option>--file=</option><replaceable>filename</></term>
       <listitem>
@@ -320,6 +335,21 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
      </varlistentry>
 
      <varlistentry>
+      <term><option>--gaussian</option><replaceable>threshold</></term>
+      <listitem>
+       <para>
+         Run gaussian distribution pgbench test using this threshold parameter.
+         The threshold controls the distribution of access frequency on the
+         <structname>pgbench_accounts</> table.
+         See the <literal>\setrandom</> documentation below for details about
+         the impact of the threshold value.
+         When set, this option applies to all test variants (<option>-N</> for
+         skipping updates, or <option>-S</> for selects).
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
       <term><option>-j</option> <replaceable>threads</></term>
       <term><option>--jobs=</option><replaceable>threads</></term>
       <listitem>
@@ -748,8 +778,8 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
 
    <varlistentry>
     <term>
-     <literal>\setrandom <replaceable>varname</> <replaceable>min</> <replaceable>max</></literal>
-    </term>
+     <literal>\setrandom <replaceable>varname</> <replaceable>min</> <replaceable>max</> [ uniform | [ { gaussian | exponential } <replaceable>threshold</> ] ]</literal>
+     </term>
 
     <listitem>
      <para>
@@ -761,9 +791,44 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
      </para>
 
      <para>
+      The default random distribution is uniform. The gaussian and exponential
+      options allow to change the distribution. The mandatory
+      <replaceable>threshold</> double value controls the actual distribution.
+     </para>
+
+     <para>
+      With the gaussian option, the larger the <replaceable>threshold</>,
+      the more frequently values close to the middle of the interval are drawn,
+      and the less frequently values close to the <replaceable>min</> and
+      <replaceable>max</> bounds.
+      In other worlds, the larger the <replaceable>threshold</>,
+      the narrower the access range around the middle.
+      the smaller the threshold, the smoother the access pattern
+      distribution. The minimum threshold is 2.0 for performance.
+     </para>
+
+     <para>
+      With the exponential option, the <replaceable>threshold</> parameter
+      controls the distribution by truncating an exponential distribution at
+      a specific value, and then projecting onto integers between the bounds.
+      To be precise, the <replaceable>threshold</> is so that the density of
+      probability of the exponential distribution at the <replaceable>max</>
+      cut-off value is exp(-threshold), the density at the <replaceable>min</>
+      value being 1.
+      Intuitively, the larger the threshold, the more frequently values close to
+      <replaceable>min</> are accessed, and the less frequently values close to
+      <replaceable>max</> are accessed.
+      A crude approximation of the distribution is that the most frequent 1%
+      values are drawn <replaceable>threshold</>% of the time.
+      The closer to 0.0 the threshold, the flatter (more uniform) the access
+      distribution.
+      The threshold value must be strictly positive with the exponential option.
+     </para>
+
+     <para>
       Example:
 <programlisting>
-\setrandom aid 1 :naccounts
+\setrandom aid 1 :naccounts gaussian 5.0
 </programlisting></para>
     </listitem>
    </varlistentry>
test-0.sqlapplication/x-sql; name=test-0.sqlDownload
test-clean.sqlapplication/x-sql; name=test-clean.sqlDownload
test-show.sqlapplication/x-sql; name=test-show.sqlDownload