From 0802cdd382cdb11739f9054281f3ccbb975686c0 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 --- src/backend/commands/explain.c | 134 ++++++++++-------- src/test/regress/expected/select_parallel.out | 2 +- 2 files changed, 75 insertions(+), 61 deletions(-) diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index a1fb22c9f7..4247a3c749 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -2553,86 +2553,100 @@ show_tablesample(TableSampleClause *tsc, PlanState *planstate, } } -/* - * If it's EXPLAIN ANALYZE, show tuplesort stats for a sort node - */ 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) + snprintf(prefix, sizeof(prefix), ""); + 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