On Fri, 14 Aug 2026 at 12:28, Álvaro Herrera <[email protected]> wrote:
> Hi
>
> On 2026-Aug-14, Japin Li wrote:
>
>> Hi, hackers
>> 
>> While reviewing patch [1], I noticed that we can reduce padding in both
>> WALOpenSegment and XLogReaderState by reordering a few fields.
>
> What do you think of the idea of changing segcxt so that the directory
> name can be allocated to the length of the given directory instead of
> the full MAXPGPATH?  We could make ws_dir a pointer that goes just
> beyond the end of the struct's sizeof, so that it's all still a single
> palloc chunk.  That would probably be a more significant memory savings
> improvement.

Yes, in this way, it can reduce the XLogReaderState to 280 bytes.

(gdb) ptype/o XLogReaderState
type = struct XLogReaderState {
/*      0      |      24 */    XLogReaderRoutine routine;
/*     24      |       8 */    uint64 system_identifier;
/*     32      |       8 */    void *private_data;
/*     40      |       8 */    XLogRecPtr ReadRecPtr;
/*     48      |       8 */    XLogRecPtr EndRecPtr;
/*     56      |       8 */    XLogRecPtr abortedRecPtr;
/*     64      |       8 */    XLogRecPtr missingContrecPtr;
/*     72      |       8 */    XLogRecPtr overwrittenRecPtr;
/*     80      |       8 */    XLogRecPtr DecodeRecPtr;
/*     88      |       8 */    XLogRecPtr NextRecPtr;
/*     96      |       8 */    XLogRecPtr PrevRecPtr;
/*    104      |       8 */    DecodedXLogRecord *record;
/*    112      |       8 */    char *decode_buffer;
/*    120      |       8 */    size_t decode_buffer_size;
/*    128      |       1 */    _Bool free_decode_buffer;
/* XXX  7-byte hole      */
/*    136      |       8 */    char *decode_buffer_head;
/*    144      |       8 */    char *decode_buffer_tail;
/*    152      |       8 */    DecodedXLogRecord *decode_queue_head;
/*    160      |       8 */    DecodedXLogRecord *decode_queue_tail;
/*    168      |       8 */    char *readBuf;
/*    176      |       4 */    uint32 readLen;
/*    180      |       4 */    uint32 segoff;
/*    184      |      16 */    WALSegmentContext segcxt;
/*    200      |      16 */    WALOpenSegment seg;
/*    216      |       8 */    XLogRecPtr latestPagePtr;
/*    224      |       4 */    TimeLineID latestPageTLI;
/*    228      |       4 */    TimeLineID currTLI;
/*    232      |       8 */    XLogRecPtr currRecPtr;
/*    240      |       8 */    XLogRecPtr currTLIValidUntil;
/*    248      |       4 */    TimeLineID nextTLI;
/*    252      |       4 */    uint32 readRecordBufSize;
/*    256      |       8 */    char *readRecordBuf;
/*    264      |       8 */    char *errormsg_buf;
/*    272      |       1 */    _Bool errormsg_deferred;
/*    273      |       1 */    _Bool nonblocking;
/* XXX  6-byte padding   */

                               /* total size (bytes):  280 */
                             }

> (Now, xlogreader is not allocated in huge numbers, so
> saving a few hundred bytes is not terribly exciting in any case.  But
> maybe there are cases where XLogReadRecord shows in profile enough to
> make it useful for XLogReaderState to fit better in CPU caches?  I kinda
> doubt this whole thing is worth it, but maybe you know of some.)
>

I ran some performance profiling with perf stat over 100 iterations on
pg_waldump to see if reducing padding in XLogReaderState yields any
micro-architectural benefits.

Test command:

sudo perf -e 
cycles,instructions,cache-references,cache-misses,L1-dcache-loads,L1-dcache-load-misses
 \
    bash -c 'for ((i=0; i<100; i++)); do 
/home/japin/Codes/pg/main/build/pg/bin/pg_waldump -q -p 
/home/japin/Codes/pg/main/build/pg/pgdata/pg_wal/ 000000010000000000000005 
>/dev/null; done'

Test result:

| Metric                | Origin         | Patched        | Difference |
|-----------------------|----------------|----------------|------------|
| cycles                | 6,820,905,346  | 6,897,331,050  | +1.12%     |
| instructions          | 14,363,106,054 | 14,382,105,941 | +0.13%     |
| L1-dcache-loads       | 3,268,517,014  | 3,261,197,724  | -0.22%     |
| L1-dcache-load-misses | 41,538,222     | 40,368,624     | -2.82%     |
| cache-misses          | 40,057,839     | 39,882,224     | -0.44%     |
| elapsed time          | 2.0646 s       | 2.0648 s       | +0.008%    |
| user+sys time         | 2.06699 s      | 2.06685 s      | -0.007%    |

L1-dcache-load-misses dropped by ~2.8% (from 41.5M down to40.3M), and overall
cache-references decreased by ~2.0%. This confirms that making the struct
tighter improves spatial locality and L1 cache line utilization.

Total execution time shows virtually no macro-level difference (~2.064s for 
both).

As you suspected, because XLogReaderState is not allocated in high volume like
tuples or expression contexts, this cache improvement doesn't translate into
measurable end-to-end throughput gains for pg_waldump.

> Regards
>
> -- 
> Álvaro Herrera               48°01'N 7°57'E  —  https://www.EnterpriseDB.com/

-- 
Regards,
Japin Li
ChengDu WenWu Information Technology Co., Ltd.


Reply via email to