Skip to content

Commit fea6807

Browse files
committed
yes: use tee syscall as pipe only fast-path
1 parent c6a57bb commit fea6807

File tree

6 files changed

+30
-4
lines changed

6 files changed

+30
-4
lines changed

.vscode/cspell.dictionaries/workspace.wordlist.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ advapi32-sys
88
aho-corasick
99
backtrace
1010
blake2b_simd
11+
rustix
1112

1213
# * uutils project
1314
uutils
@@ -361,6 +362,7 @@ uutils
361362

362363
# * function names
363364
getcwd
365+
setpipe
364366

365367
# * other
366368
weblate

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,7 @@ rstest = "0.26.0"
435435
rstest_reuse = "0.7.0"
436436
rustc-hash = "2.1.1"
437437
rust-ini = "0.21.0"
438+
rustix = "1.1.4"
438439
same-file = "1.0.6"
439440
self_cell = "1.0.4"
440441
selinux = "=0.6.0"

src/uu/yes/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ path = "src/yes.rs"
2222
clap = { workspace = true }
2323
itertools = { workspace = true }
2424
fluent = { workspace = true }
25+
rustix = { workspace = true, features = ["pipe"] }
2526

2627
[target.'cfg(unix)'.dependencies]
2728
uucore = { workspace = true, features = ["pipes", "signals"] }

src/uu/yes/src/yes.rs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,12 @@ use uucore::error::{UResult, USimpleError};
1313
use uucore::format_usage;
1414
use uucore::translate;
1515

16-
// it's possible that using a smaller or larger buffer might provide better performance on some
17-
// systems, but honestly this is good enough
16+
#[cfg(any(target_os = "linux", target_os = "android"))]
17+
const MAX_ROOTLESS_PIPE_SIZE: usize = 1024 * 1024;
18+
// todo: investigate best rate
19+
#[cfg(any(target_os = "linux", target_os = "android"))]
20+
const BUF_SIZE: usize = MAX_ROOTLESS_PIPE_SIZE;
21+
#[cfg(not(any(target_os = "linux", target_os = "android")))]
1822
const BUF_SIZE: usize = 16 * 1024;
1923

2024
#[uucore::main]
@@ -110,8 +114,24 @@ fn prepare_buffer(buf: &mut Vec<u8>) {
110114

111115
pub fn exec(bytes: &[u8]) -> io::Result<()> {
112116
let stdout = io::stdout();
113-
let mut stdout = stdout.lock();
117+
#[cfg(any(target_os = "linux", target_os = "android"))]
118+
{
119+
use rustix::io::write;
120+
use rustix::pipe::{SpliceFlags, fcntl_setpipe_size, pipe, tee};
121+
// fast-path for pipe. todo: port the fast-path for > file
122+
if let Ok((p_read, p_write)) = pipe() {
123+
let _ = fcntl_setpipe_size(&p_read, MAX_ROOTLESS_PIPE_SIZE);
124+
let _ = fcntl_setpipe_size(&p_write, MAX_ROOTLESS_PIPE_SIZE);
125+
let _ = fcntl_setpipe_size(&stdout, MAX_ROOTLESS_PIPE_SIZE);
126+
if write(&p_write, bytes).is_ok() {
127+
while let Ok(1..) = tee(&p_read, &stdout, MAX_ROOTLESS_PIPE_SIZE, SpliceFlags::MORE)
128+
{
129+
}
130+
}
131+
}
132+
}
114133

134+
let mut stdout = stdout.lock();
115135
loop {
116136
stdout.write_all(bytes)?;
117137
}
@@ -122,6 +142,7 @@ mod tests {
122142
use super::*;
123143

124144
#[test]
145+
#[cfg(not(any(target_os = "linux", target_os = "android")))] // Linux uses different buffer size
125146
fn test_prepare_buffer() {
126147
let tests = [
127148
(150, 16350),

tests/by-util/test_yes.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ fn test_long_input() {
7070
#[cfg(windows)]
7171
const TIMES: usize = 500;
7272
let arg = "abcdef".repeat(TIMES) + "\n";
73-
let expected_out = arg.repeat(30);
73+
let expected_out = arg.repeat(5);
7474
run(&[&arg[..arg.len() - 1]], expected_out.as_bytes());
7575
}
7676

0 commit comments

Comments
 (0)