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);
 }

Reply via email to