mirror of
https://github.com/postgres/postgres.git
synced 2025-06-30 21:42:05 +03:00
Propagate sort instrumentation from workers back to leader.
Up until now, when parallel query was used, no details about the sort method or space used by the workers were available; details were shown only for any sorting done by the leader. Fix that. Commit1177ab1dab
forced the test case added by commit1f6d515a67
to run without parallelism; now that we have this infrastructure, allow that again, with a little tweaking to make it pass with and without force_parallel_mode. Robert Haas and Tom Lane Discussion: http://postgr.es/m/CA+Tgmoa2VBZW6S8AAXfhpHczb=Rf6RqQ2br+zJvEgwJ0uoD_tQ@mail.gmail.com
This commit is contained in:
@ -2292,15 +2292,21 @@ show_tablesample(TableSampleClause *tsc, PlanState *planstate,
|
|||||||
static void
|
static void
|
||||||
show_sort_info(SortState *sortstate, ExplainState *es)
|
show_sort_info(SortState *sortstate, ExplainState *es)
|
||||||
{
|
{
|
||||||
if (es->analyze && sortstate->sort_Done &&
|
if (!es->analyze)
|
||||||
sortstate->tuplesortstate != NULL)
|
return;
|
||||||
|
|
||||||
|
if (sortstate->sort_Done && sortstate->tuplesortstate != NULL)
|
||||||
{
|
{
|
||||||
Tuplesortstate *state = (Tuplesortstate *) sortstate->tuplesortstate;
|
Tuplesortstate *state = (Tuplesortstate *) sortstate->tuplesortstate;
|
||||||
|
TuplesortInstrumentation stats;
|
||||||
const char *sortMethod;
|
const char *sortMethod;
|
||||||
const char *spaceType;
|
const char *spaceType;
|
||||||
long spaceUsed;
|
long spaceUsed;
|
||||||
|
|
||||||
tuplesort_get_stats(state, &sortMethod, &spaceType, &spaceUsed);
|
tuplesort_get_stats(state, &stats);
|
||||||
|
sortMethod = tuplesort_method_name(stats.sortMethod);
|
||||||
|
spaceType = tuplesort_space_type_name(stats.spaceType);
|
||||||
|
spaceUsed = stats.spaceUsed;
|
||||||
|
|
||||||
if (es->format == EXPLAIN_FORMAT_TEXT)
|
if (es->format == EXPLAIN_FORMAT_TEXT)
|
||||||
{
|
{
|
||||||
@ -2315,6 +2321,51 @@ show_sort_info(SortState *sortstate, ExplainState *es)
|
|||||||
ExplainPropertyText("Sort Space Type", spaceType, es);
|
ExplainPropertyText("Sort Space Type", spaceType, es);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sortstate->shared_info != NULL)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
bool opened_group = false;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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", n, es);
|
||||||
|
ExplainPropertyText("Sort Method", sortMethod, es);
|
||||||
|
ExplainPropertyLong("Sort Space Used", spaceUsed, es);
|
||||||
|
ExplainPropertyText("Sort Space Type", spaceType, es);
|
||||||
|
ExplainCloseGroup("Worker", NULL, true, es);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (opened_group)
|
||||||
|
ExplainCloseGroup("Workers", "Workers", false, es);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -28,9 +28,10 @@
|
|||||||
#include "executor/nodeBitmapHeapscan.h"
|
#include "executor/nodeBitmapHeapscan.h"
|
||||||
#include "executor/nodeCustom.h"
|
#include "executor/nodeCustom.h"
|
||||||
#include "executor/nodeForeignscan.h"
|
#include "executor/nodeForeignscan.h"
|
||||||
#include "executor/nodeSeqscan.h"
|
|
||||||
#include "executor/nodeIndexscan.h"
|
#include "executor/nodeIndexscan.h"
|
||||||
#include "executor/nodeIndexonlyscan.h"
|
#include "executor/nodeIndexonlyscan.h"
|
||||||
|
#include "executor/nodeSeqscan.h"
|
||||||
|
#include "executor/nodeSort.h"
|
||||||
#include "executor/tqueue.h"
|
#include "executor/tqueue.h"
|
||||||
#include "nodes/nodeFuncs.h"
|
#include "nodes/nodeFuncs.h"
|
||||||
#include "optimizer/planmain.h"
|
#include "optimizer/planmain.h"
|
||||||
@ -202,10 +203,10 @@ ExecSerializePlan(Plan *plan, EState *estate)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Ordinary plan nodes won't do anything here, but parallel-aware plan nodes
|
* Parallel-aware plan nodes (and occasionally others) may need some state
|
||||||
* may need some state which is shared across all parallel workers. Before
|
* which is shared across all parallel workers. Before we size the DSM, give
|
||||||
* we size the DSM, give them a chance to call shm_toc_estimate_chunk or
|
* them a chance to call shm_toc_estimate_chunk or shm_toc_estimate_keys on
|
||||||
* shm_toc_estimate_keys on &pcxt->estimator.
|
* &pcxt->estimator.
|
||||||
*
|
*
|
||||||
* While we're at it, count the number of PlanState nodes in the tree, so
|
* While we're at it, count the number of PlanState nodes in the tree, so
|
||||||
* we know how many SharedPlanStateInstrumentation structures we need.
|
* we know how many SharedPlanStateInstrumentation structures we need.
|
||||||
@ -219,38 +220,43 @@ ExecParallelEstimate(PlanState *planstate, ExecParallelEstimateContext *e)
|
|||||||
/* Count this node. */
|
/* Count this node. */
|
||||||
e->nnodes++;
|
e->nnodes++;
|
||||||
|
|
||||||
/* Call estimators for parallel-aware nodes. */
|
switch (nodeTag(planstate))
|
||||||
if (planstate->plan->parallel_aware)
|
|
||||||
{
|
{
|
||||||
switch (nodeTag(planstate))
|
case T_SeqScanState:
|
||||||
{
|
if (planstate->plan->parallel_aware)
|
||||||
case T_SeqScanState:
|
|
||||||
ExecSeqScanEstimate((SeqScanState *) planstate,
|
ExecSeqScanEstimate((SeqScanState *) planstate,
|
||||||
e->pcxt);
|
e->pcxt);
|
||||||
break;
|
break;
|
||||||
case T_IndexScanState:
|
case T_IndexScanState:
|
||||||
|
if (planstate->plan->parallel_aware)
|
||||||
ExecIndexScanEstimate((IndexScanState *) planstate,
|
ExecIndexScanEstimate((IndexScanState *) planstate,
|
||||||
e->pcxt);
|
e->pcxt);
|
||||||
break;
|
break;
|
||||||
case T_IndexOnlyScanState:
|
case T_IndexOnlyScanState:
|
||||||
|
if (planstate->plan->parallel_aware)
|
||||||
ExecIndexOnlyScanEstimate((IndexOnlyScanState *) planstate,
|
ExecIndexOnlyScanEstimate((IndexOnlyScanState *) planstate,
|
||||||
e->pcxt);
|
e->pcxt);
|
||||||
break;
|
break;
|
||||||
case T_ForeignScanState:
|
case T_ForeignScanState:
|
||||||
|
if (planstate->plan->parallel_aware)
|
||||||
ExecForeignScanEstimate((ForeignScanState *) planstate,
|
ExecForeignScanEstimate((ForeignScanState *) planstate,
|
||||||
e->pcxt);
|
e->pcxt);
|
||||||
break;
|
break;
|
||||||
case T_CustomScanState:
|
case T_CustomScanState:
|
||||||
|
if (planstate->plan->parallel_aware)
|
||||||
ExecCustomScanEstimate((CustomScanState *) planstate,
|
ExecCustomScanEstimate((CustomScanState *) planstate,
|
||||||
e->pcxt);
|
e->pcxt);
|
||||||
break;
|
break;
|
||||||
case T_BitmapHeapScanState:
|
case T_BitmapHeapScanState:
|
||||||
|
if (planstate->plan->parallel_aware)
|
||||||
ExecBitmapHeapEstimate((BitmapHeapScanState *) planstate,
|
ExecBitmapHeapEstimate((BitmapHeapScanState *) planstate,
|
||||||
e->pcxt);
|
e->pcxt);
|
||||||
break;
|
break;
|
||||||
default:
|
case T_SortState:
|
||||||
break;
|
/* even when not parallel-aware */
|
||||||
}
|
ExecSortEstimate((SortState *) planstate, e->pcxt);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return planstate_tree_walker(planstate, ExecParallelEstimate, e);
|
return planstate_tree_walker(planstate, ExecParallelEstimate, e);
|
||||||
@ -276,46 +282,51 @@ ExecParallelInitializeDSM(PlanState *planstate,
|
|||||||
d->nnodes++;
|
d->nnodes++;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Call initializers for parallel-aware plan nodes.
|
* Call initializers for DSM-using plan nodes.
|
||||||
*
|
*
|
||||||
* Ordinary plan nodes won't do anything here, but parallel-aware plan
|
* Most plan nodes won't do anything here, but plan nodes that allocated
|
||||||
* nodes may need to initialize shared state in the DSM before parallel
|
* DSM may need to initialize shared state in the DSM before parallel
|
||||||
* workers are available. They can allocate the space they previously
|
* workers are launched. They can allocate the space they previously
|
||||||
* estimated using shm_toc_allocate, and add the keys they previously
|
* estimated using shm_toc_allocate, and add the keys they previously
|
||||||
* estimated using shm_toc_insert, in each case targeting pcxt->toc.
|
* estimated using shm_toc_insert, in each case targeting pcxt->toc.
|
||||||
*/
|
*/
|
||||||
if (planstate->plan->parallel_aware)
|
switch (nodeTag(planstate))
|
||||||
{
|
{
|
||||||
switch (nodeTag(planstate))
|
case T_SeqScanState:
|
||||||
{
|
if (planstate->plan->parallel_aware)
|
||||||
case T_SeqScanState:
|
|
||||||
ExecSeqScanInitializeDSM((SeqScanState *) planstate,
|
ExecSeqScanInitializeDSM((SeqScanState *) planstate,
|
||||||
d->pcxt);
|
d->pcxt);
|
||||||
break;
|
break;
|
||||||
case T_IndexScanState:
|
case T_IndexScanState:
|
||||||
|
if (planstate->plan->parallel_aware)
|
||||||
ExecIndexScanInitializeDSM((IndexScanState *) planstate,
|
ExecIndexScanInitializeDSM((IndexScanState *) planstate,
|
||||||
d->pcxt);
|
d->pcxt);
|
||||||
break;
|
break;
|
||||||
case T_IndexOnlyScanState:
|
case T_IndexOnlyScanState:
|
||||||
|
if (planstate->plan->parallel_aware)
|
||||||
ExecIndexOnlyScanInitializeDSM((IndexOnlyScanState *) planstate,
|
ExecIndexOnlyScanInitializeDSM((IndexOnlyScanState *) planstate,
|
||||||
d->pcxt);
|
d->pcxt);
|
||||||
break;
|
break;
|
||||||
case T_ForeignScanState:
|
case T_ForeignScanState:
|
||||||
|
if (planstate->plan->parallel_aware)
|
||||||
ExecForeignScanInitializeDSM((ForeignScanState *) planstate,
|
ExecForeignScanInitializeDSM((ForeignScanState *) planstate,
|
||||||
d->pcxt);
|
d->pcxt);
|
||||||
break;
|
break;
|
||||||
case T_CustomScanState:
|
case T_CustomScanState:
|
||||||
|
if (planstate->plan->parallel_aware)
|
||||||
ExecCustomScanInitializeDSM((CustomScanState *) planstate,
|
ExecCustomScanInitializeDSM((CustomScanState *) planstate,
|
||||||
d->pcxt);
|
d->pcxt);
|
||||||
break;
|
break;
|
||||||
case T_BitmapHeapScanState:
|
case T_BitmapHeapScanState:
|
||||||
|
if (planstate->plan->parallel_aware)
|
||||||
ExecBitmapHeapInitializeDSM((BitmapHeapScanState *) planstate,
|
ExecBitmapHeapInitializeDSM((BitmapHeapScanState *) planstate,
|
||||||
d->pcxt);
|
d->pcxt);
|
||||||
break;
|
break;
|
||||||
|
case T_SortState:
|
||||||
default:
|
/* even when not parallel-aware */
|
||||||
break;
|
ExecSortInitializeDSM((SortState *) planstate, d->pcxt);
|
||||||
}
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return planstate_tree_walker(planstate, ExecParallelInitializeDSM, d);
|
return planstate_tree_walker(planstate, ExecParallelInitializeDSM, d);
|
||||||
@ -642,6 +653,13 @@ ExecParallelRetrieveInstrumentation(PlanState *planstate,
|
|||||||
planstate->worker_instrument->num_workers = instrumentation->num_workers;
|
planstate->worker_instrument->num_workers = instrumentation->num_workers;
|
||||||
memcpy(&planstate->worker_instrument->instrument, instrument, ibytes);
|
memcpy(&planstate->worker_instrument->instrument, instrument, ibytes);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Perform any node-type-specific work that needs to be done. Currently,
|
||||||
|
* only Sort nodes need to do anything here.
|
||||||
|
*/
|
||||||
|
if (IsA(planstate, SortState))
|
||||||
|
ExecSortRetrieveInstrumentation((SortState *) planstate);
|
||||||
|
|
||||||
return planstate_tree_walker(planstate, ExecParallelRetrieveInstrumentation,
|
return planstate_tree_walker(planstate, ExecParallelRetrieveInstrumentation,
|
||||||
instrumentation);
|
instrumentation);
|
||||||
}
|
}
|
||||||
@ -801,35 +819,40 @@ ExecParallelInitializeWorker(PlanState *planstate, shm_toc *toc)
|
|||||||
if (planstate == NULL)
|
if (planstate == NULL)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/* Call initializers for parallel-aware plan nodes. */
|
switch (nodeTag(planstate))
|
||||||
if (planstate->plan->parallel_aware)
|
|
||||||
{
|
{
|
||||||
switch (nodeTag(planstate))
|
case T_SeqScanState:
|
||||||
{
|
if (planstate->plan->parallel_aware)
|
||||||
case T_SeqScanState:
|
|
||||||
ExecSeqScanInitializeWorker((SeqScanState *) planstate, toc);
|
ExecSeqScanInitializeWorker((SeqScanState *) planstate, toc);
|
||||||
break;
|
break;
|
||||||
case T_IndexScanState:
|
case T_IndexScanState:
|
||||||
|
if (planstate->plan->parallel_aware)
|
||||||
ExecIndexScanInitializeWorker((IndexScanState *) planstate, toc);
|
ExecIndexScanInitializeWorker((IndexScanState *) planstate, toc);
|
||||||
break;
|
break;
|
||||||
case T_IndexOnlyScanState:
|
case T_IndexOnlyScanState:
|
||||||
|
if (planstate->plan->parallel_aware)
|
||||||
ExecIndexOnlyScanInitializeWorker((IndexOnlyScanState *) planstate, toc);
|
ExecIndexOnlyScanInitializeWorker((IndexOnlyScanState *) planstate, toc);
|
||||||
break;
|
break;
|
||||||
case T_ForeignScanState:
|
case T_ForeignScanState:
|
||||||
|
if (planstate->plan->parallel_aware)
|
||||||
ExecForeignScanInitializeWorker((ForeignScanState *) planstate,
|
ExecForeignScanInitializeWorker((ForeignScanState *) planstate,
|
||||||
toc);
|
toc);
|
||||||
break;
|
break;
|
||||||
case T_CustomScanState:
|
case T_CustomScanState:
|
||||||
|
if (planstate->plan->parallel_aware)
|
||||||
ExecCustomScanInitializeWorker((CustomScanState *) planstate,
|
ExecCustomScanInitializeWorker((CustomScanState *) planstate,
|
||||||
toc);
|
toc);
|
||||||
break;
|
break;
|
||||||
case T_BitmapHeapScanState:
|
case T_BitmapHeapScanState:
|
||||||
|
if (planstate->plan->parallel_aware)
|
||||||
ExecBitmapHeapInitializeWorker(
|
ExecBitmapHeapInitializeWorker(
|
||||||
(BitmapHeapScanState *) planstate, toc);
|
(BitmapHeapScanState *) planstate, toc);
|
||||||
break;
|
break;
|
||||||
default:
|
case T_SortState:
|
||||||
break;
|
/* even when not parallel-aware */
|
||||||
}
|
ExecSortInitializeWorker((SortState *) planstate, toc);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return planstate_tree_walker(planstate, ExecParallelInitializeWorker, toc);
|
return planstate_tree_walker(planstate, ExecParallelInitializeWorker, toc);
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "access/parallel.h"
|
||||||
#include "executor/execdebug.h"
|
#include "executor/execdebug.h"
|
||||||
#include "executor/nodeSort.h"
|
#include "executor/nodeSort.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
@ -127,6 +128,15 @@ ExecSort(PlanState *pstate)
|
|||||||
node->sort_Done = true;
|
node->sort_Done = true;
|
||||||
node->bounded_Done = node->bounded;
|
node->bounded_Done = node->bounded;
|
||||||
node->bound_Done = node->bound;
|
node->bound_Done = node->bound;
|
||||||
|
if (node->shared_info && node->am_worker)
|
||||||
|
{
|
||||||
|
TuplesortInstrumentation *si;
|
||||||
|
|
||||||
|
Assert(IsParallelWorker());
|
||||||
|
Assert(ParallelWorkerNumber <= node->shared_info->num_workers);
|
||||||
|
si = &node->shared_info->sinstrument[ParallelWorkerNumber];
|
||||||
|
tuplesort_get_stats(tuplesortstate, si);
|
||||||
|
}
|
||||||
SO1_printf("ExecSort: %s\n", "sorting done");
|
SO1_printf("ExecSort: %s\n", "sorting done");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -334,3 +344,90 @@ ExecReScanSort(SortState *node)
|
|||||||
else
|
else
|
||||||
tuplesort_rescan((Tuplesortstate *) node->tuplesortstate);
|
tuplesort_rescan((Tuplesortstate *) node->tuplesortstate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------
|
||||||
|
* Parallel Query Support
|
||||||
|
* ----------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------
|
||||||
|
* ExecSortEstimate
|
||||||
|
*
|
||||||
|
* Estimate space required to propagate sort statistics.
|
||||||
|
* ----------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ExecSortEstimate(SortState *node, ParallelContext *pcxt)
|
||||||
|
{
|
||||||
|
Size size;
|
||||||
|
|
||||||
|
/* don't need this if not instrumenting or no workers */
|
||||||
|
if (!node->ss.ps.instrument || pcxt->nworkers == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
size = mul_size(pcxt->nworkers, sizeof(TuplesortInstrumentation));
|
||||||
|
size = add_size(size, offsetof(SharedSortInfo, sinstrument));
|
||||||
|
shm_toc_estimate_chunk(&pcxt->estimator, size);
|
||||||
|
shm_toc_estimate_keys(&pcxt->estimator, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------
|
||||||
|
* ExecSortInitializeDSM
|
||||||
|
*
|
||||||
|
* Initialize DSM space for sort statistics.
|
||||||
|
* ----------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ExecSortInitializeDSM(SortState *node, ParallelContext *pcxt)
|
||||||
|
{
|
||||||
|
Size size;
|
||||||
|
|
||||||
|
/* don't need this if not instrumenting or no workers */
|
||||||
|
if (!node->ss.ps.instrument || pcxt->nworkers == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
size = offsetof(SharedSortInfo, sinstrument)
|
||||||
|
+ pcxt->nworkers * sizeof(TuplesortInstrumentation);
|
||||||
|
node->shared_info = shm_toc_allocate(pcxt->toc, size);
|
||||||
|
/* ensure any unfilled slots will contain zeroes */
|
||||||
|
memset(node->shared_info, 0, size);
|
||||||
|
node->shared_info->num_workers = pcxt->nworkers;
|
||||||
|
shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id,
|
||||||
|
node->shared_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------
|
||||||
|
* ExecSortInitializeWorker
|
||||||
|
*
|
||||||
|
* Attach worker to DSM space for sort statistics.
|
||||||
|
* ----------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ExecSortInitializeWorker(SortState *node, shm_toc *toc)
|
||||||
|
{
|
||||||
|
node->shared_info =
|
||||||
|
shm_toc_lookup(toc, node->ss.ps.plan->plan_node_id, true);
|
||||||
|
node->am_worker = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------
|
||||||
|
* ExecSortRetrieveInstrumentation
|
||||||
|
*
|
||||||
|
* Transfer sort statistics from DSM to private memory.
|
||||||
|
* ----------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ExecSortRetrieveInstrumentation(SortState *node)
|
||||||
|
{
|
||||||
|
Size size;
|
||||||
|
SharedSortInfo *si;
|
||||||
|
|
||||||
|
if (node->shared_info == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
size = offsetof(SharedSortInfo, sinstrument)
|
||||||
|
+ node->shared_info->num_workers * sizeof(TuplesortInstrumentation);
|
||||||
|
si = palloc(size);
|
||||||
|
memcpy(si, node->shared_info, size);
|
||||||
|
node->shared_info = si;
|
||||||
|
}
|
||||||
|
@ -3227,13 +3227,10 @@ tuplesort_restorepos(Tuplesortstate *state)
|
|||||||
*
|
*
|
||||||
* This can be called after tuplesort_performsort() finishes to obtain
|
* This can be called after tuplesort_performsort() finishes to obtain
|
||||||
* printable summary information about how the sort was performed.
|
* printable summary information about how the sort was performed.
|
||||||
* spaceUsed is measured in kilobytes.
|
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
tuplesort_get_stats(Tuplesortstate *state,
|
tuplesort_get_stats(Tuplesortstate *state,
|
||||||
const char **sortMethod,
|
TuplesortInstrumentation *stats)
|
||||||
const char **spaceType,
|
|
||||||
long *spaceUsed)
|
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Note: it might seem we should provide both memory and disk usage for a
|
* Note: it might seem we should provide both memory and disk usage for a
|
||||||
@ -3246,35 +3243,68 @@ tuplesort_get_stats(Tuplesortstate *state,
|
|||||||
*/
|
*/
|
||||||
if (state->tapeset)
|
if (state->tapeset)
|
||||||
{
|
{
|
||||||
*spaceType = "Disk";
|
stats->spaceType = SORT_SPACE_TYPE_DISK;
|
||||||
*spaceUsed = LogicalTapeSetBlocks(state->tapeset) * (BLCKSZ / 1024);
|
stats->spaceUsed = LogicalTapeSetBlocks(state->tapeset) * (BLCKSZ / 1024);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
*spaceType = "Memory";
|
stats->spaceType = SORT_SPACE_TYPE_MEMORY;
|
||||||
*spaceUsed = (state->allowedMem - state->availMem + 1023) / 1024;
|
stats->spaceUsed = (state->allowedMem - state->availMem + 1023) / 1024;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (state->status)
|
switch (state->status)
|
||||||
{
|
{
|
||||||
case TSS_SORTEDINMEM:
|
case TSS_SORTEDINMEM:
|
||||||
if (state->boundUsed)
|
if (state->boundUsed)
|
||||||
*sortMethod = "top-N heapsort";
|
stats->sortMethod = SORT_TYPE_TOP_N_HEAPSORT;
|
||||||
else
|
else
|
||||||
*sortMethod = "quicksort";
|
stats->sortMethod = SORT_TYPE_QUICKSORT;
|
||||||
break;
|
break;
|
||||||
case TSS_SORTEDONTAPE:
|
case TSS_SORTEDONTAPE:
|
||||||
*sortMethod = "external sort";
|
stats->sortMethod = SORT_TYPE_EXTERNAL_SORT;
|
||||||
break;
|
break;
|
||||||
case TSS_FINALMERGE:
|
case TSS_FINALMERGE:
|
||||||
*sortMethod = "external merge";
|
stats->sortMethod = SORT_TYPE_EXTERNAL_MERGE;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
*sortMethod = "still in progress";
|
stats->sortMethod = SORT_TYPE_STILL_IN_PROGRESS;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert TuplesortMethod to a string.
|
||||||
|
*/
|
||||||
|
const char *
|
||||||
|
tuplesort_method_name(TuplesortMethod m)
|
||||||
|
{
|
||||||
|
switch (m)
|
||||||
|
{
|
||||||
|
case SORT_TYPE_STILL_IN_PROGRESS:
|
||||||
|
return "still in progress";
|
||||||
|
case SORT_TYPE_TOP_N_HEAPSORT:
|
||||||
|
return "top-N heapsort";
|
||||||
|
case SORT_TYPE_QUICKSORT:
|
||||||
|
return "quicksort";
|
||||||
|
case SORT_TYPE_EXTERNAL_SORT:
|
||||||
|
return "external sort";
|
||||||
|
case SORT_TYPE_EXTERNAL_MERGE:
|
||||||
|
return "external merge";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert TuplesortSpaceType to a string.
|
||||||
|
*/
|
||||||
|
const char *
|
||||||
|
tuplesort_space_type_name(TuplesortSpaceType t)
|
||||||
|
{
|
||||||
|
Assert(t == SORT_SPACE_TYPE_DISK || t == SORT_SPACE_TYPE_MEMORY);
|
||||||
|
return t == SORT_SPACE_TYPE_DISK ? "Disk" : "Memory";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Heap manipulation routines, per Knuth's Algorithm 5.2.3H.
|
* Heap manipulation routines, per Knuth's Algorithm 5.2.3H.
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#ifndef NODESORT_H
|
#ifndef NODESORT_H
|
||||||
#define NODESORT_H
|
#define NODESORT_H
|
||||||
|
|
||||||
|
#include "access/parallel.h"
|
||||||
#include "nodes/execnodes.h"
|
#include "nodes/execnodes.h"
|
||||||
|
|
||||||
extern SortState *ExecInitSort(Sort *node, EState *estate, int eflags);
|
extern SortState *ExecInitSort(Sort *node, EState *estate, int eflags);
|
||||||
@ -22,4 +23,10 @@ extern void ExecSortMarkPos(SortState *node);
|
|||||||
extern void ExecSortRestrPos(SortState *node);
|
extern void ExecSortRestrPos(SortState *node);
|
||||||
extern void ExecReScanSort(SortState *node);
|
extern void ExecReScanSort(SortState *node);
|
||||||
|
|
||||||
|
/* parallel instrumentation support */
|
||||||
|
extern void ExecSortEstimate(SortState *node, ParallelContext *pcxt);
|
||||||
|
extern void ExecSortInitializeDSM(SortState *node, ParallelContext *pcxt);
|
||||||
|
extern void ExecSortInitializeWorker(SortState *node, shm_toc *toc);
|
||||||
|
extern void ExecSortRetrieveInstrumentation(SortState *node);
|
||||||
|
|
||||||
#endif /* NODESORT_H */
|
#endif /* NODESORT_H */
|
||||||
|
@ -1730,6 +1730,16 @@ typedef struct MaterialState
|
|||||||
Tuplestorestate *tuplestorestate;
|
Tuplestorestate *tuplestorestate;
|
||||||
} MaterialState;
|
} MaterialState;
|
||||||
|
|
||||||
|
/* ----------------
|
||||||
|
* Shared memory container for per-worker sort information
|
||||||
|
* ----------------
|
||||||
|
*/
|
||||||
|
typedef struct SharedSortInfo
|
||||||
|
{
|
||||||
|
int num_workers;
|
||||||
|
TuplesortInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER];
|
||||||
|
} SharedSortInfo;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* SortState information
|
* SortState information
|
||||||
* ----------------
|
* ----------------
|
||||||
@ -1744,6 +1754,8 @@ typedef struct SortState
|
|||||||
bool bounded_Done; /* value of bounded we did the sort with */
|
bool bounded_Done; /* value of bounded we did the sort with */
|
||||||
int64 bound_Done; /* value of bound we did the sort with */
|
int64 bound_Done; /* value of bound we did the sort with */
|
||||||
void *tuplesortstate; /* private state of tuplesort.c */
|
void *tuplesortstate; /* private state of tuplesort.c */
|
||||||
|
bool am_worker; /* are we a worker? */
|
||||||
|
SharedSortInfo *shared_info; /* one entry per worker */
|
||||||
} SortState;
|
} SortState;
|
||||||
|
|
||||||
/* ---------------------
|
/* ---------------------
|
||||||
|
@ -31,6 +31,34 @@
|
|||||||
*/
|
*/
|
||||||
typedef struct Tuplesortstate Tuplesortstate;
|
typedef struct Tuplesortstate Tuplesortstate;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Data structures for reporting sort statistics. Note that
|
||||||
|
* TuplesortInstrumentation can't contain any pointers because we
|
||||||
|
* sometimes put it in shared memory.
|
||||||
|
*/
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
SORT_TYPE_STILL_IN_PROGRESS = 0,
|
||||||
|
SORT_TYPE_TOP_N_HEAPSORT,
|
||||||
|
SORT_TYPE_QUICKSORT,
|
||||||
|
SORT_TYPE_EXTERNAL_SORT,
|
||||||
|
SORT_TYPE_EXTERNAL_MERGE
|
||||||
|
} TuplesortMethod;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
SORT_SPACE_TYPE_DISK,
|
||||||
|
SORT_SPACE_TYPE_MEMORY
|
||||||
|
} TuplesortSpaceType;
|
||||||
|
|
||||||
|
typedef struct TuplesortInstrumentation
|
||||||
|
{
|
||||||
|
TuplesortMethod sortMethod; /* sort algorithm used */
|
||||||
|
TuplesortSpaceType spaceType; /* type of space spaceUsed represents */
|
||||||
|
long spaceUsed; /* space consumption, in kB */
|
||||||
|
} TuplesortInstrumentation;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We provide multiple interfaces to what is essentially the same code,
|
* We provide multiple interfaces to what is essentially the same code,
|
||||||
* since different callers have different data to be sorted and want to
|
* since different callers have different data to be sorted and want to
|
||||||
@ -107,9 +135,9 @@ extern bool tuplesort_skiptuples(Tuplesortstate *state, int64 ntuples,
|
|||||||
extern void tuplesort_end(Tuplesortstate *state);
|
extern void tuplesort_end(Tuplesortstate *state);
|
||||||
|
|
||||||
extern void tuplesort_get_stats(Tuplesortstate *state,
|
extern void tuplesort_get_stats(Tuplesortstate *state,
|
||||||
const char **sortMethod,
|
TuplesortInstrumentation *stats);
|
||||||
const char **spaceType,
|
extern const char *tuplesort_method_name(TuplesortMethod m);
|
||||||
long *spaceUsed);
|
extern const char *tuplesort_space_type_name(TuplesortSpaceType t);
|
||||||
|
|
||||||
extern int tuplesort_merge_order(int64 allowedMem);
|
extern int tuplesort_merge_order(int64 allowedMem);
|
||||||
|
|
||||||
|
@ -1047,7 +1047,7 @@ drop function tattle(x int, y int);
|
|||||||
-- ANALYZE shows that a top-N sort was used. We must suppress or filter away
|
-- ANALYZE shows that a top-N sort was used. We must suppress or filter away
|
||||||
-- all the non-invariant parts of the EXPLAIN ANALYZE output.
|
-- all the non-invariant parts of the EXPLAIN ANALYZE output.
|
||||||
--
|
--
|
||||||
create temp table sq_limit (pk int primary key, c1 int, c2 int);
|
create table sq_limit (pk int primary key, c1 int, c2 int);
|
||||||
insert into sq_limit values
|
insert into sq_limit values
|
||||||
(1, 1, 1),
|
(1, 1, 1),
|
||||||
(2, 2, 2),
|
(2, 2, 2),
|
||||||
@ -1066,6 +1066,8 @@ begin
|
|||||||
select * from (select pk,c2 from sq_limit order by c1,pk) as x limit 3
|
select * from (select pk,c2 from sq_limit order by c1,pk) as x limit 3
|
||||||
loop
|
loop
|
||||||
ln := regexp_replace(ln, 'Memory: \S*', 'Memory: xxx');
|
ln := regexp_replace(ln, 'Memory: \S*', 'Memory: xxx');
|
||||||
|
-- this case might occur if force_parallel_mode is on:
|
||||||
|
ln := regexp_replace(ln, 'Worker 0: Sort Method', 'Sort Method');
|
||||||
return next ln;
|
return next ln;
|
||||||
end loop;
|
end loop;
|
||||||
end;
|
end;
|
||||||
@ -1090,3 +1092,4 @@ select * from (select pk,c2 from sq_limit order by c1,pk) as x limit 3;
|
|||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
drop function explain_sq_limit();
|
drop function explain_sq_limit();
|
||||||
|
drop table sq_limit;
|
||||||
|
@ -547,7 +547,7 @@ drop function tattle(x int, y int);
|
|||||||
-- ANALYZE shows that a top-N sort was used. We must suppress or filter away
|
-- ANALYZE shows that a top-N sort was used. We must suppress or filter away
|
||||||
-- all the non-invariant parts of the EXPLAIN ANALYZE output.
|
-- all the non-invariant parts of the EXPLAIN ANALYZE output.
|
||||||
--
|
--
|
||||||
create temp table sq_limit (pk int primary key, c1 int, c2 int);
|
create table sq_limit (pk int primary key, c1 int, c2 int);
|
||||||
insert into sq_limit values
|
insert into sq_limit values
|
||||||
(1, 1, 1),
|
(1, 1, 1),
|
||||||
(2, 2, 2),
|
(2, 2, 2),
|
||||||
@ -567,6 +567,8 @@ begin
|
|||||||
select * from (select pk,c2 from sq_limit order by c1,pk) as x limit 3
|
select * from (select pk,c2 from sq_limit order by c1,pk) as x limit 3
|
||||||
loop
|
loop
|
||||||
ln := regexp_replace(ln, 'Memory: \S*', 'Memory: xxx');
|
ln := regexp_replace(ln, 'Memory: \S*', 'Memory: xxx');
|
||||||
|
-- this case might occur if force_parallel_mode is on:
|
||||||
|
ln := regexp_replace(ln, 'Worker 0: Sort Method', 'Sort Method');
|
||||||
return next ln;
|
return next ln;
|
||||||
end loop;
|
end loop;
|
||||||
end;
|
end;
|
||||||
@ -577,3 +579,5 @@ select * from explain_sq_limit();
|
|||||||
select * from (select pk,c2 from sq_limit order by c1,pk) as x limit 3;
|
select * from (select pk,c2 from sq_limit order by c1,pk) as x limit 3;
|
||||||
|
|
||||||
drop function explain_sq_limit();
|
drop function explain_sq_limit();
|
||||||
|
|
||||||
|
drop table sq_limit;
|
||||||
|
Reference in New Issue
Block a user