Hello Alvaro,

Thanks for the progress!

I pushed this, along with a few more tweaks, mostly adding comments and
moving functions so that related things are together.  I hope I didn't
break anything.

Looks ok.

Here is a rebase of the 3 remaining parts:
 - 15-c: per script stats
 - 15-d: weighted scripts
 - 15-e: prefix selection for -b

--
Fabien.
diff --git a/doc/src/sgml/ref/pgbench.sgml b/doc/src/sgml/ref/pgbench.sgml
index 42d0667..ade1b53 100644
--- a/doc/src/sgml/ref/pgbench.sgml
+++ b/doc/src/sgml/ref/pgbench.sgml
@@ -1138,6 +1138,9 @@ number of transactions actually processed: 10000/10000
 tps = 618.764555 (including connections establishing)
 tps = 622.977698 (excluding connections establishing)
 SQL script 1: <builtin: TPC-B (sort of)>
+ - 10000 transactions (100.0% of total, tps = 618.764555)
+ - latency average = 15.844 ms
+ - latency stddev = 2.715 ms
  - statement latencies in milliseconds:
         0.004386        \set nbranches 1 * :scale
         0.001343        \set ntellers 10 * :scale
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 44da3d1..64c7a6c 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -164,6 +164,7 @@ bool		use_log;			/* log transaction latencies to a file */
 bool		use_quiet;			/* quiet logging onto stderr */
 int			agg_interval;		/* log aggregates instead of individual
 								 * transactions */
+bool        per_script_stats = false; /* whether to collect stats per script */
 int			progress = 0;		/* thread progress report every this seconds */
 bool		progress_timestamp = false; /* progress report with Unix time */
 int			nclients = 1;		/* number of clients */
@@ -299,6 +300,7 @@ static struct
 {
 	const char *name;
 	Command   **commands;
+	StatsData stats;
 }	sql_script[MAX_SCRIPTS];	/* SQL script files */
 static int	num_scripts;		/* number of scripts in sql_script[] */
 static int	num_commands = 0;	/* total number of Command structs */
@@ -1326,7 +1328,7 @@ top:
 		/* transaction finished: calculate latency and log the transaction */
 		if (commands[st->state + 1] == NULL)
 		{
-			if (progress || throttle_delay || latency_limit || logfile)
+			if (progress || throttle_delay || latency_limit || per_script_stats || logfile)
 				processXactStats(thread, st, &now, false, logfile, agg);
 			else
 				thread->stats.cnt++;
@@ -1419,7 +1421,7 @@ top:
 	}
 
 	/* Record transaction start time under logging, progress or throttling */
-	if ((logfile || progress || throttle_delay || latency_limit) && st->state == 0)
+	if ((logfile || progress || throttle_delay || latency_limit || per_script_stats) && st->state == 0)
 	{
 		INSTR_TIME_SET_CURRENT(st->txn_begin);
 
@@ -1872,6 +1874,9 @@ processXactStats(TState *thread, CState *st, instr_time *now,
 
 	if (use_log)
 		doLog(thread, st, logfile, now, agg, skipped, latency, lag);
+
+	if (per_script_stats) /* mutex? hmmm... these are only statistics */
+		accumStats(& sql_script[st->use_file].stats, skipped, latency, lag);
 }
 
 
@@ -2661,6 +2666,7 @@ addScript(const char *name, Command **commands)
 
 	sql_script[num_scripts].name = name;
 	sql_script[num_scripts].commands = commands;
+	initStats(& sql_script[num_scripts].stats, 0.0);
 	num_scripts++;
 }
 
@@ -2744,22 +2750,40 @@ printResults(TState *threads, StatsData *total, instr_time total_time,
 	printf("tps = %f (including connections establishing)\n", tps_include);
 	printf("tps = %f (excluding connections establishing)\n", tps_exclude);
 
-	/* Report per-command latencies */
-	if (is_latencies)
+	/* Report per-script stats */
+	if (per_script_stats)
 	{
 		int			i;
 
 		for (i = 0; i < num_scripts; i++)
 		{
-			Command   **commands;
+			printf("SQL script %d: %s\n"
+				   " - "INT64_FORMAT" transactions (%.1f%% of total, tps = %f)\n",
+				   i + 1, sql_script[i].name,
+				   sql_script[i].stats.cnt,
+				   100.0 * sql_script[i].stats.cnt / total->cnt,
+				   sql_script[i].stats.cnt / time_include);
 
-			printf("SQL script %d: %s\n", i + 1, sql_script[i].name);
-			printf(" - statement latencies in milliseconds:\n");
+			if (latency_limit)
+				printf(" - number of transactions skipped: "INT64_FORMAT" (%.3f%%)\n",
+					   sql_script[i].stats.skipped,
+					   100.0 * sql_script[i].stats.skipped /
+					   (sql_script[i].stats.skipped + sql_script[i].stats.cnt));
 
-			for (commands = sql_script[i].commands; *commands != NULL; commands++)
-				printf("   %11.3f  %s\n",
-				  1000.0 * (*commands)->stats.sum / (*commands)->stats.count,
-					   (*commands)->line);
+			printSimpleStats(" - latency", & sql_script[i].stats.latency);
+
+			/* Report per-command latencies */
+			if (is_latencies)
+			{
+				Command ** com;
+
+				printf(" - statement latencies in milliseconds:\n");
+
+				for (com = sql_script[i].commands; *com != NULL; com++)
+					printf("   %11.3f  %s\n",
+						   1000.0 * (*com)->stats.sum / (*com)->stats.count,
+						   (*com)->line);
+			}
 		}
 	}
 }
@@ -2945,6 +2969,7 @@ main(int argc, char **argv)
 				break;
 			case 'r':
 				benchmarking_option_set = true;
+				per_script_stats = true;
 				is_latencies = true;
 				break;
 			case 's':
@@ -3168,6 +3193,10 @@ main(int argc, char **argv)
 		internal_script_used = true;
 	}
 
+	/* show per script stats if several scripts are used */
+	if (num_scripts > 1)
+		per_script_stats = true;
+
 	/*
 	 * Don't need more threads than there are clients.  (This is not merely an
 	 * optimization; throttle_delay is calculated incorrectly below if some
diff --git a/doc/src/sgml/ref/pgbench.sgml b/doc/src/sgml/ref/pgbench.sgml
index ade1b53..ca3e158 100644
--- a/doc/src/sgml/ref/pgbench.sgml
+++ b/doc/src/sgml/ref/pgbench.sgml
@@ -262,11 +262,13 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
 
     <variablelist>
      <varlistentry>
-      <term><option>-b</> <replaceable>scriptname</></term>
-      <term><option>--builtin</> <replaceable>scriptname</></term>
+      <term><option>-b</> <replaceable>scriptname[@weight]</></term>
+      <term><option>--builtin</> <replaceable>scriptname[@weight]</></term>
       <listitem>
        <para>
         Add the specified builtin script to the list of executed scripts.
+        An optional integer weight after <literal>@</> allows to adjust the
+        probability of drawing the test.
         Available builtin scripts are: <literal>tpcb-like</>,
         <literal>simple-update</> and <literal>select-only</>.
         With special name <literal>list</>, show the list of builtin scripts
@@ -321,12 +323,14 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
      </varlistentry>
 
      <varlistentry>
-      <term><option>-f</> <replaceable>filename</></term>
-      <term><option>--file=</><replaceable>filename</></term>
+      <term><option>-f</> <replaceable>filename[@weight]</></term>
+      <term><option>--file=</><replaceable>filename[@weight]</></term>
       <listitem>
        <para>
         Add a transaction script read from <replaceable>filename</> to
         the list of executed scripts.
+        An optional integer weight after <literal>@</> allows to adjust the
+        probability of drawing the test.
         See below for details.
        </para>
       </listitem>
@@ -689,6 +693,9 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
    Pgbench executes test scripts chosen randomly from a specified list.
    They include built-in scripts with <option>-b</> and
    user-provided custom scripts with <option>-f</>.
+   Each script may be given a relative weight specified after a
+   <literal>@</> so as to change its drawing probability.
+   The default weight is <literal>1</>.
  </para>
 
   <para>
@@ -1137,7 +1144,7 @@ number of transactions per client: 1000
 number of transactions actually processed: 10000/10000
 tps = 618.764555 (including connections establishing)
 tps = 622.977698 (excluding connections establishing)
-SQL script 1: &lt;builtin: TPC-B (sort of)&gt;
+SQL script 1, weight 1: &lt;builtin: TPC-B (sort of)&gt;
  - 10000 transactions (100.0% of total, tps = 618.764555)
  - latency average = 15.844 ms
  - latency stddev = 2.715 ms
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 64c7a6c..f77c7c6 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -179,6 +179,8 @@ char	   *login = NULL;
 char	   *dbName;
 const char *progname;
 
+#define WSEP '@' /* weight separator */
+
 volatile bool timer_exceeded = false;	/* flag from signal handler */
 
 /* variable definitions */
@@ -299,11 +301,14 @@ typedef struct
 static struct
 {
 	const char *name;
+	int weight;
 	Command   **commands;
 	StatsData stats;
 }	sql_script[MAX_SCRIPTS];	/* SQL script files */
 static int	num_scripts;		/* number of scripts in sql_script[] */
 static int	num_commands = 0;	/* total number of Command structs */
+static int  total_weight = 0;
+
 static int	debug = 0;			/* debug flag */
 
 /* Define builtin test scripts */
@@ -389,9 +394,9 @@ usage(void)
 	 "  --tablespace=TABLESPACE  create tables in the specified tablespace\n"
 		   "  --unlogged-tables        create tables as unlogged tables\n"
 		   "\nOptions to select what to run:\n"
-		   "  -b, --builtin=NAME       add buitin script (use \"-b list\" to display\n"
-		   "                           available scripts)\n"
-		   "  -f, --file=FILENAME      add transaction script from FILENAME\n"
+		   "  -b, --builtin=NAME[@W]   add weighted buitin script (use \"-b list\"\n"
+		   "                           to display available scripts)\n"
+		   "  -f, --file=FILENAME[@W]  add weighted transaction script from FILENAME\n"
 		   "  -N, --skip-some-updates  skip updates of pgbench_tellers and pgbench_branches\n"
 		   "                           (same as \"-b simple-update\")\n"
 		   "  -S, --select-only        perform SELECT-only transactions\n"
@@ -1206,10 +1211,17 @@ clientDone(CState *st, bool ok)
 static int
 chooseScript(TState *thread)
 {
+	int i = 0, w = 0, wc;
+
 	if (num_scripts == 1)
 		return 0;
 
-	return getrand(thread, 0, num_scripts - 1);
+	wc = (int) getrand(thread, 0, total_weight - 1);
+	do {
+		w += sql_script[i++].weight;
+	} while (w <= wc);
+
+	return i - 1;
 }
 
 /* return false iff client should be disconnected */
@@ -2649,8 +2661,41 @@ findBuiltin(const char *name, char **desc)
 	exit(1);
 }
 
+/* Possiby truncate option and return weight */
+static int
+getWeight(char *option)
+{
+	char   *sep;
+	int		weight;
+
+	if ((sep = strrchr(option, WSEP)))
+	{
+		char *s;
+		*sep++ = '\0';
+
+		/* check that the weight is a positive integer */
+		s = sep;
+		while ('0' <= *s && *s <= '9')
+			s++;
+		if (*s != '\0' || s == sep)
+		{
+			/* empty or any other char */
+			fprintf(stderr,
+					"weight for script \"%s\" must be an integer, got \"%s\"\n",
+					option, sep);
+			exit(1);
+		}
+
+		weight = atoi(sep);
+	}
+	else
+		weight = 1;
+
+	return weight;
+}
+
 static void
-addScript(const char *name, Command **commands)
+addScript(const char *name, Command **commands, int weight)
 {
 	if (commands == NULL)
 	{
@@ -2665,6 +2710,7 @@ addScript(const char *name, Command **commands)
 	}
 
 	sql_script[num_scripts].name = name;
+	sql_script[num_scripts].weight = weight;
 	sql_script[num_scripts].commands = commands;
 	initStats(& sql_script[num_scripts].stats, 0.0);
 	num_scripts++;
@@ -2757,9 +2803,9 @@ printResults(TState *threads, StatsData *total, instr_time total_time,
 
 		for (i = 0; i < num_scripts; i++)
 		{
-			printf("SQL script %d: %s\n"
+			printf("SQL script %d, weight %d: %s\n"
 				   " - "INT64_FORMAT" transactions (%.1f%% of total, tps = %f)\n",
-				   i + 1, sql_script[i].name,
+				   i + 1, sql_script[i].weight, sql_script[i].name,
 				   sql_script[i].stats.cnt,
 				   100.0 * sql_script[i].stats.cnt / total->cnt,
 				   sql_script[i].stats.cnt / time_include);
@@ -2850,6 +2896,7 @@ main(int argc, char **argv)
 	instr_time	conn_total_time;
 	int64		latency_late = 0;
 	StatsData	stats;
+	int			weight;
 	char	   *desc;
 
 	int			i;
@@ -3029,27 +3076,32 @@ main(int argc, char **argv)
 					exit(0);
 				}
 
+				weight = getWeight(optarg);
 				addScript(desc,
-						  process_builtin(findBuiltin(optarg, &desc), desc));
+						  process_builtin(findBuiltin(optarg, &desc), desc),
+						  weight);
 				benchmarking_option_set = true;
 				internal_script_used = true;
 				break;
 			case 'S':
 				addScript(desc,
 						  process_builtin(findBuiltin("select-only", &desc),
-										  desc));
+										  desc),
+						  1);
 				benchmarking_option_set = true;
 				internal_script_used = true;
 				break;
 			case 'N':
 				addScript(desc,
 						  process_builtin(findBuiltin("simple-update", &desc),
-										  desc));
+										  desc),
+						  1);
 				benchmarking_option_set = true;
 				internal_script_used = true;
 				break;
 			case 'f':
-				addScript(optarg, process_file(optarg));
+				weight = getWeight(optarg);
+				addScript(optarg, process_file(optarg), weight);
 				benchmarking_option_set = true;
 				break;
 			case 'D':
@@ -3188,11 +3240,16 @@ main(int argc, char **argv)
 	if (num_scripts == 0 && !is_init_mode)
 	{
 		addScript(desc,
-				  process_builtin(findBuiltin("tpcb-like", &desc), desc));
+				  process_builtin(findBuiltin("tpcb-like", &desc), desc),
+				  1);
 		benchmarking_option_set = true;
 		internal_script_used = true;
 	}
 
+	/* compute total_weight */
+	for (i = 0; i < num_scripts; i++)
+		total_weight += sql_script[i].weight;
+
 	/* show per script stats if several scripts are used */
 	if (num_scripts > 1)
 		per_script_stats = true;
diff --git a/doc/src/sgml/ref/pgbench.sgml b/doc/src/sgml/ref/pgbench.sgml
index ca3e158..52dc142 100644
--- a/doc/src/sgml/ref/pgbench.sgml
+++ b/doc/src/sgml/ref/pgbench.sgml
@@ -271,6 +271,9 @@ pgbench <optional> <replaceable>options</> </optional> <replaceable>dbname</>
         probability of drawing the test.
         Available builtin scripts are: <literal>tpcb-like</>,
         <literal>simple-update</> and <literal>select-only</>.
+        The provided <replaceable>scriptname</> needs only be an unambiguous
+        prefix of the builtin name, hence <literal>si</> would be enough to
+        select <literal>simple-update</>.
         With special name <literal>list</>, show the list of builtin scripts
         and exit immediately.
        </para>
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index f77c7c6..5b4424e 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -2641,22 +2641,32 @@ listAvailableScripts(void)
 	fprintf(stderr, "\n");
 }
 
+/* return commands for selected builtin script, if unambiguous */
 static char *
 findBuiltin(const char *name, char **desc)
 {
-	int			i;
+	int			i, found = 0, len = strlen(name);
+	char	   *commands = NULL;
 
 	for (i = 0; i < N_BUILTIN; i++)
 	{
-		if (strncmp(builtin_script[i].name, name,
-					strlen(builtin_script[i].name)) == 0)
+		if (strncmp(builtin_script[i].name, name, len) == 0)
 		{
 			*desc = builtin_script[i].desc;
-			return builtin_script[i].commands;
+			commands = builtin_script[i].commands;
+			found++;
 		}
 	}
 
-	fprintf(stderr, "no builtin script found for name \"%s\"\n", name);
+	if (found == 1)
+		return commands;
+
+	/* error cases */
+	if (found == 0)
+		fprintf(stderr, "no builtin script found for name \"%s\"\n", name);
+	else /* found > 1 */
+		fprintf(stderr,
+				"%d builtin scripts found for prefix \"%s\"\n", found, name);
 	listAvailableScripts();
 	exit(1);
 }
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to