Re: [PATCH] btrfs: zstd: avoid a copy in zstd_decompress_bio()

From: David Sterba

Date: Mon Sep 07 2026 - 08:18:39 EST


On Fri, Sep 04, 2026 at 09:41:48AM -0700, Usama Arif wrote:
> zstd_decompress_bio() gives zstd a sectorsize-sized scratch buffer, and
> btrfs_decompress_buf2page() then copies the part overlapping the read bio
> into the destination folios. Every delivered byte is written twice.
>
> Instead, choose the output buffer per streaming call. zstd_map_dest()
> kmaps the current page-bounded segment of the read bio, so zstd writes
> into the page cache directly. The scratch buffer is kept only for output
> with no destination: the prefix before a read starting inside a
> compressed extent, which zstd cannot skip, and gaps left by folios
> already in the page cache.
>
> Varying the output buffer across calls is safe: btrfs uses the default
> ZSTD_bm_buffered mode, where the sliding window lives in the dstream's
> internal buffer and the caller's dst is a pure sink. The read bio's
> iterator must still advance by exactly the bytes delivered, since
> btrfs_decompress_bio() zero-fills from it; that used to happen inside
> btrfs_decompress_buf2page() and is now an explicit bio_advance(), made
> only for output that reached a folio.
>
> bio_iter_iovec() exposes at most one base page, so direct output is
> page-bounded. Compared to the old sectorsize-sized chunks, this can
> increase stream calls when sectorsize exceeds PAGE_SIZE, but eliminates
> the extra btrfs copy for output delivered to the read bio; the 64 KiB
> sectorsize row below shows the copy still wins there.
>
> Benchmarked the change in 2-vCPU x86-64 KVM guests (4 KiB pages, RAM
> disk) using a 64 MiB zstd-compressed file. Results are medians of seven
> cold-cache reads in each of six interleaved A/B boot pairs; mincore
> confirmed zero resident pages before every run.
>
> Normal sequential reads with readahead produced:
>
> sectorsize base patched reduction
> 4 KiB 8.678 ms 8.004 ms 7.80%
> 16 KiB 8.216 ms 7.934 ms 3.64%
> 64 KiB 7.875 ms 7.344 ms 6.88%
>
> Random 4 KiB preads at 4 KiB sectorsize, means of six interleaved A/B
> boot pairs, patched better in all six:
>
> base patched gain
> 264.33 MB/s 272.67 MB/s 3.2%
>
> Signed-off-by: Usama Arif <usama.arif@xxxxxxxxx>

Nice, also good that it's measurable as it's a microoptimization. The
btrfs_decompress_buf2page() is there because of LZO which does not have
the internal copies. ZLIB uses it as well but I'm not sure if it's
needed and similar optimizaiotn could be done there as well. It's less
used because of zstd so we can keep it like that.

Reviewed-by: David Sterba <dsterba@xxxxxxxx>