tuplestore-trim-speed.patch
text/x-patch
Filename: tuplestore-trim-speed.patch
Type: text/x-patch
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
| File | + | − |
|---|---|---|
| src/backend/utils/sort/tuplestore.c | 0 | 0 |
diff --git a/src/backend/utils/sort/tuplestore.c b/src/backend/utils/sort/tuplestore.c
index 9bbaba43771f495fdf24e9f2afd545b69a22ecbd..8c8139c897679892e0d4ad13e69ae8d814484206 100644
*** a/src/backend/utils/sort/tuplestore.c
--- b/src/backend/utils/sort/tuplestore.c
*************** struct Tuplestorestate
*** 145,152 ****
--- 145,159 ----
/*
* This array holds pointers to tuples in memory if we are in state INMEM.
* In states WRITEFILE and READFILE it's not used.
+ *
+ * When memtupdeleted > 0, the first memtupdeleted pointers are already
+ * released due to a tuplestore_trim() operation, but we haven't expended
+ * the effort to slide the remaining pointers down. These unused pointers
+ * are set to NULL to catch any invalid accesses. Note that memtupcount
+ * includes the deleted pointers.
*/
void **memtuples; /* array of pointers to palloc'd tuples */
+ int memtupdeleted; /* the first N slots are currently unused */
int memtupcount; /* number of tuples currently present */
int memtupsize; /* allocated length of memtuples array */
*************** tuplestore_begin_common(int eflags, bool
*** 252,257 ****
--- 259,265 ----
state->context = CurrentMemoryContext;
state->resowner = CurrentResourceOwner;
+ state->memtupdeleted = 0;
state->memtupcount = 0;
state->memtupsize = 1024; /* initial guess */
state->memtuples = (void **) palloc(state->memtupsize * sizeof(void *));
*************** tuplestore_clear(Tuplestorestate *state)
*** 401,407 ****
state->myfile = NULL;
if (state->memtuples)
{
! for (i = 0; i < state->memtupcount; i++)
{
FREEMEM(state, GetMemoryChunkSpace(state->memtuples[i]));
pfree(state->memtuples[i]);
--- 409,415 ----
state->myfile = NULL;
if (state->memtuples)
{
! for (i = state->memtupdeleted; i < state->memtupcount; i++)
{
FREEMEM(state, GetMemoryChunkSpace(state->memtuples[i]));
pfree(state->memtuples[i]);
*************** tuplestore_clear(Tuplestorestate *state)
*** 409,414 ****
--- 417,423 ----
}
state->status = TSS_INMEM;
state->truncated = false;
+ state->memtupdeleted = 0;
state->memtupcount = 0;
readptr = state->readptrs;
for (i = 0; i < state->readptrcount; readptr++, i++)
*************** tuplestore_end(Tuplestorestate *state)
*** 432,438 ****
BufFileClose(state->myfile);
if (state->memtuples)
{
! for (i = 0; i < state->memtupcount; i++)
pfree(state->memtuples[i]);
pfree(state->memtuples);
}
--- 441,447 ----
BufFileClose(state->myfile);
if (state->memtuples)
{
! for (i = state->memtupdeleted; i < state->memtupcount; i++)
pfree(state->memtuples[i]);
pfree(state->memtuples);
}
*************** tuplestore_gettuple(Tuplestorestate *sta
*** 774,787 ****
}
else
{
! if (readptr->current <= 0)
{
Assert(!state->truncated);
return NULL;
}
readptr->current--; /* last returned tuple */
}
! if (readptr->current <= 0)
{
Assert(!state->truncated);
return NULL;
--- 783,796 ----
}
else
{
! if (readptr->current <= state->memtupdeleted)
{
Assert(!state->truncated);
return NULL;
}
readptr->current--; /* last returned tuple */
}
! if (readptr->current <= state->memtupdeleted)
{
Assert(!state->truncated);
return NULL;
*************** dumptuples(Tuplestorestate *state)
*** 969,975 ****
{
int i;
! for (i = 0;; i++)
{
TSReadPointer *readptr = state->readptrs;
int j;
--- 978,984 ----
{
int i;
! for (i = state->memtupdeleted;; i++)
{
TSReadPointer *readptr = state->readptrs;
int j;
*************** dumptuples(Tuplestorestate *state)
*** 984,989 ****
--- 993,999 ----
break;
WRITETUP(state, state->memtuples[i]);
}
+ state->memtupdeleted = 0;
state->memtupcount = 0;
}
*************** tuplestore_trim(Tuplestorestate *state)
*** 1153,1176 ****
nremove = oldest - 1;
if (nremove <= 0)
return; /* nothing to do */
Assert(nremove <= state->memtupcount);
/* Release no-longer-needed tuples */
! for (i = 0; i < nremove; i++)
{
FREEMEM(state, GetMemoryChunkSpace(state->memtuples[i]));
pfree(state->memtuples[i]);
}
/*
! * Slide the array down and readjust pointers. This may look pretty
! * stupid, but we expect that there will usually not be very many
! * tuple-pointers to move, so this isn't that expensive; and it keeps a
! * lot of other logic simple.
*
! * In fact, in the current usage for merge joins, it's demonstrable that
! * there will always be exactly one non-removed tuple; so optimize that
! * case.
*/
if (nremove + 1 == state->memtupcount)
state->memtuples[0] = state->memtuples[nremove];
--- 1163,1198 ----
nremove = oldest - 1;
if (nremove <= 0)
return; /* nothing to do */
+
+ Assert(nremove >= state->memtupdeleted);
Assert(nremove <= state->memtupcount);
/* Release no-longer-needed tuples */
! for (i = state->memtupdeleted; i < nremove; i++)
{
FREEMEM(state, GetMemoryChunkSpace(state->memtuples[i]));
pfree(state->memtuples[i]);
+ state->memtuples[i] = NULL;
}
+ state->memtupdeleted = nremove;
+
+ /* mark tuplestore as truncated (used for Assert crosschecks only) */
+ state->truncated = true;
/*
! * If nremove is less than 1/8th memtupcount, just stop here, leaving the
! * "deleted" slots as NULL. This prevents us from expending O(N^2) time
! * repeatedly memmove-ing a large pointer array. The worst case space
! * wastage is pretty small, since it's just pointers and not whole tuples.
! */
! if (nremove < state->memtupcount / 8)
! return;
!
! /*
! * Slide the array down and readjust pointers.
*
! * In mergejoin's current usage, it's demonstrable that there will always
! * be exactly one non-removed tuple; so optimize that case.
*/
if (nremove + 1 == state->memtupcount)
state->memtuples[0] = state->memtuples[nremove];
*************** tuplestore_trim(Tuplestorestate *state)
*** 1178,1192 ****
memmove(state->memtuples, state->memtuples + nremove,
(state->memtupcount - nremove) * sizeof(void *));
state->memtupcount -= nremove;
for (i = 0; i < state->readptrcount; i++)
{
if (!state->readptrs[i].eof_reached)
state->readptrs[i].current -= nremove;
}
-
- /* mark tuplestore as truncated (used for Assert crosschecks only) */
- state->truncated = true;
}
/*
--- 1200,1212 ----
memmove(state->memtuples, state->memtuples + nremove,
(state->memtupcount - nremove) * sizeof(void *));
+ state->memtupdeleted = 0;
state->memtupcount -= nremove;
for (i = 0; i < state->readptrcount; i++)
{
if (!state->readptrs[i].eof_reached)
state->readptrs[i].current -= nremove;
}
}
/*