Re: Expanding HOT updates for expression and partial indexes

From: Greg Burd <greg(at)burd(dot)me>
To: pgsql-hackers(at)postgresql(dot)org
Cc: greg(at)burd(dot)me
Subject: Re: Expanding HOT updates for expression and partial indexes
Date: 2026-10-23 16:05:17
Message-ID: y5Lpi3Xhb6mwYUiabFvbjjna67H6JbQtBWWqy27mqQ00v_QPqc7FEm1b4LD9eWyqey9tJoU1KcNK6eeBg8ddMbwgVQ_-qgI34JECCz9mDIY=@burd.me
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hello,

Attached is v44, eight patches on master (1910daf9831). It does the
two things v43's mail left open.

0008 hashcomparedatums() no longer hashes when it already knows the
answer. The executor asks it only about values that differ once
detoasted, and when the type's equality implies image equality
(its default btree operator class's equalimage function, for the
index's collation), two such values are unequal and share a hash
code only by collision. So it says "different" at once. That is
integers, text under a deterministic collation, uuid, bytea, dates
and times; numeric, float, interval, arrays and nondeterministic
collations are still hashed, which is where the callback earns its
keep. The answer is looked up once per statement and kept in
ii_AmCache. The exclusion constraint test now uses interval spans
of 1 and 2^32 microseconds, which collide in hashint8() as 1 and
2^32 did, because int8 is no longer asked at all.

0002 simple_heap_update() puts the new tuple in a slot next to the old
one and compares them with table_slot_modified_idx_attrs(), the
executor's comparison. Slots deform each tuple once and only as
far as the last indexed column, so the heap_getattr() per indexed
column of both tuples, with its "XXX this is pretty inefficient",
is gone. The old loop stays only for the assert-enabled check in
heap_update().

Same benchmark as v43 (release builds, tmpfs, fsync and
full_page_writes off, CHECKPOINT before each run, median of 12), in ms:

master v43 v44 v44/master
value changes, same length
compressed, btree 1383 1458 1462 +5.7%
out of line, btree 3338 3442 3410 +2.2%
out of line, hash 1272 1481 1315 +3.4%
value changes, different length
compressed, btree 6050 6173 6094 +0.7%
same value written back
compressed, btree 2944 226 227 -92.3%
out of line, btree 2121 1217 1228 -42.1%
compressed, SP-GiST 657 230 232 -64.7%
out of line, hash 693 642 647 -6.6%
controls
unindexed column changes 358 356 352 -1.7%
short indexed value changes 2068 2088 2086 +0.9%

The hash case went from +16.5% to +3.4%: what is left is detoasting the
old value once to find that it changed. The rest moved within noise.

Catalog updates all go through simple_heap_update(). On a loop of
five catalog-updating DDL statements per transaction, the backend runs
0.6% more instructions than with v43 (1.7% more than master, where v43
was 1.0% more), for the second slot; the time is within run-to-run
noise. That is what the O(n) comparison costs for catalogs, whose
indexed columns come first and are fixed-width, so heap_getattr() was
cheap for them.

best.

-greg

Attachment Content-Type Size
v44-0005-Add-amcomparedatums-index-AM-callback-for-HOT-up.patch text/x-patch 42.7 KB
v44-0003-Allow-HOT-updates-when-expression-and-partial-in.patch text/x-patch 41.5 KB
v44-0001-Improve-test-coverage-for-heap-only-tuple-HOT-up.patch text/x-patch 63.9 KB
v44-0007-GIN-implement-amcomparedatums.patch text/x-patch 12.7 KB
v44-0006-Treat-an-indexed-value-stored-differently-but-un.patch text/x-patch 23.9 KB
v44-0008-hash-implement-amcomparedatums.patch text/x-patch 13.9 KB
v44-0002-Identify-modified-indexed-attributes-in-the-exec.patch text/x-patch 52.9 KB
v44-0004-Skip-evaluating-indexes-that-an-UPDATE-s-SET-cla.patch text/x-patch 96.9 KB

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Greg Burd 2026-10-23 16:14:33 Re: Adding basic NUMA awareness
Previous Message Dongpo Liu 2026-10-23 15:54:52 Re: pg_*_advice: tsv load failure, etc.