mirror of
https://github.com/bobwen-dev/hunter
synced 2025-04-12 00:55:41 +02:00
another day, another boost: 30% faster on linux with getdents64
This commit is contained in:
parent
f10f7d5e32
commit
963e0ae1cb
24
Cargo.lock
generated
24
Cargo.lock
generated
@ -214,6 +214,27 @@ name = "constant_time_eq"
|
|||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam"
|
||||||
|
version = "0.7.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-channel"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-deque"
|
name = "crossbeam-deque"
|
||||||
version = "0.7.2"
|
version = "0.7.2"
|
||||||
@ -594,6 +615,7 @@ dependencies = [
|
|||||||
"base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
"chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"crossbeam 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"derivative 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"derivative 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"dirs-2 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"dirs-2 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -1716,6 +1738,8 @@ dependencies = [
|
|||||||
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
||||||
"checksum color_quant 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0dbbb57365263e881e805dc77d94697c9118fd94d8da011240555aa7b23445bd"
|
"checksum color_quant 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0dbbb57365263e881e805dc77d94697c9118fd94d8da011240555aa7b23445bd"
|
||||||
"checksum constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
|
"checksum constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
|
||||||
|
"checksum crossbeam 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "69323bff1fb41c635347b8ead484a5ca6c3f11914d784170b158d8449ab07f8e"
|
||||||
|
"checksum crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "acec9a3b0b3559f15aee4f90746c4e5e293b701c0f7d3925d24e01645267b68c"
|
||||||
"checksum crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3aa945d63861bfe624b55d153a39684da1e8c0bc8fba932f7ee3a3c16cea3ca"
|
"checksum crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3aa945d63861bfe624b55d153a39684da1e8c0bc8fba932f7ee3a3c16cea3ca"
|
||||||
"checksum crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5064ebdbf05ce3cb95e45c8b086f72263f4166b29b97f6baff7ef7fe047b55ac"
|
"checksum crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5064ebdbf05ce3cb95e45c8b086f72263f4166b29b97f6baff7ef7fe047b55ac"
|
||||||
"checksum crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db"
|
"checksum crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db"
|
||||||
|
@ -45,6 +45,7 @@ derivative = "1.0.3"
|
|||||||
itertools = "0.8"
|
itertools = "0.8"
|
||||||
nix = "0.17"
|
nix = "0.17"
|
||||||
strip-ansi-escapes = "0.1"
|
strip-ansi-escapes = "0.1"
|
||||||
|
crossbeam = "0.7"
|
||||||
|
|
||||||
|
|
||||||
image = { version = "0.21.1", optional = true }
|
image = { version = "0.21.1", optional = true }
|
||||||
|
174
src/files.rs
174
src/files.rs
@ -104,6 +104,8 @@ pub enum FileError {
|
|||||||
OpenDir(#[cause] nix::Error),
|
OpenDir(#[cause] nix::Error),
|
||||||
#[fail(display = "Couldn't read files! Error: {}", _0)]
|
#[fail(display = "Couldn't read files! Error: {}", _0)]
|
||||||
ReadFiles(#[cause] nix::Error),
|
ReadFiles(#[cause] nix::Error),
|
||||||
|
#[fail(display = "Had problems with getdents64 in directory: {}", _0)]
|
||||||
|
GetDents(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_pool() -> ThreadPool {
|
pub fn get_pool() -> ThreadPool {
|
||||||
@ -383,7 +385,179 @@ impl Drop for Files {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct linux_dirent {
|
||||||
|
pub d_ino: u64,
|
||||||
|
pub d_off: u64,
|
||||||
|
pub d_reclen: u16,
|
||||||
|
pub d_type: u8,
|
||||||
|
pub d_name: [u8; 0],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// This arcane spell hastens the target by around 30%.
|
||||||
|
|
||||||
|
// It uses quite a bit of usafe code, mostly to call into libc and
|
||||||
|
// dereference raw pointers inherent to the getdents API, but also to
|
||||||
|
// avoid some of the overhead built into Rust's default conversion
|
||||||
|
// methods. How the getdents64 syscall is intended to be used was
|
||||||
|
// mostly looked up in man 2 getdents64, the nc crate, and the
|
||||||
|
// upcoming version of the walkdir crate, plus random examples here
|
||||||
|
// and there..
|
||||||
|
|
||||||
|
// This should probably be replaced with walkdir when it gets a proper
|
||||||
|
// release with the new additions. nc itself is already too high level
|
||||||
|
// to meet the performance target, unfortunately.
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub fn from_getdents(fd: i32, path: &Path, nothidden: &AtomicUsize) -> Vec<File>
|
||||||
|
{
|
||||||
|
use libc::SYS_getdents64;
|
||||||
|
|
||||||
|
// Nice big 4MB buffer
|
||||||
|
const BUFFER_SIZE: usize = 1024 * 1024 * 4;
|
||||||
|
|
||||||
|
let mut buf: Vec<u8> = vec![0; BUFFER_SIZE];
|
||||||
|
let bufptr = buf.as_mut_ptr();
|
||||||
|
let files = std::sync::Mutex::new(vec![]);
|
||||||
|
let files = &files;
|
||||||
|
|
||||||
|
crossbeam::scope(|s| {
|
||||||
|
loop {
|
||||||
|
let nread = unsafe { libc::syscall(SYS_getdents64, fd, bufptr, BUFFER_SIZE) };
|
||||||
|
|
||||||
|
if nread <= 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone buffer for processing in another thread and fetch more entries
|
||||||
|
let mut buf: Vec<u8> = buf.clone();
|
||||||
|
|
||||||
|
s.spawn(move |_| {
|
||||||
|
let cap = nread as usize / std::mem::size_of::<linux_dirent>();
|
||||||
|
let mut localfiles = Vec::with_capacity(cap);
|
||||||
|
let bufptr = buf.as_mut_ptr();
|
||||||
|
let mut bpos: usize = 0;
|
||||||
|
|
||||||
|
while bpos < nread as usize {
|
||||||
|
let d: &linux_dirent = unsafe { std::mem::transmute(bufptr as usize + bpos as usize) };
|
||||||
|
|
||||||
|
// Name lenegth is overallocated, true length can be found by checking with strlen
|
||||||
|
let name_len = d.d_reclen as usize -
|
||||||
|
std::mem::size_of::<u64>() -
|
||||||
|
std::mem::size_of::<u64>() -
|
||||||
|
std::mem::size_of::<u16>() -
|
||||||
|
std::mem::size_of::<u8>();
|
||||||
|
|
||||||
|
// OOB!!!
|
||||||
|
if bpos + name_len > BUFFER_SIZE {
|
||||||
|
HError::log::<()>(&format!("WARNING: Name for file was out of bounds in: {}",
|
||||||
|
path.to_string_lossy())).ok();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Doing it here simplifies skipping
|
||||||
|
bpos = bpos + d.d_reclen as usize;
|
||||||
|
|
||||||
|
let name: &OsStr = {
|
||||||
|
let true_len = unsafe { libc::strlen(d.d_name.as_ptr() as *const i8) };
|
||||||
|
let bytes: &[u8] = unsafe { std::slice::from_raw_parts(d.d_name.as_ptr() as *const u8,
|
||||||
|
true_len) };
|
||||||
|
|
||||||
|
// Don't want this
|
||||||
|
if bytes.len() == 0 || bytes == b"." || bytes == b".." {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe { std::mem::transmute(bytes) }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Avoid reallocation on push
|
||||||
|
let mut pathstr = std::ffi::OsString::with_capacity(path.as_os_str().len() +
|
||||||
|
name.len() +
|
||||||
|
2);
|
||||||
|
pathstr.push(path.as_os_str());
|
||||||
|
pathstr.push("/");
|
||||||
|
pathstr.push(name);
|
||||||
|
|
||||||
|
let path = PathBuf::from(pathstr);
|
||||||
|
|
||||||
|
let name = name.to_str()
|
||||||
|
.map(|n| String::from(n))
|
||||||
|
.unwrap_or_else(|| name.to_string_lossy().to_string());
|
||||||
|
|
||||||
|
|
||||||
|
let hidden = name.as_bytes()[0] == b'.';
|
||||||
|
|
||||||
|
if !hidden {
|
||||||
|
nothidden.fetch_add(1, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
let kind = match d.d_type {
|
||||||
|
4 => Kind::Directory,
|
||||||
|
_ => Kind::File,
|
||||||
|
};
|
||||||
|
|
||||||
|
let file = File {
|
||||||
|
name: name,
|
||||||
|
hidden: hidden,
|
||||||
|
kind: kind,
|
||||||
|
path: path,
|
||||||
|
dirsize: None,
|
||||||
|
target: None,
|
||||||
|
meta: None,
|
||||||
|
selected: false,
|
||||||
|
tag: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
localfiles.push(file);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
files.lock().unwrap().append(&mut localfiles);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).unwrap();
|
||||||
|
|
||||||
|
return std::mem::take(&mut *files.lock().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Files {
|
impl Files {
|
||||||
|
// Use getdents64 on Linux
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub fn new_from_path_cancellable(path: &Path, stale: Stale) -> HResult<Files> {
|
||||||
|
use std::os::unix::io::AsRawFd;
|
||||||
|
|
||||||
|
let nonhidden = AtomicUsize::default();
|
||||||
|
|
||||||
|
let dir = Dir::open(path.clone(),
|
||||||
|
OFlag::O_DIRECTORY,
|
||||||
|
Mode::empty())
|
||||||
|
.map_err(|e| FileError::OpenDir(e))?;
|
||||||
|
|
||||||
|
let direntries = from_getdents(dir.as_raw_fd(), path, &nonhidden);
|
||||||
|
|
||||||
|
if stale.is_stale()? {
|
||||||
|
HError::stale()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut files = Files::default();
|
||||||
|
files.directory = File::new_from_path(&path)?;
|
||||||
|
|
||||||
|
|
||||||
|
files.files = direntries;
|
||||||
|
files.len = nonhidden.load(Ordering::Relaxed);
|
||||||
|
files.stale = Some(stale);
|
||||||
|
|
||||||
|
Ok(files)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
pub fn new_from_path_cancellable(path: &Path, stale: Stale) -> HResult<Files> {
|
pub fn new_from_path_cancellable(path: &Path, stale: Stale) -> HResult<Files> {
|
||||||
let nonhidden = AtomicUsize::default();
|
let nonhidden = AtomicUsize::default();
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ extern crate strum_macros;
|
|||||||
extern crate derivative;
|
extern crate derivative;
|
||||||
extern crate nix;
|
extern crate nix;
|
||||||
extern crate strip_ansi_escapes;
|
extern crate strip_ansi_escapes;
|
||||||
|
extern crate crossbeam;
|
||||||
|
|
||||||
extern crate osstrtools;
|
extern crate osstrtools;
|
||||||
extern crate pathbuftools;
|
extern crate pathbuftools;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user