Skip to content

Commit 5af7bab

Browse files
committed
tail: Performace improvements
1 parent 5a0988c commit 5af7bab

File tree

1 file changed

+37
-31
lines changed

1 file changed

+37
-31
lines changed

src/uu/tail/src/tail.rs

Lines changed: 37 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
// For the full copyright and license information, please view the LICENSE
44
// file that was distributed with this source code.
55

6-
// spell-checker:ignore (ToDO) seekable seek'd tail'ing ringbuffer ringbuf unwatch Uncategorized filehandle Signum
6+
// spell-checker:ignore (ToDO) seekable seek'd tail'ing ringbuffer ringbuf unwatch
7+
// spell-checker:ignore (ToDO) Uncategorized filehandle Signum memrchr
78
// spell-checker:ignore (libs) kqueue
89
// spell-checker:ignore (acronyms)
910
// spell-checker:ignore (env/flags)
@@ -24,11 +25,12 @@ pub use args::uu_app;
2425
use args::{FilterMode, Settings, Signum, parse_args};
2526
use chunks::ReverseChunks;
2627
use follow::Observer;
28+
use memchr::{memchr_iter, memrchr_iter};
2729
use paths::{FileExtTail, HeaderPrinter, Input, InputKind, MetadataExtTail};
2830
use same_file::Handle;
2931
use std::cmp::Ordering;
3032
use std::fs::File;
31-
use std::io::{self, BufRead, BufReader, BufWriter, Read, Seek, SeekFrom, Write, stdin, stdout};
33+
use std::io::{self, BufReader, BufWriter, ErrorKind, Read, Seek, SeekFrom, Write, stdin, stdout};
3234
use std::path::{Path, PathBuf};
3335
use uucore::display::Quotable;
3436
use uucore::error::{FromIo, UResult, USimpleError, get_exit_code, set_exit_code};
@@ -293,26 +295,29 @@ fn forwards_thru_file<R>(
293295
where
294296
R: Read,
295297
{
296-
let mut reader = BufReader::new(reader);
297-
298-
let mut buf = vec![];
298+
if num_delimiters == 0 {
299+
return Ok(0);
300+
}
301+
// Use a 32K buffer.
302+
let mut buf = [0; 32 * 1024];
299303
let mut total = 0;
300-
for _ in 0..num_delimiters {
301-
match reader.read_until(delimiter, &mut buf) {
302-
Ok(0) => {
303-
return Ok(total);
304-
}
304+
let mut count = 0;
305+
loop {
306+
match reader.read(&mut buf) {
307+
Ok(0) => return Ok(total),
305308
Ok(n) => {
309+
for offset in memchr_iter(delimiter, &buf[..n]) {
310+
count += 1;
311+
if count == num_delimiters {
312+
return Ok(total + offset + 1);
313+
}
314+
}
306315
total += n;
307-
buf.clear();
308-
continue;
309-
}
310-
Err(e) => {
311-
return Err(e);
312316
}
317+
Err(e) if e.kind() == ErrorKind::Interrupted => continue,
318+
Err(e) => return Err(e),
313319
}
314320
}
315-
Ok(total)
316321
}
317322

318323
/// Iterate over bytes in the file, in reverse, until we find the
@@ -322,35 +327,36 @@ fn backwards_thru_file(file: &mut File, num_delimiters: u64, delimiter: u8) {
322327
// This variable counts the number of delimiters found in the file
323328
// so far (reading from the end of the file toward the beginning).
324329
let mut counter = 0;
325-
326-
for (block_idx, slice) in ReverseChunks::new(file).enumerate() {
330+
let mut first_slice = true;
331+
for slice in ReverseChunks::new(file) {
327332
// Iterate over each byte in the slice in reverse order.
328-
let mut iter = slice.iter().enumerate().rev();
333+
let mut iter = memrchr_iter(delimiter, &slice);
329334

330335
// Ignore a trailing newline in the last block, if there is one.
331-
if block_idx == 0 {
336+
if first_slice {
332337
if let Some(c) = slice.last() {
333338
if *c == delimiter {
334339
iter.next();
335340
}
336341
}
342+
first_slice = false;
337343
}
338344

339345
// For each byte, increment the count of the number of
340346
// delimiters found. If we have found more than the specified
341347
// number of delimiters, terminate the search and seek to the
342348
// appropriate location in the file.
343-
for (i, ch) in iter {
344-
if *ch == delimiter {
345-
counter += 1;
346-
if counter >= num_delimiters {
347-
// After each iteration of the outer loop, the
348-
// cursor in the file is at the *beginning* of the
349-
// block, so seeking forward by `i + 1` bytes puts
350-
// us right after the found delimiter.
351-
file.seek(SeekFrom::Current((i + 1) as i64)).unwrap();
352-
return;
353-
}
349+
for i in iter {
350+
counter += 1;
351+
if counter >= num_delimiters {
352+
// We should never over-count - assert that.
353+
assert_eq!(counter, num_delimiters);
354+
// After each iteration of the outer loop, the
355+
// cursor in the file is at the *beginning* of the
356+
// block, so seeking forward by `i + 1` bytes puts
357+
// us right after the found delimiter.
358+
file.seek(SeekFrom::Current((i + 1) as i64)).unwrap();
359+
return;
354360
}
355361
}
356362
}

0 commit comments

Comments
 (0)