Commit 16827d44 authored by Tom Lane's avatar Tom Lane

pgbench: fix stats reporting when some transactions are skipped.

pgbench can skip some transactions when both -R and -L options are used.
Previously, this resulted in slightly silly statistics both in progress
reports and final output, because the skipped transactions were counted
as executed for TPS and related stats.  Discount skipped xacts in TPS
numbers, and also when figuring the percentage of xacts exceeding the
latency limit.

Also, don't print per-script skipped-transaction counts when there is
only one script.  That's redundant with the overall count, and it's
inconsistent with the fact that we don't print other per-script stats
when there's only one script.  Clean up some unnecessary interactions
between what should be independent options that were due to that
decision.

While at it, avoid division-by-zero in cases where no transactions were
executed.  While on modern platforms this would generally result in
printing "NaN" rather than a crash, that isn't spelled consistently
across platforms and it would confuse many people.  Skip the relevant
output entirely when practical, else print zeroes.

Fabien Coelho, reviewed by Steve Singer, additional hacking by me

Discussion: https://postgr.es/m/26654.1505232433@sss.pgh.pa.us
parent 41761265
...@@ -1363,8 +1363,7 @@ latency average = 15.844 ms ...@@ -1363,8 +1363,7 @@ latency average = 15.844 ms
latency stddev = 2.715 ms latency stddev = 2.715 ms
tps = 618.764555 (including connections establishing) tps = 618.764555 (including connections establishing)
tps = 622.977698 (excluding connections establishing) tps = 622.977698 (excluding connections establishing)
script statistics: statement latencies in milliseconds:
- statement latencies in milliseconds:
0.002 \set aid random(1, 100000 * :scale) 0.002 \set aid random(1, 100000 * :scale)
0.005 \set bid random(1, 1 * :scale) 0.005 \set bid random(1, 1 * :scale)
0.002 \set tid random(1, 10 * :scale) 0.002 \set tid random(1, 10 * :scale)
......
...@@ -3664,27 +3664,32 @@ addScript(ParsedScript script) ...@@ -3664,27 +3664,32 @@ addScript(ParsedScript script)
static void static void
printSimpleStats(const char *prefix, SimpleStats *ss) printSimpleStats(const char *prefix, SimpleStats *ss)
{ {
/* print NaN if no transactions where executed */ if (ss->count > 0)
{
double latency = ss->sum / ss->count; double latency = ss->sum / ss->count;
double stddev = sqrt(ss->sum2 / ss->count - latency * latency); double stddev = sqrt(ss->sum2 / ss->count - latency * latency);
printf("%s average = %.3f ms\n", prefix, 0.001 * latency); printf("%s average = %.3f ms\n", prefix, 0.001 * latency);
printf("%s stddev = %.3f ms\n", prefix, 0.001 * stddev); printf("%s stddev = %.3f ms\n", prefix, 0.001 * stddev);
}
} }
/* print out results */ /* print out results */
static void static void
printResults(TState *threads, StatsData *total, instr_time total_time, printResults(TState *threads, StatsData *total, instr_time total_time,
instr_time conn_total_time, int latency_late) instr_time conn_total_time, int64 latency_late)
{ {
double time_include, double time_include,
tps_include, tps_include,
tps_exclude; tps_exclude;
int64 ntx = total->cnt - total->skipped;
time_include = INSTR_TIME_GET_DOUBLE(total_time); time_include = INSTR_TIME_GET_DOUBLE(total_time);
tps_include = total->cnt / time_include;
tps_exclude = total->cnt / (time_include - /* tps is about actually executed transactions */
(INSTR_TIME_GET_DOUBLE(conn_total_time) / nclients)); tps_include = ntx / time_include;
tps_exclude = ntx /
(time_include - (INSTR_TIME_GET_DOUBLE(conn_total_time) / nclients));
/* Report test parameters. */ /* Report test parameters. */
printf("transaction type: %s\n", printf("transaction type: %s\n",
...@@ -3697,13 +3702,13 @@ printResults(TState *threads, StatsData *total, instr_time total_time, ...@@ -3697,13 +3702,13 @@ printResults(TState *threads, StatsData *total, instr_time total_time,
{ {
printf("number of transactions per client: %d\n", nxacts); printf("number of transactions per client: %d\n", nxacts);
printf("number of transactions actually processed: " INT64_FORMAT "/%d\n", printf("number of transactions actually processed: " INT64_FORMAT "/%d\n",
total->cnt - total->skipped, nxacts * nclients); ntx, nxacts * nclients);
} }
else else
{ {
printf("duration: %d s\n", duration); printf("duration: %d s\n", duration);
printf("number of transactions actually processed: " INT64_FORMAT "\n", printf("number of transactions actually processed: " INT64_FORMAT "\n",
total->cnt); ntx);
} }
/* Remaining stats are nonsensical if we failed to execute any xacts */ /* Remaining stats are nonsensical if we failed to execute any xacts */
...@@ -3716,9 +3721,9 @@ printResults(TState *threads, StatsData *total, instr_time total_time, ...@@ -3716,9 +3721,9 @@ printResults(TState *threads, StatsData *total, instr_time total_time,
100.0 * total->skipped / total->cnt); 100.0 * total->skipped / total->cnt);
if (latency_limit) if (latency_limit)
printf("number of transactions above the %.1f ms latency limit: %d (%.3f %%)\n", printf("number of transactions above the %.1f ms latency limit: " INT64_FORMAT "/" INT64_FORMAT " (%.3f %%)\n",
latency_limit / 1000.0, latency_late, latency_limit / 1000.0, latency_late, ntx,
100.0 * latency_late / total->cnt); (ntx > 0) ? 100.0 * latency_late / ntx : 0.0);
if (throttle_delay || progress || latency_limit) if (throttle_delay || progress || latency_limit)
printSimpleStats("latency", &total->latency); printSimpleStats("latency", &total->latency);
...@@ -3745,50 +3750,58 @@ printResults(TState *threads, StatsData *total, instr_time total_time, ...@@ -3745,50 +3750,58 @@ printResults(TState *threads, StatsData *total, instr_time total_time,
printf("tps = %f (excluding connections establishing)\n", tps_exclude); printf("tps = %f (excluding connections establishing)\n", tps_exclude);
/* Report per-script/command statistics */ /* Report per-script/command statistics */
if (per_script_stats || latency_limit || is_latencies) if (per_script_stats || is_latencies)
{ {
int i; int i;
for (i = 0; i < num_scripts; i++) for (i = 0; i < num_scripts; i++)
{ {
if (num_scripts > 1) if (per_script_stats)
{
StatsData *sstats = &sql_script[i].stats;
printf("SQL script %d: %s\n" printf("SQL script %d: %s\n"
" - weight: %d (targets %.1f%% of total)\n" " - weight: %d (targets %.1f%% of total)\n"
" - " INT64_FORMAT " transactions (%.1f%% of total, tps = %f)\n", " - " INT64_FORMAT " transactions (%.1f%% of total, tps = %f)\n",
i + 1, sql_script[i].desc, i + 1, sql_script[i].desc,
sql_script[i].weight, sql_script[i].weight,
100.0 * sql_script[i].weight / total_weight, 100.0 * sql_script[i].weight / total_weight,
sql_script[i].stats.cnt, sstats->cnt,
100.0 * sql_script[i].stats.cnt / total->cnt, 100.0 * sstats->cnt / total->cnt,
sql_script[i].stats.cnt / time_include); (sstats->cnt - sstats->skipped) / time_include);
else
printf("script statistics:\n");
if (latency_limit) if (throttle_delay && latency_limit && sstats->cnt > 0)
printf(" - number of transactions skipped: " INT64_FORMAT " (%.3f%%)\n", printf(" - number of transactions skipped: " INT64_FORMAT " (%.3f%%)\n",
sql_script[i].stats.skipped, sstats->skipped,
100.0 * sql_script[i].stats.skipped / sql_script[i].stats.cnt); 100.0 * sstats->skipped / sstats->cnt);
if (num_scripts > 1) printSimpleStats(" - latency", &sstats->latency);
printSimpleStats(" - latency", &sql_script[i].stats.latency); }
/* Report per-command latencies */ /* Report per-command latencies */
if (is_latencies) if (is_latencies)
{ {
Command **commands; Command **commands;
if (per_script_stats)
printf(" - statement latencies in milliseconds:\n"); printf(" - statement latencies in milliseconds:\n");
else
printf("statement latencies in milliseconds:\n");
for (commands = sql_script[i].commands; for (commands = sql_script[i].commands;
*commands != NULL; *commands != NULL;
commands++) commands++)
{
SimpleStats *cstats = &(*commands)->stats;
printf(" %11.3f %s\n", printf(" %11.3f %s\n",
1000.0 * (*commands)->stats.sum / (cstats->count > 0) ?
(*commands)->stats.count, 1000.0 * cstats->sum / cstats->count : 0.0,
(*commands)->line); (*commands)->line);
} }
} }
} }
}
} }
...@@ -3984,7 +3997,6 @@ main(int argc, char **argv) ...@@ -3984,7 +3997,6 @@ main(int argc, char **argv)
break; break;
case 'r': case 'r':
benchmarking_option_set = true; benchmarking_option_set = true;
per_script_stats = true;
is_latencies = true; is_latencies = true;
break; break;
case 's': case 's':
...@@ -4861,7 +4873,8 @@ threadRun(void *arg) ...@@ -4861,7 +4873,8 @@ threadRun(void *arg)
{ {
/* generate and show report */ /* generate and show report */
StatsData cur; StatsData cur;
int64 run = now - last_report; int64 run = now - last_report,
ntx;
double tps, double tps,
total_run, total_run,
latency, latency,
...@@ -4876,7 +4889,7 @@ threadRun(void *arg) ...@@ -4876,7 +4889,7 @@ threadRun(void *arg)
* XXX: No locking. There is no guarantee that we get an * XXX: No locking. There is no guarantee that we get an
* atomic snapshot of the transaction count and latencies, so * atomic snapshot of the transaction count and latencies, so
* these figures can well be off by a small amount. The * these figures can well be off by a small amount. The
* progress is report's purpose is to give a quick overview of * progress report's purpose is to give a quick overview of
* how the test is going, so that shouldn't matter too much. * how the test is going, so that shouldn't matter too much.
* (If a read from a 64-bit integer is not atomic, you might * (If a read from a 64-bit integer is not atomic, you might
* get a "torn" read and completely bogus latencies though!) * get a "torn" read and completely bogus latencies though!)
...@@ -4890,15 +4903,21 @@ threadRun(void *arg) ...@@ -4890,15 +4903,21 @@ threadRun(void *arg)
cur.skipped += thread[i].stats.skipped; cur.skipped += thread[i].stats.skipped;
} }
/* we count only actually executed transactions */
ntx = (cur.cnt - cur.skipped) - (last.cnt - last.skipped);
total_run = (now - thread_start) / 1000000.0; total_run = (now - thread_start) / 1000000.0;
tps = 1000000.0 * (cur.cnt - last.cnt) / run; tps = 1000000.0 * ntx / run;
latency = 0.001 * (cur.latency.sum - last.latency.sum) / if (ntx > 0)
(cur.cnt - last.cnt); {
sqlat = 1.0 * (cur.latency.sum2 - last.latency.sum2) latency = 0.001 * (cur.latency.sum - last.latency.sum) / ntx;
/ (cur.cnt - last.cnt); sqlat = 1.0 * (cur.latency.sum2 - last.latency.sum2) / ntx;
stdev = 0.001 * sqrt(sqlat - 1000000.0 * latency * latency); stdev = 0.001 * sqrt(sqlat - 1000000.0 * latency * latency);
lag = 0.001 * (cur.lag.sum - last.lag.sum) / lag = 0.001 * (cur.lag.sum - last.lag.sum) / ntx;
(cur.cnt - last.cnt); }
else
{
latency = sqlat = stdev = lag = 0;
}
if (progress_timestamp) if (progress_timestamp)
{ {
...@@ -4915,7 +4934,10 @@ threadRun(void *arg) ...@@ -4915,7 +4934,10 @@ threadRun(void *arg)
(long) tv.tv_sec, (long) (tv.tv_usec / 1000)); (long) tv.tv_sec, (long) (tv.tv_usec / 1000));
} }
else else
{
/* round seconds are expected, but the thread may be late */
snprintf(tbuf, sizeof(tbuf), "%.1f s", total_run); snprintf(tbuf, sizeof(tbuf), "%.1f s", total_run);
}
fprintf(stderr, fprintf(stderr,
"progress: %s, %.1f tps, lat %.3f ms stddev %.3f", "progress: %s, %.1f tps, lat %.3f ms stddev %.3f",
......
...@@ -427,7 +427,7 @@ pgbench( ...@@ -427,7 +427,7 @@ pgbench(
0, 0,
[ qr{processed: [01]/10}, [ qr{processed: [01]/10},
qr{type: .*/001_pgbench_sleep}, qr{type: .*/001_pgbench_sleep},
qr{above the 1.0 ms latency limit: [01] } ], qr{above the 1.0 ms latency limit: [01]/} ],
[qr{^$}i], [qr{^$}i],
'pgbench late throttling', 'pgbench late throttling',
{ '001_pgbench_sleep' => q{\sleep 2ms} }); { '001_pgbench_sleep' => q{\sleep 2ms} });
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment