diff options
| author | David Rowley | 2024-07-05 02:05:08 +0000 |
|---|---|---|
| committer | David Rowley | 2024-07-05 02:05:08 +0000 |
| commit | 1eff8279d494b96f0073df78abc74954a2f6ee54 (patch) | |
| tree | ad0a2c26cc564a821be0a3ade446b3bfd861c604 /src/backend | |
| parent | aa86129e19d704afb93cb84ab9638f33d266ee9d (diff) | |
Add memory/disk usage for Material nodes in EXPLAIN
Up until now, there was no ability to easily determine if a Material
node caused the underlying tuplestore to spill to disk or even see how
much memory the tuplestore used if it didn't.
Here we add some new functions to tuplestore.c to query this information
and add some additional output in EXPLAIN ANALYZE to display this
information for the Material node.
There are a few other executor node types that use tuplestores, so we
could also consider adding these details to the EXPLAIN ANALYZE for
those nodes too. Let's consider those independently from this. Having
the tuplestore.c infrastructure in to allow that is step 1.
Author: David Rowley
Reviewed-by: Matthias van de Meent, Dmitry Dolgov
Discussion: https://postgr.es/m/CAApHDvp5Py9g4Rjq7_inL3-MCK1Co2CRt_YWFwTU2zfQix0p4A@mail.gmail.com
Diffstat (limited to 'src/backend')
| -rw-r--r-- | src/backend/commands/explain.c | 37 | ||||
| -rw-r--r-- | src/backend/utils/sort/tuplestore.c | 53 |
2 files changed, 90 insertions, 0 deletions
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 30de9de9d4f..1b5ab503898 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -125,6 +125,7 @@ static void show_sort_info(SortState *sortstate, ExplainState *es); static void show_incremental_sort_info(IncrementalSortState *incrsortstate, ExplainState *es); static void show_hash_info(HashState *hashstate, ExplainState *es); +static void show_material_info(MaterialState *mstate, ExplainState *es); static void show_memoize_info(MemoizeState *mstate, List *ancestors, ExplainState *es); static void show_hashagg_info(AggState *aggstate, ExplainState *es); @@ -2251,6 +2252,9 @@ ExplainNode(PlanState *planstate, List *ancestors, case T_Hash: show_hash_info(castNode(HashState, planstate), es); break; + case T_Material: + show_material_info(castNode(MaterialState, planstate), es); + break; case T_Memoize: show_memoize_info(castNode(MemoizeState, planstate), ancestors, es); @@ -3323,6 +3327,39 @@ show_hash_info(HashState *hashstate, ExplainState *es) } /* + * Show information on material node, storage method and maximum memory/disk + * space used. + */ +static void +show_material_info(MaterialState *mstate, ExplainState *es) +{ + Tuplestorestate *tupstore; + const char *storageType; + int64 spaceUsedKB; + + if (!es->analyze) + return; + + tupstore = mstate->tuplestorestate; + storageType = tuplestore_storage_type_name(tupstore); + spaceUsedKB = BYTES_TO_KILOBYTES(tuplestore_space_used(tupstore)); + + if (es->format != EXPLAIN_FORMAT_TEXT) + { + ExplainPropertyText("Storage", storageType, es); + ExplainPropertyInteger("Maximum Storage", "kB", spaceUsedKB, es); + } + else + { + ExplainIndentText(es); + appendStringInfo(es->str, + "Storage: %s Maximum Storage: " INT64_FORMAT "kB\n", + storageType, + spaceUsedKB); + } +} + +/* * Show information on memoize hits/misses/evictions and memory usage. */ static void diff --git a/src/backend/utils/sort/tuplestore.c b/src/backend/utils/sort/tuplestore.c index 947a868e569..24bb49ca874 100644 --- a/src/backend/utils/sort/tuplestore.c +++ b/src/backend/utils/sort/tuplestore.c @@ -109,6 +109,7 @@ struct Tuplestorestate bool truncated; /* tuplestore_trim has removed tuples? */ int64 availMem; /* remaining memory available, in bytes */ int64 allowedMem; /* total memory allowed, in bytes */ + int64 maxSpace; /* maximum space used in memory */ int64 tuples; /* number of tuples added */ BufFile *myfile; /* underlying file, or NULL if none */ MemoryContext context; /* memory context for holding tuples */ @@ -238,6 +239,7 @@ static Tuplestorestate *tuplestore_begin_common(int eflags, int maxKBytes); static void tuplestore_puttuple_common(Tuplestorestate *state, void *tuple); static void dumptuples(Tuplestorestate *state); +static void tuplestore_updatemax(Tuplestorestate *state); static unsigned int getlen(Tuplestorestate *state, bool eofOK); static void *copytup_heap(Tuplestorestate *state, void *tup); static void writetup_heap(Tuplestorestate *state, void *tup); @@ -262,6 +264,7 @@ tuplestore_begin_common(int eflags, bool interXact, int maxKBytes) state->truncated = false; state->allowedMem = maxKBytes * 1024L; state->availMem = state->allowedMem; + state->maxSpace = 0; state->myfile = NULL; state->context = CurrentMemoryContext; state->resowner = CurrentResourceOwner; @@ -420,6 +423,9 @@ tuplestore_clear(Tuplestorestate *state) int i; TSReadPointer *readptr; + /* update the maxSpace before doing any USEMEM/FREEMEM adjustments */ + tuplestore_updatemax(state); + if (state->myfile) BufFileClose(state->myfile); state->myfile = NULL; @@ -1402,6 +1408,9 @@ tuplestore_trim(Tuplestorestate *state) Assert(nremove >= state->memtupdeleted); Assert(nremove <= state->memtupcount); + /* before freeing any memory, update maxSpace */ + tuplestore_updatemax(state); + /* Release no-longer-needed tuples */ for (i = state->memtupdeleted; i < nremove; i++) { @@ -1445,6 +1454,49 @@ tuplestore_trim(Tuplestorestate *state) } /* + * tuplestore_updatemax + * Update maxSpace field + */ +static void +tuplestore_updatemax(Tuplestorestate *state) +{ + if (state->status == TSS_INMEM) + state->maxSpace = Max(state->maxSpace, + state->allowedMem - state->availMem); +} + +/* + * tuplestore_storage_type_name + * Return a string description of the storage method used to store the + * tuples. + */ +const char * +tuplestore_storage_type_name(Tuplestorestate *state) +{ + if (state->status == TSS_INMEM) + return "Memory"; + else + return "Disk"; +} + +/* + * tuplestore_space_used + * Return the maximum space used in memory unless the tuplestore has spilled + * to disk, in which case, return the disk space used. + */ +int64 +tuplestore_space_used(Tuplestorestate *state) +{ + /* First, update the maxSpace field */ + tuplestore_updatemax(state); + + if (state->status == TSS_INMEM) + return state->maxSpace; + else + return BufFileSize(state->myfile); +} + +/* * tuplestore_in_memory * * Returns true if the tuplestore has not spilled to disk. @@ -1513,6 +1565,7 @@ writetup_heap(Tuplestorestate *state, void *tup) if (state->backward) /* need trailing length word? */ BufFileWrite(state->myfile, &tuplen, sizeof(tuplen)); + /* no need to call tuplestore_updatemax() when not in TSS_INMEM */ FREEMEM(state, GetMemoryChunkSpace(tuple)); heap_free_minimal_tuple(tuple); } |
