Re: [PATCH] Use streaming read I/O in sample scans

From: Nazir Bilal Yavuz <byavuz81(at)gmail(dot)com>
To: Yuhang Qiu <iamqyh(at)gmail(dot)com>
Cc: PostgreSQL-development <pgsql-hackers(at)postgresql(dot)org>, Melanie Plageman <melanieplageman(at)gmail(dot)com>
Subject: Re: [PATCH] Use streaming read I/O in sample scans
Date: 2026-08-27 14:31:21
Message-ID: CAN55FZ3Lik2m9J+EQjzEHSMSBNgAXU5Myt8LFg=D2OQ0i_jUMQ@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi,

Thank you for working on this!

On Wed, 26 Aug 2026 at 16:46, Yuhang Qiu <iamqyh(at)gmail(dot)com> wrote:
>
> I tested this with a 64 GiB table containing 8,388,608 pages, with one
> row per page. PostgreSQL was restarted before each run to clear shared
> buffers. The main settings were:
> debug_io_direct=data
> io_method=worker
> shared_buffers=128MB
> effective_io_concurrency=16
>
> Results:
> master patch speedup
> SYSTEM(0.1) 829ms 121ms 6.8x
> SYSTEM(1) 7.53s 0.89s 8.4x
> SYSTEM(10) 70.84s 10.62s 6.7x
> SYSTEM_ROWS(100000) 9.05s 0.97s 9.3x
> BERNOULLI(100) LIMIT 100000 1.83s 0.56s 3.3x
>
> With 8 concurrent clients running `SYSTEM(1)`, throughput increased
> from 1.06 TPS to 4.29 TPS.

Nice results!

> Performance was almost unchanged with `effective_io_concurrency=0`.
> With worker I/O and `effective_io_concurrency=1`, the patch was about
> 8% slower. When all data was cached, the regression was about 1% to 2%.
> Queries that stop early, such as `LIMIT` and `SYSTEM_ROWS`, may also
> issue a small number of unused speculative reads.

I think that is expected.

I started reviewing the patches, but it will take some time.
Meanwhile, I think the initscan() strategy changes in 0001 and 0002
fix an independent bug and could be committed separately. Melanie is
CCed since she may know more. Bug:

heap_beginscan() creates a read stream using the current
scan->rs_strategy. On rescan, initscan() recalculates strategy
eligibility and may allocate or free the strategy if the relation
crosses the NBuffers / 4 threshold. However, the reused read stream
retains the original scan->rs_strategy. This can cause the stream to
either keep using no strategy after one is allocated or retain a
dangling pointer after one is freed.

Also, review for the 0001:

+void
+read_stream_set_strategy(ReadStream *stream, BufferAccessStrategy strategy)
+{
+ Assert(stream->pinned_buffers == 0);
+ Assert(stream->ios_in_progress == 0);
+ Assert(stream->pending_read_nblocks == 0);
+
+ stream->max_pinned_buffers = Min(stream->queue_size - 1,
+ GetAccessStrategyPinLimit(strategy));

stream->max_pinned_buffers might be 0 after this, it should be at
least 1 to proceed.

Another thing is, GetAccessStrategyPinLimit() could return higher
number than current stream->max_pinned_buffers, it would make sense to
re-create stream on some of this cases because it might be faster. I
am not sure how to calculate that, though.

+
+ if (stream->stats)
+ stream->stats->distance_capacity = stream->max_pinned_buffers;

You change distance_capacity but we are still in the same scan. I
think distance_capacity should be highest stream->max_pinned_buffers,
which is the first one as it might only decrease. So, no need to
change it.

--
Regards,
Nazir Bilal Yavuz
Microsoft

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Nikita Malakhov 2026-08-27 14:36:27 Re: [(known) BUG] DELETE/UPDATE more than one row in partitioned foreign table
Previous Message Osama Abdul Qader 2026-08-27 14:23:39 Re: REPACK (ANALYZE) within transaction block segfaults