Re: Let an ordering index scan hand its ORDER BY value to the target list

From: Greg Burd <greg(at)burd(dot)me>
To: Matthias van de Meent <boekewurm+postgres(at)gmail(dot)com>
Cc: PostgreSQL Hackers <pgsql-hackers(at)lists(dot)postgresql(dot)org>, Heikki Linnakangas <hlinnaka(at)iki(dot)fi>, Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>, Chris Cleveland <ccleveland(at)dieselpoint(dot)com>
Subject: Re: Let an ordering index scan hand its ORDER BY value to the target list
Date: 2026-10-07 12:59:41
Message-ID: 3Wu1KkiywQwpYqvPWd8xjNR309g5IQI-fgYrjQsN33Cejf2chkSMvMJFw7kbfe7957nNt5UFMqq7_AMRGtz0t4hXwLDwk1x6VVKAr771YOA=@burd.me
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers


On Wednesday, October 7th, 2026 at 8:09 AM, Matthias van de Meent <boekewurm+postgres(at)gmail(dot)com> wrote:
>
> On Wed, 7 Oct 2026 at 13:44, Greg Burd <greg(at)burd(dot)me> wrote:
> >
> > On Tuesday, October 6th, 2026 at 5:57 PM, Matthias van de Meent <boekewurm+postgres(at)gmail(dot)com> wrote:
> > >
> > > On Tue, 6 Oct 2026 at 18:29, Greg Burd <greg(at)burd(dot)me> wrote:
> > > >
> > > > On Monday, October 5th, 2026 at 2:14 PM, Greg Burd <greg(at)burd(dot)me> wrote:
> > > > >
> > > > > I've not yet measured performance or memory difference due to this change,
> > > > > net should be better but I should quantify that at some point.
> > > >
> > > > I've run a test in EC2 to answer this question, details at the end but this
> > > > change is significant for some important shapes.
> [...]
> > > > What this means is a) this is a good performance win, and b) I now need to see
> > > > if it makes sense to adjust the costing of this pattern given that some shapes
> > > > are ~97% faster now.
> > > Do you have the details on what was tested to get to these results?
> > > And how it came to be that the results are 97% faster?
> > >
> > > The index still produces the projected distance output, which
> > > shouldn't be much cheaper for the index than a plan-level operator
> > > evaluation. Assuming 1 projection per row inside the index (which is
> > > reasonable for any ANN index I'm aware of) the theoretical savings are
> > > at most 66% (= 1 - (2/3), because you save one projection for
> > > xs_recheckorderby, and one projection for the plan node ORDER BY
> > > output column; the one inside the index remains). Or did I miss
> > > something -- is detoasting such an expensive factor here?
> >
> > Your model is right for an index that computes the distance at scan
> > time, which is what pgvector's HNSW does. The 97% case is an expression
> > index:
> >
> > CREATE INDEX ON vecs USING gist (point(vdist(v, qv()), 0));
> > SELECT id, point(vdist(v, qv()), 0) <-> point(0,0) FROM vecs
> > ORDER BY point(vdist(v, qv()), 0) <-> point(0,0) LIMIT 10000;
> >
> > vdist() is a 128-dim SQL L2 distance. It runs once per row at CREATE
> > INDEX and the result is stored; at scan time GiST only computes point
> > <-> point on the stored key. So the index's "projection" is the cheap
> > operator, and the expensive function is evaluated zero times instead of
> > once per returned row. The cost moved to build time, it didn't go
> > away. For an AM that computes the expensive distance in amgettuple,
> > expect your 1/3 to 2/3 depending on recheck, not 97%. I've posted the
> > script and raw CSV if anyone wants to rerun them in a gist.
> >
> > https://gist.github.com/gburd/3f6acd95583845f4924a548167a58e07
> >
> > In hindsight that was not a fair statistic to quote, the measure should
> > have been limited to the impact of the change. Benchmarking is hard,
> > and this time I wasn't trying to cheat, but I guess that 97% shouldn't
> > have been the lead or it should have been made clear what I'd measured.
>
> No, I do think that it is very important to keep this in mind, too:
> We don't just avoid evaluating the possibly expensive distance up to 2
> more times per row than necessary, but also avoid re-projecting the
> index column definition itself (which clearly can be extremely
> costly).
>
> That column projection is something I'd mostly overlooked when trying
> to figure out the performance difference: Whilst detoasting is a part
> in that re-projection cost, it is not necessarily the most expensive
> part of re-evaluating the full order-by expression.

Agreed, and thanks: I undersold it. The expression-index result is real:
the patch avoids re-projecting the indexed expression, and that' is the
expensive part there.

>
> Kind regards,
>
> Matthias van de Meent
> Databricks (https://www.databricks.com)
>

Two corrections to my last mail. 0001 does add an index-AM callback,
amcanreturnorderby (it doesn't touch the table AM). And it doesn't remove
a recheck: on a rechecked tuple it reuses the recomputed value. For the
opclasses that qualify today, recheck is always false, so the saving is
one projection per row.

I also found that my vector generator repeated a single vector across all
200k rows (an uncorrelated scalar subquery), so the vec_* rows were
measured on degenerate data. The slow_* rows don't have that problem.
I'll rerun with the harness fixed and post the corrected numbers.

best.

-greg

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Amit Langote 2026-10-07 13:02:13 Re: Two more RI fast-path issues
Previous Message Amit Langote 2026-10-07 12:40:13 Re: PG19: two RI fast-path issues found while testing the batching revert