Hi,

On Tue, Feb 10, 2026 at 01:15:01PM -0500, Andres Freund wrote:
> Hi,
> 
> On 2026-02-10 19:14:44 +0200, Heikki Linnakangas wrote:
> Yea, I don't think we need to be perfect here. Just a bit less bad. And, as
> you say, the current order doesn't make a lot of sense.
> Just grouping things like
> - pid, pgxactoff, backendType (i.e. barely if ever changing)
> - wait_event_info, waitStart (i.e. very frequently changing, but typically
>   accessed within one proc)
> - sem, lwWaiting, waitLockMode (i.e. stuff that is updated frequently and
>   accessed across processes)

With an ordering like in the attached (to apply on top of Heikki's patch), we're
back to 832 bytes.

But, then the pg_attribute_aligned() added in Heikki's patch makes it 896 
bytes...

"
/*    816      |      16 */    dlist_node lockGroupLink;
/* XXX 64-byte padding   */

                               /* total size (bytes):  896 */
                             }
"

What about applying this new ordering and remove the pg_attribute_aligned()? (I
thought the aligned attribute would be smarter than that and not add this 64 
padding
bytes).

Regards,

-- 
Bertrand Drouvot
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com
commit ea3aded2c4dec487c8a2ff5470cd7bae83e8dd47
Author: Bertrand Drouvot <[email protected]>
Date:   Tue Feb 10 18:54:29 2026 +0000

    reorder

diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index 53acce8a5a1..5d41301c966 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -184,29 +184,16 @@ typedef enum
  */
 typedef struct PGPROC
 {
-       dlist_node      links;                  /* list link if process is in a 
list */
-       dlist_head *procgloballist; /* procglobal list that owns this PGPROC */
-
-       PGSemaphore sem;                        /* ONE semaphore to sleep on */
-       ProcWaitStatus waitStatus;
-
-       Latch           procLatch;              /* generic latch for process */
-
-
-       TransactionId xid;                      /* id of top-level transaction 
currently being
-                                                                * executed by 
this proc, if running and XID
-                                                                * is assigned; 
else InvalidTransactionId.
-                                                                * mirrored in 
ProcGlobal->xids[pgxactoff] */
-
-       TransactionId xmin;                     /* minimal running XID as it 
was when we were
-                                                                * starting our 
xact, excluding LAZY VACUUM:
-                                                                * vacuum must 
not remove tuples deleted by
-                                                                * xid >= xmin 
! */
-
        int                     pid;                    /* Backend's process 
ID; 0 if prepared xact */
-
        int                     pgxactoff;              /* offset into various 
ProcGlobal->arrays with
                                                                 * data 
mirrored from this PGPROC */
+       BackendType backendType;        /* what kind of process is this? */
+
+       /* These fields are zero while a backend is still starting up: */
+       Oid                     databaseId;             /* OID of database this 
backend is using */
+       Oid                     roleId;                 /* OID of role using 
this backend */
+       Oid                     tempNamespaceId;        /* OID of temp schema 
this backend is
+                                                                        * 
using */
 
        /*
         * Currently running top-level transaction's virtual xid. Together these
@@ -227,24 +214,44 @@ typedef struct PGPROC
                                                                         * 
InvalidLocalTransactionId */
        }                       vxid;
 
-       /* These fields are zero while a backend is still starting up: */
-       Oid                     databaseId;             /* OID of database this 
backend is using */
-       Oid                     roleId;                 /* OID of role using 
this backend */
+       dlist_node      links;                  /* list link if process is in a 
list */
+       dlist_head *procgloballist; /* procglobal list that owns this PGPROC */
 
-       Oid                     tempNamespaceId;        /* OID of temp schema 
this backend is
-                                                                        * 
using */
+       uint32          wait_event_info;        /* proc's wait information */
+       pg_atomic_uint64 waitStart; /* time at which wait for lock acquisition
+                                                                * started */
 
-       BackendType backendType;        /* what kind of process is this? */
+       TransactionId xid;                      /* id of top-level transaction 
currently being
+                                                                * executed by 
this proc, if running and XID
+                                                                * is assigned; 
else InvalidTransactionId.
+                                                                * mirrored in 
ProcGlobal->xids[pgxactoff] */
 
-       /*
-        * While in hot standby mode, shows that a conflict signal has been sent
-        * for the current transaction. Set/cleared while holding ProcArrayLock,
-        * though not required. Accessed without lock, if needed.
-        *
-        * This is a bitmask; each bit corresponds to a RecoveryConflictReason
-        * enum value.
-        */
-       pg_atomic_uint32 pendingRecoveryConflicts;
+       TransactionId xmin;                     /* minimal running XID as it 
was when we were
+                                                                * starting our 
xact, excluding LAZY VACUUM:
+                                                                * vacuum must 
not remove tuples deleted by
+                                                                * xid >= xmin 
! */
+
+       ProcWaitStatus waitStatus;
+       int                     delayChkptFlags;        /* for DELAY_CHKPT_* 
flags */
+
+       LOCKMODE        waitLockMode;   /* type of lock we're waiting for */
+       LOCKMASK        heldLocks;              /* bitmask for lock types 
already held on this
+                                                                * lock object 
by this backend */
+
+       uint8           statusFlags;    /* this backend's status flags, see 
PROC_*
+                                                                * above. 
mirrored in
+                                                                * 
ProcGlobal->statusFlags[pgxactoff] */
+       XidCacheStatus subxidStatus;    /* mirrored with
+                                                                        * 
ProcGlobal->subxidStates[i] */
+
+       bool            procArrayGroupMember;   /* true, if member of ProcArray 
group
+                                                                               
 * waiting for XID clear */
+       bool            clogGroupMember;        /* true, if member of clog 
group */
+       bool            fpVXIDLock;             /* are we holding a fast-path 
VXID lock? */
+
+       LocalTransactionId fpLocalTransactionId;        /* lxid for fast-path 
VXID
+                                                                               
                 * lock */
+       PGSemaphore sem;                        /* ONE semaphore to sleep on */
 
        /*
         * Info about LWLock the process is currently waiting for, if any.
@@ -255,6 +262,19 @@ typedef struct PGPROC
         */
        uint8           lwWaiting;              /* see LWLockWaitState */
        uint8           lwWaitMode;             /* lwlock mode being waited for 
*/
+
+       Latch           procLatch;              /* generic latch for process */
+
+       /*
+        * While in hot standby mode, shows that a conflict signal has been sent
+        * for the current transaction. Set/cleared while holding ProcArrayLock,
+        * though not required. Accessed without lock, if needed.
+        *
+        * This is a bitmask; each bit corresponds to a RecoveryConflictReason
+        * enum value.
+        */
+       pg_atomic_uint32 pendingRecoveryConflicts;
+
        proclist_node lwWaitLink;       /* position in LW lock wait list */
 
        /* Support for condition variables. */
@@ -264,17 +284,6 @@ typedef struct PGPROC
        /* waitLock and waitProcLock are NULL if not currently waiting. */
        LOCK       *waitLock;           /* Lock object we're sleeping on ... */
        PROCLOCK   *waitProcLock;       /* Per-holder info for awaited lock */
-       LOCKMODE        waitLockMode;   /* type of lock we're waiting for */
-       LOCKMASK        heldLocks;              /* bitmask for lock types 
already held on this
-                                                                * lock object 
by this backend */
-       pg_atomic_uint64 waitStart; /* time at which wait for lock acquisition
-                                                                * started */
-
-       int                     delayChkptFlags;        /* for DELAY_CHKPT_* 
flags */
-
-       uint8           statusFlags;    /* this backend's status flags, see 
PROC_*
-                                                                * above. 
mirrored in
-                                                                * 
ProcGlobal->statusFlags[pgxactoff] */
 
        /*
         * Info to allow us to wait for synchronous replication, if needed.
@@ -293,15 +302,11 @@ typedef struct PGPROC
         */
        dlist_head      myProcLocks[NUM_LOCK_PARTITIONS];
 
-       XidCacheStatus subxidStatus;    /* mirrored with
-                                                                        * 
ProcGlobal->subxidStates[i] */
        struct XidCache subxids;        /* cache for subtransaction XIDs */
 
        /* Support for group XID clearing. */
-       /* true, if member of ProcArray group waiting for XID clear */
-       bool            procArrayGroupMember;
-       /* next ProcArray group member waiting for XID clear */
-       pg_atomic_uint32 procArrayGroupNext;
+       pg_atomic_uint32 procArrayGroupNext;    /* next ProcArray group member
+                                                                               
         * waiting for XID clear */
 
        /*
         * latest transaction id among the transaction's main XID and
@@ -309,10 +314,7 @@ typedef struct PGPROC
         */
        TransactionId procArrayGroupMemberXid;
 
-       uint32          wait_event_info;        /* proc's wait information */
-
        /* Support for group transaction status update. */
-       bool            clogGroupMember;        /* true, if member of clog 
group */
        pg_atomic_uint32 clogGroupNext; /* next clog group member */
        TransactionId clogGroupMemberXid;       /* transaction id of clog group 
member */
        XidStatus       clogGroupMemberXidStatus;       /* transaction status 
of clog
@@ -326,9 +328,6 @@ typedef struct PGPROC
        LWLock          fpInfoLock;             /* protects per-backend 
fast-path state */
        uint64     *fpLockBits;         /* lock modes held for each fast-path 
slot */
        Oid                *fpRelId;            /* slots for rel oids */
-       bool            fpVXIDLock;             /* are we holding a fast-path 
VXID lock? */
-       LocalTransactionId fpLocalTransactionId;        /* lxid for fast-path 
VXID
-                                                                               
                 * lock */
 
        /*
         * Support for lock groups.  Use LockHashPartitionLockByProc on the 
group

Reply via email to