Repair old performance bug in tuplesort.c/logtape.c. In the case where
we are doing the final merge pass on-the-fly, and not writing the data back onto a 'tape', the number of free blocks in the tape set will become large, leading to a lot of time wasted in ltsReleaseBlock(). There is really no need to track the free blocks anymore in this state, so add a simple shutoff switch. Per report from Stefan Kaltenbrunner.
This commit is contained in:
parent
e6107da53c
commit
8db05ba411
@ -64,7 +64,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/utils/sort/logtape.c,v 1.19 2006/03/05 15:58:49 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/utils/sort/logtape.c,v 1.20 2006/03/07 19:06:49 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -146,7 +146,12 @@ struct LogicalTapeSet
|
|||||||
* When there are no such blocks, we extend the underlying file. Note
|
* When there are no such blocks, we extend the underlying file. Note
|
||||||
* that the block numbers in freeBlocks are always in *decreasing* order,
|
* that the block numbers in freeBlocks are always in *decreasing* order,
|
||||||
* so that removing the last entry gives us the lowest free block.
|
* so that removing the last entry gives us the lowest free block.
|
||||||
|
*
|
||||||
|
* If forgetFreeSpace is true then any freed blocks are simply forgotten
|
||||||
|
* rather than being remembered in freeBlocks[]. See notes for
|
||||||
|
* LogicalTapeSetForgetFreeSpace().
|
||||||
*/
|
*/
|
||||||
|
bool forgetFreeSpace; /* are we remembering free blocks? */
|
||||||
long *freeBlocks; /* resizable array */
|
long *freeBlocks; /* resizable array */
|
||||||
int nFreeBlocks; /* # of currently free blocks */
|
int nFreeBlocks; /* # of currently free blocks */
|
||||||
int freeBlocksLen; /* current allocated length of freeBlocks[] */
|
int freeBlocksLen; /* current allocated length of freeBlocks[] */
|
||||||
@ -247,6 +252,12 @@ ltsReleaseBlock(LogicalTapeSet *lts, long blocknum)
|
|||||||
int ndx;
|
int ndx;
|
||||||
long *ptr;
|
long *ptr;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Do nothing if we're no longer interested in remembering free space.
|
||||||
|
*/
|
||||||
|
if (lts->forgetFreeSpace)
|
||||||
|
return;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Enlarge freeBlocks array if full.
|
* Enlarge freeBlocks array if full.
|
||||||
*/
|
*/
|
||||||
@ -491,6 +502,7 @@ LogicalTapeSetCreate(int ntapes)
|
|||||||
(ntapes - 1) * sizeof(LogicalTape));
|
(ntapes - 1) * sizeof(LogicalTape));
|
||||||
lts->pfile = BufFileCreateTemp(false);
|
lts->pfile = BufFileCreateTemp(false);
|
||||||
lts->nFileBlocks = 0L;
|
lts->nFileBlocks = 0L;
|
||||||
|
lts->forgetFreeSpace = false;
|
||||||
lts->freeBlocksLen = 32; /* reasonable initial guess */
|
lts->freeBlocksLen = 32; /* reasonable initial guess */
|
||||||
lts->freeBlocks = (long *) palloc(lts->freeBlocksLen * sizeof(long));
|
lts->freeBlocks = (long *) palloc(lts->freeBlocksLen * sizeof(long));
|
||||||
lts->nFreeBlocks = 0;
|
lts->nFreeBlocks = 0;
|
||||||
@ -546,6 +558,21 @@ LogicalTapeSetClose(LogicalTapeSet *lts)
|
|||||||
pfree(lts);
|
pfree(lts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Mark a logical tape set as not needing management of free space anymore.
|
||||||
|
*
|
||||||
|
* This should be called if the caller does not intend to write any more data
|
||||||
|
* into the tape set, but is reading from un-frozen tapes. Since no more
|
||||||
|
* writes are planned, remembering free blocks is no longer useful. Setting
|
||||||
|
* this flag lets us avoid wasting time and space in ltsReleaseBlock(), which
|
||||||
|
* is not designed to handle large numbers of free blocks.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
LogicalTapeSetForgetFreeSpace(LogicalTapeSet *lts)
|
||||||
|
{
|
||||||
|
lts->forgetFreeSpace = true;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Dump the dirty buffer of a logical tape.
|
* Dump the dirty buffer of a logical tape.
|
||||||
*/
|
*/
|
||||||
|
@ -91,7 +91,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/utils/sort/tuplesort.c,v 1.62 2006/03/05 15:58:49 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/utils/sort/tuplesort.c,v 1.63 2006/03/07 19:06:50 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -1384,7 +1384,8 @@ mergeruns(Tuplesortstate *state)
|
|||||||
/*
|
/*
|
||||||
* If we produced only one initial run (quite likely if the total data
|
* If we produced only one initial run (quite likely if the total data
|
||||||
* volume is between 1X and 2X workMem), we can just use that tape as the
|
* volume is between 1X and 2X workMem), we can just use that tape as the
|
||||||
* finished output, rather than doing a useless merge.
|
* finished output, rather than doing a useless merge. (This obvious
|
||||||
|
* optimization is not in Knuth's algorithm.)
|
||||||
*/
|
*/
|
||||||
if (state->currentRun == 1)
|
if (state->currentRun == 1)
|
||||||
{
|
{
|
||||||
@ -1401,33 +1402,51 @@ mergeruns(Tuplesortstate *state)
|
|||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
/* Step D5: merge runs onto tape[T] until tape[P] is empty */
|
/*
|
||||||
while (state->tp_runs[state->tapeRange - 1] ||
|
* At this point we know that tape[T] is empty. If there's just one
|
||||||
state->tp_dummy[state->tapeRange - 1])
|
* (real or dummy) run left on each input tape, then only one merge
|
||||||
|
* pass remains. If we don't have to produce a materialized sorted
|
||||||
|
* tape, we can stop at this point and do the final merge on-the-fly.
|
||||||
|
*/
|
||||||
|
if (!state->randomAccess)
|
||||||
{
|
{
|
||||||
bool allDummy = true;
|
|
||||||
bool allOneRun = true;
|
bool allOneRun = true;
|
||||||
|
|
||||||
|
Assert(state->tp_runs[state->tapeRange] == 0);
|
||||||
for (tapenum = 0; tapenum < state->tapeRange; tapenum++)
|
for (tapenum = 0; tapenum < state->tapeRange; tapenum++)
|
||||||
{
|
{
|
||||||
if (state->tp_dummy[tapenum] == 0)
|
|
||||||
allDummy = false;
|
|
||||||
if (state->tp_runs[tapenum] + state->tp_dummy[tapenum] != 1)
|
if (state->tp_runs[tapenum] + state->tp_dummy[tapenum] != 1)
|
||||||
allOneRun = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If we don't have to produce a materialized sorted tape, quit as
|
|
||||||
* soon as we're down to one real/dummy run per tape.
|
|
||||||
*/
|
|
||||||
if (!state->randomAccess && allOneRun)
|
|
||||||
{
|
{
|
||||||
Assert(!allDummy);
|
allOneRun = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (allOneRun)
|
||||||
|
{
|
||||||
|
/* Tell logtape.c we won't be writing anymore */
|
||||||
|
LogicalTapeSetForgetFreeSpace(state->tapeset);
|
||||||
/* Initialize for the final merge pass */
|
/* Initialize for the final merge pass */
|
||||||
beginmerge(state);
|
beginmerge(state);
|
||||||
state->status = TSS_FINALMERGE;
|
state->status = TSS_FINALMERGE;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Step D5: merge runs onto tape[T] until tape[P] is empty */
|
||||||
|
while (state->tp_runs[state->tapeRange - 1] ||
|
||||||
|
state->tp_dummy[state->tapeRange - 1])
|
||||||
|
{
|
||||||
|
bool allDummy = true;
|
||||||
|
|
||||||
|
for (tapenum = 0; tapenum < state->tapeRange; tapenum++)
|
||||||
|
{
|
||||||
|
if (state->tp_dummy[tapenum] == 0)
|
||||||
|
{
|
||||||
|
allDummy = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (allDummy)
|
if (allDummy)
|
||||||
{
|
{
|
||||||
state->tp_dummy[state->tapeRange]++;
|
state->tp_dummy[state->tapeRange]++;
|
||||||
@ -1437,6 +1456,7 @@ mergeruns(Tuplesortstate *state)
|
|||||||
else
|
else
|
||||||
mergeonerun(state);
|
mergeonerun(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Step D6: decrease level */
|
/* Step D6: decrease level */
|
||||||
if (--state->Level == 0)
|
if (--state->Level == 0)
|
||||||
break;
|
break;
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/utils/logtape.h,v 1.14 2006/03/05 15:59:07 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/utils/logtape.h,v 1.15 2006/03/07 19:06:50 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -26,6 +26,7 @@ typedef struct LogicalTapeSet LogicalTapeSet;
|
|||||||
|
|
||||||
extern LogicalTapeSet *LogicalTapeSetCreate(int ntapes);
|
extern LogicalTapeSet *LogicalTapeSetCreate(int ntapes);
|
||||||
extern void LogicalTapeSetClose(LogicalTapeSet *lts);
|
extern void LogicalTapeSetClose(LogicalTapeSet *lts);
|
||||||
|
extern void LogicalTapeSetForgetFreeSpace(LogicalTapeSet *lts);
|
||||||
extern size_t LogicalTapeRead(LogicalTapeSet *lts, int tapenum,
|
extern size_t LogicalTapeRead(LogicalTapeSet *lts, int tapenum,
|
||||||
void *ptr, size_t size);
|
void *ptr, size_t size);
|
||||||
extern void LogicalTapeWrite(LogicalTapeSet *lts, int tapenum,
|
extern void LogicalTapeWrite(LogicalTapeSet *lts, int tapenum,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user