siftup-reverse-linear.patch
application/octet-stream
Filename: siftup-reverse-linear.patch
Type: application/octet-stream
Part: 1
Message:
Re: Memory usage during sorting
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/tuplesort.c | 57 | 13 |
diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c
index d8e5d68..cb837a8 100644
--- a/src/backend/utils/sort/tuplesort.c
+++ b/src/backend/utils/sort/tuplesort.c
@@ -442,6 +442,7 @@ struct Tuplesortstate
elog(ERROR, "unexpected end of data"); \
} while(0)
+#define HEAP_ANCESTOR(slot, levels) ((((slot)+1)>>(levels))-1)
static Tuplesortstate *tuplesort_begin_common(int workMem, bool randomAccess);
static void puttuple_common(Tuplesortstate *state, SortTuple *tuple);
@@ -2549,31 +2550,74 @@ tuplesort_heap_siftup(Tuplesortstate *state, bool checkIndex)
SortTuple *memtuples = state->memtuples;
SortTuple *tuple;
int i,
- n;
+ n,
+ levels = 0,
+ l;
if (--state->memtupcount <= 0)
return;
CHECK_FOR_INTERRUPTS();
- n = state->memtupcount;
- tuple = &memtuples[n]; /* tuple that must be reinserted */
- i = 0; /* i is where the "hole" is */
- for (;;)
+ tuple = &memtuples[state->memtupcount]; /* tuple that must be reinserted */
+ n = (state->memtupcount - 1) >> 1; /* first tuple without two children */
+
+ /* Descend heap, following lesser child at each level. */
+ i = 0;
+ while (i < n)
{
int j = 2 * i + 1;
- if (j >= n)
- break;
- if (j + 1 < n &&
- HEAPCOMPARE(&memtuples[j], &memtuples[j + 1]) > 0)
+ if (HEAPCOMPARE(&memtuples[j], &memtuples[j + 1]) > 0)
j++;
- if (HEAPCOMPARE(tuple, &memtuples[j]) <= 0)
- break;
- memtuples[i] = memtuples[j];
i = j;
+ ++levels;
+ }
+
+ /*
+ * If the heap is of even size, we might have landed on the tuple that has
+ * just a single child. In that case, we don't need to compare the children
+ * to determine which is smaller, but we do need to descend to the child.
+ */
+ if (i == n && (state->memtupcount & 1) == 0)
+ {
+ i = 2 * i + 1;
+ ++levels;
+ }
+ Assert(i < state->memtupcount);
+ Assert(HEAP_ANCESTOR(i, levels) == 0);
+
+ /*
+ * Search path of least children to find the insert position.
+ *
+ * From the heap property, we know that the path of least children is
+ * sorted; that is, memtuples[i] is greater than its parent, which in
+ * turn is greater than its own parent, and so on. We'd like to find the
+ * smallest number of levels we must travel up the heap to find a tuple
+ * less than or equal to the one we're reinserting; if there is no
+ * such shift, then we reinsert at the top of the heap.
+ *
+ * The element we're reinserting here came from the bottom of the heap,
+ * so it's likely to be large. Accordingly, we perform a linear search.
+ * A binary search would be better if the insertion position were just
+ * as likely to be at the top of the heap as at the bottom, but in reality
+ * the bottom is much more likely.
+ */
+ for (l = 0; l < levels; ++l)
+ if (HEAPCOMPARE(tuple, &memtuples[HEAP_ANCESTOR(i, l)]) > 0)
+ break;
+
+ /*
+ * Move the hole down the tree until it reaches the correct position, and
+ * then stick the re-inserted tuple into it.
+ */
+ while (levels > l)
+ {
+ memtuples[HEAP_ANCESTOR(i, levels)] =
+ memtuples[HEAP_ANCESTOR(i, levels - 1)];
+ --levels;
}
- memtuples[i] = *tuple;
+ memtuples[HEAP_ANCESTOR(i, l)] = *tuple;
}