On Fri, 6 Sept 2024 at 16:21, Tatsuo Ishii <is...@postgresql.org> wrote: > However, for 10, 2, 1 partitions. I see large performance > degradation with the patched version: patched is slower than stock > master in 1.5% (10 partitions), 41% (2 partitions) and 55.7% (1 > partition). See the attached graph.
Thanks for making the adjustments to this. I don't think there is any need to call tuplestore_updatemax() from within writetup_heap(). That means having to update the maximum space used every time a tuple is written to disk. That's a fairly massive overhead. Instead, it should be fine to modify tuplestore_updatemax() to set a flag to true if state->status != TSS_INMEM and then record the disk space used. That flag won't ever be set to false again. tuplestore_storage_type_name() should just return "Disk" if the new disk flag is set, even if state->status == TSS_INMEM. Since the work_mem size won't change between tuplestore_clear() calls, if we've once spilt to disk, then we shouldn't care about the memory used for runs that didn't. Those will always have used less memory. I did this quickly, but playing around with the attached, I didn't see any slowdown. Here's the results I got on my Zen2 AMD machine: parts master yours mine mine_v_master 10000 5.01 5.12 5.09 99% 1000 4.30 4.25 4.24 101% 100 4.17 4.13 4.12 101% 10 4.16 4.12 4.10 101% 2 4.75 7.64 4.73 100% 1 4.75 8.57 4.73 100% David
diff --git a/src/backend/utils/sort/tuplestore.c b/src/backend/utils/sort/tuplestore.c index 444c8e25c2..03d745c609 100644 --- a/src/backend/utils/sort/tuplestore.c +++ b/src/backend/utils/sort/tuplestore.c @@ -107,9 +107,10 @@ struct Tuplestorestate bool backward; /* store extra length words in file? */ bool interXact; /* keep open through transactions? */ bool truncated; /* tuplestore_trim has removed tuples? */ + bool usedDisk; /* true if tuple store has ever gone to disk */ int64 availMem; /* remaining memory available, in bytes */ int64 allowedMem; /* total memory allowed, in bytes */ - int64 maxSpace; /* maximum space used in memory */ + int64 maxSpace; /* maximum space used in memory or disk */ int64 tuples; /* number of tuples added */ BufFile *myfile; /* underlying file, or NULL if none */ MemoryContext context; /* memory context for holding tuples */ @@ -262,6 +263,7 @@ tuplestore_begin_common(int eflags, bool interXact, int maxKBytes) state->eflags = eflags; state->interXact = interXact; state->truncated = false; + state->usedDisk = false; state->allowedMem = maxKBytes * 1024L; state->availMem = state->allowedMem; state->maxSpace = 0; @@ -1499,17 +1501,25 @@ tuplestore_updatemax(Tuplestorestate *state) if (state->status == TSS_INMEM) state->maxSpace = Max(state->maxSpace, state->allowedMem - state->availMem); + else + { + state->maxSpace = Max(state->maxSpace, + BufFileSize(state->myfile)); + state->usedDisk = true; + } } /* * tuplestore_storage_type_name * Return a string description of the storage method used to store the - * tuples. + * tuples. We don't use the current status as if the tuplestore went to + * disk and a tuplestore_clear() allowed it to switch back to memory + * again, we still want to know that it has spilled to disk. */ const char * tuplestore_storage_type_name(Tuplestorestate *state) { - if (state->status == TSS_INMEM) + if (!state->usedDisk) return "Memory"; else return "Disk"; @@ -1517,8 +1527,7 @@ tuplestore_storage_type_name(Tuplestorestate *state) /* * 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. + * Return the maximum space used in memory or disk. */ int64 tuplestore_space_used(Tuplestorestate *state) @@ -1526,10 +1535,7 @@ 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); + return state->maxSpace; } /* @@ -1601,7 +1607,6 @@ 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); }