Skip to content

Commit 8fe8078

Browse files
committed
tee: improve throughput by raw syscall
1 parent 485b156 commit 8fe8078

3 files changed

Lines changed: 40 additions & 17 deletions

File tree

src/uu/tee/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@ doctest = false
2121

2222
[dependencies]
2323
clap = { workspace = true }
24-
uucore = { workspace = true, features = ["libc", "parser", "signals"] }
24+
rustix = { workspace = true, features = ["stdio", "fs"] }
25+
uucore = { workspace = true, features = ["fs", "libc", "parser", "signals"] }
2526
fluent = { workspace = true }
2627

2728
[dev-dependencies]
2829
divan = { workspace = true }
29-
rustix = { workspace = true, features = ["stdio", "fs"] }
3030
tempfile = { workspace = true }
3131
uucore = { workspace = true, features = ["benchmark"] }
3232

src/uu/tee/src/tee.rs

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -197,21 +197,16 @@ impl MultiWriter {
197197

198198
fn write_flush(&mut self, buf: &[u8]) -> Result<()> {
199199
let mode = self.output_error_mode;
200-
self.writers.retain_mut(|writer| {
201-
let res = (|| {
202-
writer.inner.write_all(buf)?;
203-
writer.inner.flush()
204-
})();
205-
match res {
200+
self.writers
201+
.retain_mut(|writer| match writer.inner.write_all(buf) {
206202
Ok(()) => true,
207203
Err(e) => {
208204
if let Err(e) = process_error(mode, e, writer, &mut self.ignored_errors) {
209205
self.aborted.get_or_insert(e);
210206
}
211207
false
212208
}
213-
}
214-
});
209+
});
215210
self.aborted.take().map_or(
216211
if self.writers.is_empty() {
217212
// This error kind will never be raised by the standard
@@ -254,18 +249,29 @@ enum Writer {
254249
Stdout(std::io::Stdout),
255250
}
256251

257-
impl Write for Writer {
258-
fn write(&mut self, buf: &[u8]) -> Result<usize> {
252+
// raw syscall avoids buffering which is POSIX requirement
253+
// throughput is better than flush after write_all by unknown reason
254+
#[cfg(any(unix, target_os = "wasi"))]
255+
impl Writer {
256+
pub fn write_all(&mut self, buf: &[u8]) -> Result<()> {
259257
match self {
260-
Self::File(f) => f.write(buf),
261-
Self::Stdout(s) => s.write(buf),
258+
Self::File(f) => uucore::io::write_all_raw(f, buf),
259+
Self::Stdout(s) => uucore::io::write_all_raw(s, buf),
262260
}
263261
}
262+
}
264263

265-
fn flush(&mut self) -> Result<()> {
264+
#[cfg(not(any(unix, target_os = "wasi")))]
265+
impl Writer {
266+
// override write_all to avoid calling many flush
267+
pub fn write_all(&mut self, buf: &[u8]) -> Result<()> {
266268
match self {
267-
Self::File(f) => f.flush(),
268-
Self::Stdout(s) => s.flush(),
269+
// File does not have line buffering
270+
Self::File(f) => f.write_all(buf),
271+
Self::Stdout(s) => {
272+
s.write_all(buf)?;
273+
s.flush() // todo: investigate how to remove flush overhead
274+
}
269275
}
270276
}
271277
}

src/uucore/src/lib/mods/io.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,24 @@ type NativeType = OwnedHandle;
3030
#[cfg(not(windows))]
3131
type NativeType = OwnedFd;
3232

33+
// io::write_all but no buffering
34+
#[inline]
35+
#[cfg(any(unix, target_os = "wasi"))]
36+
pub fn write_all_raw(output: impl AsFd, buf: &[u8]) -> io::Result<()> {
37+
let mut written = 0;
38+
let len = buf.len();
39+
while written < len {
40+
match rustix::io::write(&output, &buf[written..]) {
41+
Ok(n) => written += n,
42+
Err(e) if e.kind() != io::ErrorKind::Interrupted => return Err(e.into()),
43+
_ => {}
44+
}
45+
}
46+
Ok(())
47+
}
48+
3349
/// abstraction wrapper for native file handle / file descriptor
50+
// todo: remove clone introducing additional syscall dependency
3451
pub struct OwnedFileDescriptorOrHandle {
3552
fx: NativeType,
3653
}

0 commit comments

Comments
 (0)