diff src/backend/utils/sort/tuplesort.c index d5a2003..27d833f *** a/src/backend/utils/sort/tuplesort.c --- b/src/backend/utils/sort/tuplesort.c *************** struct Tuplesortstate *** 275,280 **** --- 275,281 ---- SortTuple *memtuples; /* array of SortTuple structs */ int memtupcount; /* number of tuples currently present */ int memtupsize; /* allocated length of memtuples array */ + bool fin_growth; /* are we no longer growing memtupsize? */ /* * While building initial runs, this is the current output run number *************** tuplesort_begin_common(int workMem, bool *** 569,574 **** --- 570,576 ---- state->memtupcount = 0; state->memtupsize = 1024; /* initial guess */ + state->fin_growth = false; state->memtuples = (SortTuple *) palloc(state->memtupsize * sizeof(SortTuple)); USEMEM(state, GetMemoryChunkSpace(state->memtuples)); *************** tuplesort_end(Tuplesortstate *state) *** 956,965 **** * Grow the memtuples[] array, if possible within our memory constraint. * Return TRUE if able to enlarge the array, FALSE if not. * ! * At each increment we double the size of the array. When we are short ! * on memory we could consider smaller increases, but because availMem ! * moves around with tuple addition/removal, this might result in thrashing. ! * Small increases in the array size are likely to be pretty inefficient. */ static bool grow_memtuples(Tuplesortstate *state) --- 958,968 ---- * Grow the memtuples[] array, if possible within our memory constraint. * Return TRUE if able to enlarge the array, FALSE if not. * ! * At each increment (except, possibly, the last) we double the size of the ! * array. When we are short on memory we consider smaller increases, but ! * because availMem moves around with tuple addition/removal, this might result ! * in thrashing, which is why it will only occur once at the most. Small ! * increases in the array size are likely to be pretty inefficient. */ static bool grow_memtuples(Tuplesortstate *state) *************** grow_memtuples(Tuplesortstate *state) *** 973,990 **** * enough to force palloc to treat it as a separate chunk, so this * assumption should be good. But let's check it.) */ ! if (state->availMem <= (long) (state->memtupsize * sizeof(SortTuple))) return false; /* * On a 64-bit machine, allowedMem could be high enough to get us into * trouble with MaxAllocSize, too. */ ! if ((Size) (state->memtupsize * 2) >= MaxAllocSize / sizeof(SortTuple)) return false; FREEMEM(state, GetMemoryChunkSpace(state->memtuples)); ! state->memtupsize *= 2; state->memtuples = (SortTuple *) repalloc(state->memtuples, state->memtupsize * sizeof(SortTuple)); --- 976,1028 ---- * enough to force palloc to treat it as a separate chunk, so this * assumption should be good. But let's check it.) */ ! int newmemtupsize; ! ! if (state->fin_growth) ! { return false; + } + else if (state->availMem < state->allowedMem / 2) + { + /* + * Make a last-ditch effort to avoid exceeding allowedMem - abandon + * doubling strategy. + * + * Once we are approaching the final growth of memtuples, use the known + * size of the tuples seen so far to estimate final growth size. This + * should be on average more space-efficient, which is particularly + * important here. + * + * N.B. We rely on the assumption that nothing other than memtuples and + * individual tuple storage has been deducted from availMem. + */ + float oldmemtupsize = (float) state->memtupsize; + float allowedMem = (float) state->allowedMem; + float memNowUsed = (float) state->allowedMem - state->availMem; + + Assert(memNowUsed > 0); + + /* + * XXX: This feels quite brittle; is there a better principled approach, + * that does not violate modularity? + */ + newmemtupsize = (int) floor(oldmemtupsize * allowedMem / memNowUsed); + state->fin_growth = true; + } + else + { + newmemtupsize = state->memtupsize * 2; + } /* * On a 64-bit machine, allowedMem could be high enough to get us into * trouble with MaxAllocSize, too. */ ! if ((Size) (newmemtupsize) >= MaxAllocSize / sizeof(SortTuple)) return false; FREEMEM(state, GetMemoryChunkSpace(state->memtuples)); ! state->memtupsize = newmemtupsize; state->memtuples = (SortTuple *) repalloc(state->memtuples, state->memtupsize * sizeof(SortTuple));