diff --git a/.vscode/cspell.dictionaries/workspace.wordlist.txt b/.vscode/cspell.dictionaries/workspace.wordlist.txt index 72f2d95f11e..04a1402158b 100644 --- a/.vscode/cspell.dictionaries/workspace.wordlist.txt +++ b/.vscode/cspell.dictionaries/workspace.wordlist.txt @@ -367,6 +367,7 @@ uutils # * function names getcwd +setpipe # * other getlimits diff --git a/src/uu/cat/src/splice.rs b/src/uu/cat/src/splice.rs index a8b01e22605..14e88dd6e44 100644 --- a/src/uu/cat/src/splice.rs +++ b/src/uu/cat/src/splice.rs @@ -7,9 +7,8 @@ use super::{CatResult, FdReadable, InputHandle}; use rustix::io::{read, write}; use std::os::{fd::AsFd, unix::io::AsRawFd}; -use uucore::pipes::{pipe, splice, splice_exact}; +use uucore::pipes::{MAX_ROOTLESS_PIPE_SIZE, pipe, splice, splice_exact}; -const SPLICE_SIZE: usize = 1024 * 128; const BUF_SIZE: usize = 1024 * 16; /// This function is called from `write_fast()` on Linux and Android. The @@ -24,27 +23,40 @@ pub(super) fn write_fast_using_splice( handle: &InputHandle, write_fd: &S, ) -> CatResult { - let (pipe_rd, pipe_wr) = pipe()?; - - loop { - match splice(&handle.reader, &pipe_wr, SPLICE_SIZE) { - Ok(n) => { - if n == 0 { - return Ok(false); + const FIRST_PIPE_SIZE: usize = 64 * 1024; + if splice(&handle.reader, &write_fd, FIRST_PIPE_SIZE).is_ok() { + // fcntl improves performance for large file which is large overhead for small files + let _ = rustix::pipe::fcntl_setpipe_size(write_fd, MAX_ROOTLESS_PIPE_SIZE); + loop { + match splice(&handle.reader, &write_fd, MAX_ROOTLESS_PIPE_SIZE) { + Ok(1..) => {} + Ok(0) => return Ok(false), + Err(_) => return Ok(true), + } + } + } else { + // output is not pipe. Needs broker to use splice() which is high cost for small files + let (pipe_rd, pipe_wr) = pipe()?; + loop { + match splice(&handle.reader, &pipe_wr, MAX_ROOTLESS_PIPE_SIZE) { + Ok(n) => { + if n == 0 { + return Ok(false); + } + if splice_exact(&pipe_rd, write_fd, n).is_err() { + // If the first splice manages to copy to the intermediate + // pipe, but the second splice to stdout fails for some reason + // we can recover by copying the data that we have from the + // intermediate pipe to stdout using normal read/write. Then + // we tell the caller to fall back. + copy_exact(&pipe_rd, write_fd, n)?; + return Ok(true); + } } - if splice_exact(&pipe_rd, write_fd, n).is_err() { - // If the first splice manages to copy to the intermediate - // pipe, but the second splice to stdout fails for some reason - // we can recover by copying the data that we have from the - // intermediate pipe to stdout using normal read/write. Then - // we tell the caller to fall back. - copy_exact(&pipe_rd, write_fd, n)?; + Err(_) => { return Ok(true); } } - Err(_) => { - return Ok(true); - } } } } diff --git a/src/uucore/src/lib/features/pipes.rs b/src/uucore/src/lib/features/pipes.rs index 776cf2711aa..2615912c08d 100644 --- a/src/uucore/src/lib/features/pipes.rs +++ b/src/uucore/src/lib/features/pipes.rs @@ -4,21 +4,26 @@ // file that was distributed with this source code. //! Thin zero-copy-related wrappers around functions from the `rustix` crate. + +#[cfg(any(target_os = "linux", target_os = "android"))] +use rustix::pipe::{SpliceFlags, fcntl_setpipe_size}; #[cfg(any(target_os = "linux", target_os = "android"))] use std::fs::File; #[cfg(any(target_os = "linux", target_os = "android"))] use std::os::fd::AsFd; - -#[cfg(any(target_os = "linux", target_os = "android"))] -use rustix::pipe::SpliceFlags; +pub const MAX_ROOTLESS_PIPE_SIZE: usize = 1024 * 1024; /// A wrapper around [`rustix::pipe::pipe`] that ensures the pipe is cleaned up. /// /// Returns two `File` objects: everything written to the second can be read /// from the first. +/// This is used only for resolving the limitation for splice: one of a input or output should be pipe #[cfg(any(target_os = "linux", target_os = "android"))] pub fn pipe() -> std::io::Result<(File, File)> { let (read, write) = rustix::pipe::pipe()?; + // improve performance for splice + let _ = fcntl_setpipe_size(&read, MAX_ROOTLESS_PIPE_SIZE); + Ok((File::from(read), File::from(write))) }