| From: | Japin Li <japinli(at)hotmail(dot)com> |
|---|---|
| To: | Álvaro Herrera <alvherre(at)kurilemu(dot)de> |
| Cc: | pgsql-hackers <pgsql-hackers(at)lists(dot)postgresql(dot)org> |
| Subject: | Re: Reduce padding in WALOpenSegment and XLogReaderState |
| Date: | 2026-08-17 05:08:23 |
| Message-ID: | SY7PR01MB10921EFBB9CBE0AEEB3159F1FB6A72@SY7PR01MB10921.ausprd01.prod.outlook.com |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-hackers |
On Fri, 14 Aug 2026 at 12:28, Álvaro Herrera <alvherre(at)kurilemu(dot)de> 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.
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Amit Kapila | 2026-08-17 05:52:04 | Re: Logical replication row filter loses unchanged toasted columns |
| Previous Message | Bertrand Drouvot | 2026-08-17 04:43:07 | Re: pgstat: Flush some statistics within running transactions, take 2 |