Thread: Possible regression in PG18 beta1

Possible regression in PG18 beta1

From
Sadeq Dousti
Date:
Dear all,

I was testing PG18 beta 1 new features (noticeably, AIO and index skip scan), and I came up with this example:

===========
drop table if exists t;

create TEMP table t(i,j,k)
as select n,n,n
from generate_series(1,10_000_000) as n;

analyze t;

create index on t(i,j,k);

explain (analyze,buffers,costs off,timing off)
select * from t where k = 1;
===========


On my laptop (MacBook Air M3), Postgres 17.5 runs the query over 2x faster than 18 beta1. The former uses index-only scan, while the latter uses seq scan:

=== PG 17.5 =====
                           QUERY PLAN
----------------------------------------------------------------
 Index Only Scan using t_i_j_k_idx on t (actual rows=1 loops=1)
   Index Cond: (k = 1)
   Heap Fetches: 1
   Buffers: local hit=1 read=38317
 Planning Time: 0.315 ms
 Execution Time: 242.711 ms
(6 rows)



=== PG 18 beta 1 =====
                QUERY PLAN
------------------------------------------
 Seq Scan on t (actual rows=1.00 loops=1)
   Filter: (k = 1)
   Rows Removed by Filter: 9999999
   Buffers: local hit=1 read=54079
 Planning Time: 0.115 ms
 Execution Time: 520.465 ms
(6 rows)


If I turn off enable_seqscan on the latter, it will use the index-only scan, with the query time similar to PG17 (yet a tad slower):

=== PG 18 beta 1, with enable_seqscan disabled =====
                            QUERY PLAN
-------------------------------------------------------------------
 Index Only Scan using t_i_j_k_idx on t (actual rows=1.00 loops=1)
   Index Cond: (k = 1)
   Heap Fetches: 1
   Index Searches: 1
   Buffers: local hit=1 read=38317
 Planning Time: 0.200 ms
 Execution Time: 281.742 ms
(7 rows)


* Also, I noticed "explain analyze" reports actual rows as a decimal number (1.00) as opposed to an integer (1); not sure if that's intentional.

* Changing the table from TEMP to UNLOGGED makes both versions use "Parallel Seq Scan", with PG18 being ~25% faster. (190ms vs. 150ms).


Best Regards,
Sadeq Dousti

Re: Possible regression in PG18 beta1

From
Sadeq Dousti
Date:
Hi,

You're right, with settings, it revealed that PG17 had random_page_cost=1.1 configured.

Adding that to PG18 sets the plan to index only; however, PG18 is still somewhat (~15%) slower:

============ PG17.5 =================
                           QUERY PLAN
----------------------------------------------------------------
 Index Only Scan using t_i_j_k_idx on t (actual rows=1 loops=1)
   Index Cond: (k = 1)
   Heap Fetches: 1
   Buffers: local read=38318 written=424
 Settings: random_page_cost = '1.1'
 Planning:
   Buffers: shared hit=29, local read=1 written=1
 Planning Time: 0.098 ms
 Execution Time: 137.209 ms
(9 rows)

============ PG18 Beta 1 =================
                            QUERY PLAN
-------------------------------------------------------------------
 Index Only Scan using t_i_j_k_idx on t (actual rows=1.00 loops=1)
   Index Cond: (k = 1)
   Heap Fetches: 1
   Index Searches: 1
   Buffers: local read=38318 written=443
 Settings: random_page_cost = '1.1'
 Planning:
   Buffers: shared hit=30, local read=1
 Planning Time: 0.097 ms
 Execution Time: 160.595 ms
(10 rows)


So, one mystery solved (no planner regression), still three questions:
* Somewhat slower execution in PG18
* Planner differences in TEMP vs. UNLOGGED
Actual rows with decimal (1.00) vs. integer (1)

Best Regards,
Sadeq Dousti

On Sat, May 17, 2025 at 6:18 PM Christophe Courtois <christophe.courtois@dalibo.com> wrote:
Hi,

- I cannot reproduce your regression, I have always a Seq Scan (PG 13,
17, 18, default config, last versions).

I suggest that you add a SETTINGS clause in EXPLAIN and \d+ and \di+
before, just in case.

- I see the difference between TEMP and UNLOGGED too (since PG13),
this is funny but I have no explanation. Something to do with the access
to shared buffers, I suppose.

Yours,

Le 17/05/2025 à 17:45, Sadeq Dousti a écrit :
> Dear all,
>
> I was testing PG18 beta 1 new features (noticeably, AIO and index skip
> scan), and I came up with this example:
>
> ===========
> drop table if exists t;
>
> create TEMP table t(i,j,k)
> as select n,n,n
> from generate_series(1,10_000_000) as n;
>
> analyze t;
>
> create index on t(i,j,k);
>
> explain (analyze,buffers,costs off,timing off)
> select * from t where k = 1;
> ===========
>
>
> On my laptop (MacBook Air M3), Postgres 17.5 runs the query over 2x
> faster than 18 beta1. The former uses index-only scan, while the latter
> uses seq scan:
>
> === PG 17.5 =====
>                             QUERY PLAN
> ----------------------------------------------------------------
>   Index Only Scan using t_i_j_k_idx on t (actual rows=1 loops=1)
>     Index Cond: (k = 1)
>     Heap Fetches: 1
>     Buffers: local hit=1 read=38317
>   Planning Time: 0.315 ms
>   Execution Time: 242.711 ms
> (6 rows)
>
>
>
> === PG 18 beta 1 =====
>                  QUERY PLAN
> ------------------------------------------
>   Seq Scan on t (actual rows=1.00 loops=1)
>     Filter: (k = 1)
>     Rows Removed by Filter: 9999999
>     Buffers: local hit=1 read=54079
>   Planning Time: 0.115 ms
>   Execution Time: 520.465 ms
> (6 rows)
>
>
> If I turn off enable_seqscan on the latter, it will use the index-only
> scan, with the query time similar to PG17 (yet a tad slower):
>
> === PG 18 beta 1, with enable_seqscan disabled =====
>                              QUERY PLAN
> -------------------------------------------------------------------
>   Index Only Scan using t_i_j_k_idx on t (actual rows=1.00 loops=1)
>     Index Cond: (k = 1)
>     Heap Fetches: 1
>     Index Searches: 1
>     Buffers: local hit=1 read=38317
>   Planning Time: 0.200 ms
>   Execution Time: 281.742 ms
> (7 rows)
>
>
> * Also, I noticed "explain analyze" reports actual rows as a decimal
> number (1.00) as opposed to an integer (1); not sure if that's intentional.
>
> * Changing the table from TEMP to UNLOGGED makes both versions use
> "Parallel Seq Scan", with PG18 being ~25% faster. (190ms vs. 150ms).
>
>
> Best Regards,
> Sadeq Dousti

--
_________  ____
|         ||    |   Christophe Courtois
|         ||__  |   Consultant DALIBO
|         |   | |   43, rue du Faubourg Montmartre
|    -    |  / /    75009 Paris
|___| |___|  \/     www.dalibo.com

Re: Possible regression in PG18 beta1

From
Sadeq Dousti
Date:
Yes, this is intentional as of 95dbd827f2edc
Thanks!

 
>> > * Changing the table from TEMP to UNLOGGED makes both versions use
>> > "Parallel Seq Scan", with PG18 being ~25% faster. (190ms vs. 150ms).

I'll be curious about tests with a normal table as well with a
sufficiently large shared_buffers.

Here are results for a normal table with default shared_buffers (128 MB) and large shared_buffers (4GB):

==== PG17.5, shared_buffers=128MB ======
                      QUERY PLAN
------------------------------------------------------
 Gather (actual rows=1 loops=1)
   Workers Planned: 2
   Workers Launched: 2
   Buffers: shared hit=3584 read=50496
   ->  Parallel Seq Scan on t (actual rows=0 loops=3)
         Filter: (k = 1)
         Rows Removed by Filter: 3333333
         Buffers: shared hit=3584 read=50496
 Settings: random_page_cost = '1.1'
 Planning Time: 0.119 ms
 Execution Time: 141.374 ms
(11 rows)

==== PG17.5, shared_buffers=4GB ======
select * from t where k = 1;
                      QUERY PLAN
------------------------------------------------------
 Gather (actual rows=1 loops=1)
   Workers Planned: 2
   Workers Launched: 2
   Buffers: shared hit=3872 read=50208
   ->  Parallel Seq Scan on t (actual rows=0 loops=3)
         Filter: (k = 1)
         Rows Removed by Filter: 3333333
         Buffers: shared hit=3872 read=50208
 Settings: random_page_cost = '1.1'
 Planning Time: 0.118 ms
 Execution Time: 141.846 ms
(11 rows)


==== PG18 beta1, shared_buffers=128MB ======
                       QUERY PLAN
---------------------------------------------------------
 Gather (actual rows=1.00 loops=1)
   Workers Planned: 2
   Workers Launched: 2
   Buffers: shared hit=15986 read=38094
   ->  Parallel Seq Scan on t (actual rows=0.33 loops=3)
         Filter: (k = 1)
         Rows Removed by Filter: 3333333
         Buffers: shared hit=15986 read=38094
 Settings: random_page_cost = '1.1'
 Planning Time: 0.123 ms
 Execution Time: 110.650 ms
(11 rows)


==== PG18 beta1, shared_buffers=4GB ======
                        QUERY PLAN
---------------------------------------------------------
 Gather (actual rows=1.00 loops=1)
   Workers Planned: 2
   Workers Launched: 2
   Buffers: shared hit=15991 read=38089
   ->  Parallel Seq Scan on t (actual rows=0.33 loops=3)
         Filter: (k = 1)
         Rows Removed by Filter: 3333333
         Buffers: shared hit=15991 read=38089
 Settings: random_page_cost = '1.1'
 Planning Time: 0.095 ms
 Execution Time: 109.073 ms
(11 rows)


Best Regards,
Sadeq Dousti

Re: Possible regression in PG18 beta1

From
Peter Geoghegan
Date:
On Sat, May 17, 2025 at 12:38 PM Sadeq Dousti <msdousti@gmail.com> wrote:
> So, one mystery solved (no planner regression), still three questions:
> * Somewhat slower execution in PG18

I cannot recreate the problem.

The fastest plan for this query is a parallel sequential scan -- the
t_i_j_k_idx  index is useless. The only reason it's used for an
index-only scan when random_page_cost is reduced to 1.1 is because it
happens to have no fragmentation (meaning the index itself can be read
in physical order).

Even if I force an index-only scan, I still see no significant
performance differences when I compare REL_17_STABLE to master/18.

Here's what I see on master/18:

regression=# explain (analyze,buffers,costs off,timing off)
select * from t where k = 1;
                            QUERY PLAN
-------------------------------------------------------------------
 Index Only Scan using t_i_j_k_idx on t (actual rows=1.00 loops=1)
   Index Cond: (k = 1)
   Heap Fetches: 0
   Index Searches: 1
   Buffers: shared hit=38318
 Planning Time: 0.025 ms
 Execution Time: 152.502 ms
(7 rows)

Here's the same query on current REL_17_STABLE:

regression=# explain (analyze,buffers,costs off,timing off)
select * from t where k = 1;
                           QUERY PLAN
----------------------------------------------------------------
 Index Only Scan using t_i_j_k_idx on t (actual rows=1 loops=1)
   Index Cond: (k = 1)
   Heap Fetches: 0
   Buffers: shared hit=38318
 Planning Time: 0.042 ms
 Execution Time: 155.890 ms
(6 rows)

I used a regular/logged table for this. I repeatedly executed the
query, to minimize noise (I'm showing the last execution for each of
master and REL_17_STABLE).

For what it's worth, there *are* known regressions with more
complicated cases, most of which involve multiple inequality/range
conditions on multiple high cardinality columns. This was discussed
extensively over the course of work on skip scan -- they were deemed
acceptable. But I see no reason to believe that this particular query
will run any slower on 18.

FWIW, similar queries that don't have to scan the full index (which
makes an index-only scan attractive to the planner) are much faster on
Postgres 18, compared to 17. Here's one executed on 18:

regression=# explain (analyze,buffers,costs off,timing off)
select * from t where i between 1 and 500_000 and k = 1;
                            QUERY PLAN
-------------------------------------------------------------------
 Index Only Scan using t_i_j_k_idx on t (actual rows=1.00 loops=1)
   Index Cond: ((i >= 1) AND (i <= 500000) AND (k = 1))
   Heap Fetches: 0
   Index Searches: 1
   Buffers: shared hit=1919
 Planning:
   Buffers: shared hit=4
 Planning Time: 0.039 ms
 Execution Time: 7.141 ms
(9 rows)

Here's the same query executed on Postgres 17:

regression=# explain (analyze,buffers,costs off,timing off)
select * from t where i between 1 and 500_000 and k = 1;
                           QUERY PLAN
----------------------------------------------------------------
 Index Only Scan using t_i_j_k_idx on t (actual rows=1 loops=1)
   Index Cond: ((i >= 1) AND (i <= 500000) AND (k = 1))
   Heap Fetches: 0
   Buffers: shared hit=1919
 Planning:
   Buffers: shared hit=4
 Planning Time: 0.039 ms
 Execution Time: 10.771 ms
(8 rows)

--
Peter Geoghegan



Re: Possible regression in PG18 beta1

From
Sami Imseih
Date:
On Sat, May 17, 2025 at 2:00 PM Sadeq Dousti <msdousti@gmail.com> wrote:
>>
>> thanks. I don't see regression for a normal table, at least for this test.
>
>
> No, there isn't. I just added them as per your request ;)
>
>
>> In terms of your original test, I tried it out on my Ubuntu machine
>>
>> and with your test as-is, I see 2.8 seconds on 17.5 and 3.3 seconds
>>
>> on HEAD if the plan performs a seq scan without parallelism.
>
>
> Which is unexpected, no?

For the temp table test, it seems like I can account mostly all of the
extra time to the fact that checksums are enabled by default in 18,
due to 04bec894a04c

I ran the below script which runs the select 100 times against
the temp table on HEAD

```
drop table if exists t;

create TEMP table t(i,j,k)
as select n,n,n
from generate_series(1,10_000_000) as n;

analyze t;

create index on t(i,j,k);

SELECT 'select * from t where k = 1;' FROM generate_series(1, 100)
\gexec
```

and looked at perf top at the time, which shows
pg_checksum_block at the top using a cluster that was created
with initdb without any flags.

```
  12.12%  postgres   [.] pg_checksum_block
  11.97%  postgres   [.] ExecInterpExpr
  10.57%  postgres   [.] slot_deform_heap_tuple_internal
   5.90%  postgres   [.] fetch_att
   4.18%  postgres   [.] heapgettup_pagemode
```

and explain analyze for a single execution
```
test=# EXPLAIN (ANALYZE, timing on) select * from t where k = 1;
                                              QUERY PLAN
-------------------------------------------------------------------------------------------------------
 Seq Scan on t  (cost=0.00..179080.00 rows=1 width=12) (actual
time=0.065..3375.125 rows=1.00 loops=1)
   Filter: (k = 1)
   Rows Removed by Filter: 9999999
   Buffers: local read=54080
 Planning Time: 0.090 ms
 Execution Time: 3375.149 ms
(6 rows)
```

Now, with initdb and --no-data-checksums
```
  13.32%  postgres   [.] ExecInterpExpr
  12.44%  postgres   [.] slot_deform_heap_tuple_internal
   6.64%  postgres   [.] fetch_att
   4.70%  postgres   [.] heapgettup_pagemode
   4.22%  postgres   [.] slot_deform_heap_tuple
   3.75%  postgres   [.] TupleDescCompactAttr
```
and explain for a single execution
```
test=# EXPLAIN (ANALYZE, timing on) select * from t where k = 1;
                                              QUERY PLAN
-------------------------------------------------------------------------------------------------------
 Seq Scan on t  (cost=0.00..179080.00 rows=1 width=12) (actual
time=0.043..2939.101 rows=1.00 loops=1)
   Filter: (k = 1)
   Rows Removed by Filter: 9999999
   Buffers: local read=54080
 Planning Time: 0.087 ms
 Execution Time: 2939.125 ms
(6 rows)
```
v18 with --no-data-checksums gives me close performance to v17

Can you try the same test ( with --no-data-checksums) on you mac
and see if that makes a difference?

[0] states "Only data pages are protected by checksums; internal data
structures and temporary files are not."

Is what is observed with temp files being protected by checksums correct?

[0] https://www.postgresql.org/docs/current/checksums.html

--
Sami



Re: Possible regression in PG18 beta1

From
Sami Imseih
Date:

I'm now thinking maybe the compilation options for PG 17.5 have been different. I'm using the default version that comes with Homebrew, but for PG 18, I compiled it myself. Here are the results for `select version();` on both:

PostgreSQL 17.5 (Homebrew) on aarch64-apple-darwin24.4.0, 
compiled by Apple clang version 17.0.0 (clang-1700.0.13.3), 64-bit

In my tests I build from source from both the 17 stable and head branch 
and use the same build options. 

Why temp uses checksum, which also happens on 17 and presumably 
earlier versions, is still not too clear to me. 

Sami