Hi,

On Wed, Aug 12, 2026 at 6:31 PM Peipei YIN <[email protected]> wrote:
>
> Hi hackers,
>
> While reviewing relation extension paths, I noticed three places that still 
> manually perform zero-fill operations using smgrextend(), even though 
> smgrzeroextend() (and its underlying mdzeroextend()) exists specifically for 
> this purpose.
>
> The Problem
> Currently, RelationCopyStorageUsingBuffer(), smgr_bulk_flush(), and segment 
> padding in _mdfd_getseg() manually construct or loop over zeroed buffers to 
> extend relations. This bypasses the dedicated zero-extension interfaces, 
> leading to inconsistent code patterns. It also misses out on 
> platform-specific optimizations like posix_fallocate() or FileZero() that 
> mdzeroextend() leverages instead of relying on sparse-file hole semantics.
>
> The Fix
> The attached patch refactors these remaining instances to use 
> smgrzeroextend() (and mdzeroextend() directly in md.c):
>
> bufmgr.c: Replaces memset() + smgrextend() with a single smgrzeroextend() 
> call to bulk-extend the destination relation.
> bulk_write.c: Eliminates the file-scope zero_buffer and the while loop, 
> substituting a single smgrzeroextend() call to fill non-sequential write gaps 
> efficiently.
> md.c: Removes the aligned palloc_aligned zero-buffer allocation in 
> _mdfd_getseg(), replacing it with a direct call to mdzeroextend() to pad 
> short segments.
>
> This unifies all zero-fill relation extensions, improves code readability, 
> and ensures optimized zero-extension paths are used consistently across the 
> storage manager.

Thanks for sending the patch.

I read through the code and commits (4d330a61bb, f94e9141a0b) around
smgrextend and smgrzeroextend and did some experimentation.

@@ -5405,9 +5404,8 @@ RelationCopyStorageUsingBuffer(RelFileLocator srclocator,
  * Bulk extend the destination relation of the same size as the source
  * relation before starting to copy block by block.
  */
- memset(buf.data, 0, BLCKSZ);
- smgrextend(smgropen(dstlocator, INVALID_PROC_NUMBER), forkNum, nblocks - 1,
-   buf.data, true);
+ smgrzeroextend(smgropen(dstlocator, INVALID_PROC_NUMBER), forkNum, 0,
+   nblocks, true);

In the CREATE DATABASE path, smgrextend writes just one block with
zeros (out of N relation blocks only the last one gets written zero),
so this is already optimized today (which is why with posix_fallocate
the patch shows no visible benefit here). However, with smgrzeroextend
and file_extend_method=write_zeros (IOW, on platforms where
posix_fallocate doesn't exist), this patch increases the disk writes
(2x) and slows the query (2x).

I did a quick benchmark of CREATE DATABASE with a source database of
7.3GB (pgbench scale 500) on an m5d.4xlarge Amazon EC2 instance with a
gp3 (network-attached) data volume. Disk writes were captured from
/proc/diskstats sectors-written for the data device. Config used was
wal_level=minimal, fsync=on, checkpoint_timeout=24h, and small
vm.dirty_bytes=64MB / vm.dirty_background_bytes=32MB so dirty pages
are flushed mid-copy once they cross the threshold, rather than the
zero pages being overwritten in cache first.

I ran each configuration 3 times. The numbers below are the average
(variance was under 1%).

code                       exec time (s)   disk writes (GB)
HEAD                           59.2            7.3
patched, posix_fallocate       59.2            7.3
patched, write_zeros          119.1           14.6

I didn't dive deep into the other paths bulk_write.c and md.c changed
in this patch, so I'm not sure if they could also hit this problem.

--
Bharath Rupireddy
Amazon Web Services: https://aws.amazon.com


Reply via email to