From: Thomas Munro Date: Sat, 6 Apr 2024 04:14:12 +0000 (+1300) Subject: Improve read_stream.c's fast path. X-Git-Tag: REL_17_BETA1~385 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=aa1e8c206454d666ab600050022aef2c3c390f69;p=thirdparty%2Fpostgresql.git 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 Discussion: https://postgr.es/m/CA%2BhUKGKXZALJ%3D6aArUsXRJzBm%3Dqvc4AWp7%3DiJNXJQqpbRLnD_w%40mail.gmail.com --- diff --git a/src/backend/storage/aio/read_stream.c b/src/backend/storage/aio/read_stream.c index 4f21262ff5e..b9e11a28312 100644 --- a/src/backend/storage/aio/read_stream.c +++ b/src/backend/storage/aio/read_stream.c @@ -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; - } - - /* - * For anything more complex, set up some more state and take the slow - * path next time. - */ - stream->fast_path = false; + /* + * 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; + } - 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);