Re: More speedups for tuple deformation

From: David Rowley <dgrowleyml(at)gmail(dot)com>
To: Chao Li <li(dot)evan(dot)chao(at)gmail(dot)com>
Cc: PostgreSQL Developers <pgsql-hackers(at)lists(dot)postgresql(dot)org>
Subject: Re: More speedups for tuple deformation
Date: 2026-01-20 00:11:55
Message-ID: CAApHDvqhbJU_-yF3Hbf4VhX33qXtpeYv3MsvMPDMcDwGGLr9ZQ@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

On Mon, 19 Jan 2026 at 18:48, Chao Li <li(dot)evan(dot)chao(at)gmail(dot)com> wrote:
> I reviewed the patch and traced some basic workflows. But I haven’t done a load test to compare performance differences with and without this patch, I will do that if I get some bandwidth later. Here comes some review comments:
>
> 1 - tupmacs.h
> ```
> + /* Create a mask with all bits beyond natts's bit set to off */
> + mask = 0xFF & ((((uint8) 1) << (natts & 7)) - 1);
> + byte = (~bits[lastByte]) & mask;
> ```
>
> When I read the code, I got an impression bits[lastByte] might overflow when natts % 8 == 0, so I traced the code, then I realized that, this function is only called when a row has null values, so that, when reaching here, natts % 8 != 0, otherwise it should return earlier within the for loop.

It certainly is possible to get to that part of the code when natts is
a multiple of 8 and the tuple contains NULLs after that (we may not be
deforming the entire tuple). The code you quoted that's setting "mask"
in that case will produce a zero mask, resulting in not finding any
NULLs. I don't quite see any risk of overflowing any of the types
here. If natts is 16 then effectively the code does 0xFF & ((1 << 0)
- 1); so no overflow. Just left shift by 0 bits and bitwise AND with
zero, resulting in the mask becoming zero.

How about if I write the comment as follows?

/*
* Create a mask with all bits beyond natts's bit set to off. The code
* below will generate a zero mask when natts & 7 == 0. When that happens
* all bytes that need to be checked were done so in the loop above. The
* code below will create an empty mask and end up returning natts. This
* has been done to avoid having to write a special case to check if we've
* covered all bytes already.
*/

> In TupleDescFinalize(), given firstNonCachedOffAttr = i + 1, firstNonCachedOffAttr will never be 0.
> But in nocachegetattr(), it checks firstNonCachedOffAttr >= 0:
> This is kinda inconsistent, and may potentially lead to some confusion to code readers.

I've changed things around so that firstNonCachedOffAttr == 0 means
there are no attributes with cached offsets. -1 becomes uninitialised.
I've added Asserts to check for firstNonCachedOffAttr >= 0 with a
comment directing anyone who's facing debugging one of those failing
to add the missing call to TupleDescFinalize().

Thanks for reviewing.

I've attached the v4 patch, which also fixes the LLVM compiler warning
that I introduced.

David

Attachment Content-Type Size
v4-0001-Precalculate-CompactAttribute-s-attcacheoff.patch text/plain 74.9 KB

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Tatsuo Ishii 2026-01-20 00:37:04 Re: Row pattern recognition
Previous Message Henson Choi 2026-01-19 23:51:30 Re: Row pattern recognition