From 23be3e560fbd99fd142e177d04bbc1d8cee4c459 Mon Sep 17 00:00:00 2001 From: Thomas Munro Date: Wed, 23 Oct 2019 17:39:09 +1300 Subject: [PATCH 2/2] Improve EXPLAIN of Sort in parallel queries. Previously, it wasn't very clear to the uninitiated what work a Sort node did in the leader process. Label the ANALYZE output explicitly, like VERBOSE does. Author: Thomas Munro Reviewed-by: Melanie Plageman, Rafia Sabih Discussion: https://postgr.es/m/CA+hUKG+Z22=vkVkXtKRznzRdtj=MtygfZMYUJs8j7ObjzkG1Lw@mail.gmail.com --- src/backend/commands/explain.c | 133 ++++++++++-------- src/test/regress/expected/select_parallel.out | 2 +- 2 files changed, 76 insertions(+), 59 deletions(-) diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 89c39de72a..dcc359de89 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -2561,85 +2561,102 @@ show_tablesample(TableSampleClause *tsc, PlanState *planstate, } /* - * If it's EXPLAIN ANALYZE, show tuplesort stats for a sort node + * Workhorse for show_sort_info(). Use work number -1 for the leader process. */ static void -show_sort_info(SortState *sortstate, ExplainState *es) +show_sort_info_per_process(TuplesortInstrumentation *sinstrument, + bool has_workers, + int worker_number, + bool *opened_group, + ExplainState *es) { - if (!es->analyze) - return; + const char *sortMethod; + const char *spaceType; + long spaceUsed; - if (sortstate->sort_Done && sortstate->tuplesortstate != NULL) + if (sinstrument->sortMethod == SORT_TYPE_STILL_IN_PROGRESS) + return; /* ignore any unfilled slots */ + + sortMethod = tuplesort_method_name(sinstrument->sortMethod); + spaceType = tuplesort_space_type_name(sinstrument->spaceType); + spaceUsed = sinstrument->spaceUsed; + + if (es->format == EXPLAIN_FORMAT_TEXT) { - Tuplesortstate *state = (Tuplesortstate *) sortstate->tuplesortstate; - TuplesortInstrumentation stats; - const char *sortMethod; - const char *spaceType; - long spaceUsed; + char prefix[80]; - tuplesort_get_stats(state, &stats); - sortMethod = tuplesort_method_name(stats.sortMethod); - spaceType = tuplesort_space_type_name(stats.spaceType); - spaceUsed = stats.spaceUsed; + if (!has_workers) + prefix[0] = '\0'; + else if (worker_number < 0) + snprintf(prefix, sizeof(prefix), "Leader: "); + else + snprintf(prefix, sizeof(prefix), "Worker %d: ", worker_number); - if (es->format == EXPLAIN_FORMAT_TEXT) + appendStringInfoSpaces(es->str, es->indent * 2); + appendStringInfo(es->str, + "%sSort Method: %s %s: %ldkB\n", + prefix, sortMethod, spaceType, spaceUsed); + } + else + { + if (!has_workers) { - appendStringInfoSpaces(es->str, es->indent * 2); - appendStringInfo(es->str, "Sort Method: %s %s: %ldkB\n", - sortMethod, spaceType, spaceUsed); + ExplainPropertyText("Sort Method", sortMethod, es); + ExplainPropertyInteger("Sort Space Used", "kB", spaceUsed, es); + ExplainPropertyText("Sort Space Type", spaceType, es); } else { + if (!*opened_group) + { + ExplainOpenGroup("Workers", "Workers", false, es); + *opened_group = true; + } + ExplainOpenGroup("Worker", NULL, true, es); + ExplainPropertyInteger("Worker Number", NULL, worker_number, es); ExplainPropertyText("Sort Method", sortMethod, es); ExplainPropertyInteger("Sort Space Used", "kB", spaceUsed, es); ExplainPropertyText("Sort Space Type", spaceType, es); + ExplainCloseGroup("Worker", NULL, true, es); } } +} - if (sortstate->shared_info != NULL) +/* + * If it's EXPLAIN ANALYZE, show tuplesort stats for a sort node + */ +static void +show_sort_info(SortState *sortstate, ExplainState *es) +{ + bool opened_group = false; + + if (!es->analyze) + return; + + /* + * Show the leader stats for parallel queries, or only stats for + * non-parallel queries. + */ + if (sortstate->sort_Done && sortstate->tuplesortstate != NULL) { - int n; - bool opened_group = false; + Tuplesortstate *state = (Tuplesortstate *) sortstate->tuplesortstate; + TuplesortInstrumentation stats; - for (n = 0; n < sortstate->shared_info->num_workers; n++) - { - TuplesortInstrumentation *sinstrument; - const char *sortMethod; - const char *spaceType; - long spaceUsed; - - sinstrument = &sortstate->shared_info->sinstrument[n]; - if (sinstrument->sortMethod == SORT_TYPE_STILL_IN_PROGRESS) - continue; /* ignore any unfilled slots */ - sortMethod = tuplesort_method_name(sinstrument->sortMethod); - spaceType = tuplesort_space_type_name(sinstrument->spaceType); - spaceUsed = sinstrument->spaceUsed; + tuplesort_get_stats(state, &stats); + show_sort_info_per_process(&stats, sortstate->shared_info != NULL, + -1, &opened_group, es); + } - if (es->format == EXPLAIN_FORMAT_TEXT) - { - appendStringInfoSpaces(es->str, es->indent * 2); - appendStringInfo(es->str, - "Worker %d: Sort Method: %s %s: %ldkB\n", - n, sortMethod, spaceType, spaceUsed); - } - else - { - if (!opened_group) - { - ExplainOpenGroup("Workers", "Workers", false, es); - opened_group = true; - } - ExplainOpenGroup("Worker", NULL, true, es); - ExplainPropertyInteger("Worker Number", NULL, n, es); - ExplainPropertyText("Sort Method", sortMethod, es); - ExplainPropertyInteger("Sort Space Used", "kB", spaceUsed, es); - ExplainPropertyText("Sort Space Type", spaceType, es); - ExplainCloseGroup("Worker", NULL, true, es); - } - } - if (opened_group) - ExplainCloseGroup("Workers", "Workers", false, es); + /* Show the worker stats. */ + if (sortstate->shared_info != NULL) + { + for (int n = 0; n < sortstate->shared_info->num_workers; n++) + show_sort_info_per_process(&sortstate->shared_info->sinstrument[n], + true, n, &opened_group, es); } + + if (opened_group) + ExplainCloseGroup("Workers", "Workers", false, es); } /* diff --git a/src/test/regress/expected/select_parallel.out b/src/test/regress/expected/select_parallel.out index 0eca76cb41..5c0738ccdc 100644 --- a/src/test/regress/expected/select_parallel.out +++ b/src/test/regress/expected/select_parallel.out @@ -551,7 +551,7 @@ select * from explain_parallel_sort_stats(); Workers Launched: 4 -> Sort (actual rows=2000 loops=15) Sort Key: tenk1.ten - Sort Method: quicksort Memory: xxx + Leader: Sort Method: quicksort Memory: xxx Worker 0: Sort Method: quicksort Memory: xxx Worker 1: Sort Method: quicksort Memory: xxx Worker 2: Sort Method: quicksort Memory: xxx -- 2.23.0