[PATCH] io: allow filesystem st_blksize-directed buffer sizes up to 128k instead of 8k

Collin Funk collin.funk1@gmail.com
Mon Dec 29 02:27:15 GMT 2025


Hi наб,

наб <nabijaczleweli@nabijaczleweli.xyz> writes:

> The only difference between old and new being
>   setvbuf(input, (char *)malloc(128*1024), _IOFBF, 128*1024);
> with 128k matching zfs's st_blksize:
>   Benchmark 1: < f  out/cmd/head -n 290955
>     Time (mean ± σ):     153.4 ms ±   3.9 ms    [User: 78.0 ms, System: 75.3 ms]
>     Range (min … max):   146.5 ms … 159.2 ms    19 runs
>
>   Benchmark 2:  < f ./head.old -n 290955
>     Time (mean ± σ):     259.7 ms ±   5.7 ms    [User: 86.3 ms, System: 173.3 ms]
>     Range (min … max):   249.1 ms … 269.3 ms    11 runs
>
>   Summary
>     < f  out/cmd/head -n 290955 ran
>       1.69 ± 0.06 times faster than  < f ./head.old -n 290955
> (where the output is 290955 lines, 230427757 bytes and the program
>  reduces to a getdelim()/fwrite() loop which reduces to a read()/write()
>  loop).
>
> A value around 128k gets us around 60% of the performance back
> without being so large it should affect the memory pressure.
>
> Signed-off-by: Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
> ---
>  libio/filedoalloc.c | 11 ++++++++++-
>  1 file changed, 10 insertions(+), 1 deletion(-)
>
> diff --git libio/filedoalloc.c libio/filedoalloc.c
> index f360d96a9b..3a3d6f52d3 100644
> --- libio/filedoalloc.c
> +++ libio/filedoalloc.c
> @@ -61,6 +61,13 @@
>  #include <stdlib.h>
>  #include <unistd.h>
>  
> +/* nfs/cifs/cephfs request 512k/1M/4M. That is a /lot/ for an iostream.
> +   However, many other filesystems request reasonable values,
> +   and clamping them to BUFSIZ of 8k throws away a lot of performance:
> +   for a getdelim()/fwrite() loop at zfs's requested st_blksize of 128k,
> +   over a 220М file, completes in 60% of the time of when the buffer size is 8k. */
> +#define MAX_FS_BUFSIZ (128 * 1024)
> +
>  /* Allocate a file buffer, or switch to unbuffered I/O.  Streams for
>     TTY devices default to line buffered.  */
>  int
> @@ -84,7 +91,9 @@ _IO_file_doallocate (FILE *fp)
>  	    fp->_flags |= _IO_LINE_BUF;
>  	}
>  #if defined _STATBUF_ST_BLKSIZE
> -      if (st.st_blksize > 0 && st.st_blksize < BUFSIZ)
> +      if (st.st_blksize > MAX_FS_BUFSIZ)
> +        st.st_blksize = MAX_FS_BUFSIZ;
> +      if (st.st_blksize > 0)
>  	size = st.st_blksize;
>  #endif
>      }

Adding Pádraig and Paul to CC, who certainly know more about reasonable
buffer sizes than I do.

However, with some trivial testing I recall doing in Coreutils, large
buffers don't always have great throughput.

Here is a trivial example that was run once, so obviously not perfect:

    $ dd if=/dev/random of=input bs=1G count=10 status=none
    $ dd iflag=fullblock if=input bs=$(numfmt --from=iec 8k) \
        status=none | pv -r > /dev/null
    [ 639MiB/s]
    $ dd iflag=fullblock if=input bs=$(($(stat -c %o input) * 512)) \
        status=none | pv -r > /dev/null
    [ 576MiB/s]

That test was done on btrfs without any compression, for reference.

Collin


More information about the Libc-alpha mailing list