Improve read_stream.c's fast path.

The "fast path" for well cached scans that don't do any I/O was
accidentally coded in a way that could only be triggered by pg_prewarm's
usage pattern, which starts out with a higher distance because of the
flags it passes in.  We want it to work for streaming sequential scans
too, once that patch is committed.  Adjust.

Reviewed-by: Melanie Plageman <melanieplageman@gmail.com>
Discussion: https://postgr.es/m/CA%2BhUKGKXZALJ%3D6aArUsXRJzBm%3Dqvc4AWp7%3DiJNXJQqpbRLnD_w%40mail.gmail.com
This commit is contained in:
Thomas Munro 2024-04-06 17:14:12 +13:00
parent 9e7386924e
commit aa1e8c2064
1 changed files with 31 additions and 44 deletions

View File

@ -169,7 +169,7 @@ get_per_buffer_data(ReadStream *stream, int16 buffer_index)
/*
* Ask the callback which block it would like us to read next, with a small
* buffer in front to allow read_stream_unget_block() to work and to allow the
* fast path to work in batches.
* fast path to skip this function and work directly from the array.
*/
static inline BlockNumber
read_stream_get_block(ReadStream *stream, void *per_buffer_data)
@ -578,13 +578,12 @@ read_stream_next_buffer(ReadStream *stream, void **per_buffer_data)
if (likely(stream->fast_path))
{
BlockNumber next_blocknum;
bool need_wait;
/* Fast path assumptions. */
Assert(stream->ios_in_progress == 0);
Assert(stream->pinned_buffers == 1);
Assert(stream->distance == 1);
Assert(stream->pending_read_nblocks == 1);
Assert(stream->pending_read_nblocks == 0);
Assert(stream->per_buffer_data_size == 0);
/* We're going to return the buffer we pinned last time. */
@ -594,40 +593,29 @@ read_stream_next_buffer(ReadStream *stream, void **per_buffer_data)
buffer = stream->buffers[oldest_buffer_index];
Assert(buffer != InvalidBuffer);
/*
* Pin a buffer for the next call. Same buffer entry, and arbitrary
* I/O entry (they're all free).
*/
need_wait = StartReadBuffer(&stream->ios[0].op,
&stream->buffers[oldest_buffer_index],
stream->pending_read_blocknum,
stream->advice_enabled ?
READ_BUFFERS_ISSUE_ADVICE : 0);
/* Choose the block the next call will pin. */
/* Choose the next block to pin. */
if (unlikely(stream->blocknums_next == stream->blocknums_count))
read_stream_fill_blocknums(stream);
next_blocknum = stream->blocknums[stream->blocknums_next++];
/*
* Fast return if the next call doesn't require I/O for the buffer we
* just pinned, and we have a block number to give it as a pending
* read.
*/
if (likely(!need_wait && next_blocknum != InvalidBlockNumber))
if (likely(next_blocknum != InvalidBlockNumber))
{
stream->pending_read_blocknum = next_blocknum;
return buffer;
}
/*
* Pin a buffer for the next call. Same buffer entry, and
* arbitrary I/O entry (they're all free). We don't have to
* adjust pinned_buffers because we're transferring one to caller
* but pinning one more.
*/
if (likely(!StartReadBuffer(&stream->ios[0].op,
&stream->buffers[oldest_buffer_index],
next_blocknum,
stream->advice_enabled ?
READ_BUFFERS_ISSUE_ADVICE : 0)))
{
/* Fast return. */
return buffer;
}
/*
* For anything more complex, set up some more state and take the slow
* path next time.
*/
stream->fast_path = false;
if (need_wait)
{
/* Next call must wait for I/O for the newly pinned buffer. */
stream->oldest_io_index = 0;
stream->next_io_index = stream->max_ios > 1 ? 1 : 0;
@ -635,17 +623,15 @@ read_stream_next_buffer(ReadStream *stream, void **per_buffer_data)
stream->ios[0].buffer_index = oldest_buffer_index;
stream->seq_blocknum = next_blocknum + 1;
}
if (next_blocknum == InvalidBlockNumber)
{
/* Next call hits end of stream and can't pin anything more. */
stream->distance = 0;
stream->pending_read_nblocks = 0;
}
else
{
/* Set up the pending read. */
stream->pending_read_blocknum = next_blocknum;
/* No more blocks, end of stream. */
stream->distance = 0;
stream->oldest_buffer_index = stream->next_buffer_index;
stream->pinned_buffers = 0;
}
stream->fast_path = false;
return buffer;
}
#endif
@ -762,15 +748,11 @@ read_stream_next_buffer(ReadStream *stream, void **per_buffer_data)
if (stream->ios_in_progress == 0 &&
stream->pinned_buffers == 1 &&
stream->distance == 1 &&
stream->pending_read_nblocks == 1 &&
stream->pending_read_nblocks == 0 &&
stream->per_buffer_data_size == 0)
{
stream->fast_path = true;
}
else
{
stream->fast_path = false;
}
#endif
return buffer;
@ -790,6 +772,11 @@ read_stream_reset(ReadStream *stream)
/* Stop looking ahead. */
stream->distance = 0;
/* Forget buffered block numbers and fast path state. */
stream->blocknums_next = 0;
stream->blocknums_count = 0;
stream->fast_path = false;
/* Unpin anything that wasn't consumed. */
while ((buffer = read_stream_next_buffer(stream, NULL)) != InvalidBuffer)
ReleaseBuffer(buffer);