Basic perf testing.

Fortune for enumerate-ordered's current commit: Future blessing − 末吉
master
Avril 2 years ago
parent e156e2cc1e
commit cfe11a1916
Signed by: flanchan
GPG Key ID: 284488987C31F630

2
.gitignore vendored

@ -1 +1,3 @@
/target /target
flamegraph.*
perf.*

@ -5,6 +5,12 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[profile.release]
opt-level = 3
lto = "fat"
codegen-units = 1
strip = false
[dependencies] [dependencies]
color-eyre = { version = "0.6.2", default-features = false } color-eyre = { version = "0.6.2", default-features = false }
futures = "0.3.25" futures = "0.3.25"

@ -55,12 +55,21 @@ impl Args
break; break;
}, },
}; };
trace!("paths: buffer: {:?}", &buf[..]);
if n == 0 { if n == 0 {
trace!("paths: Stdin exhausted. Stopping read."); trace!("paths: Stdin exhausted. Exiting.");
break; break;
} }
let path_bytes = &buf[..n]; let path_bytes = &buf[..n];
let path_bytes = if path_bytes.len() == 1 {
trace!("paths: Ignoring empty line. Yielding then continuing.");
tokio::task::yield_now().await;
continue;
} else {
&path_bytes[.. (path_bytes.len()-1)]
};
let path = Path::new(OsStr::from_bytes(path_bytes)); let path = Path::new(OsStr::from_bytes(path_bytes));
trace!("Read path {:?}", path);
if path.exists() { if path.exists() {
if tx.send(path.to_owned()).await.is_err() { if tx.send(path.to_owned()).await.is_err() {
trace!("paths: Stream dropped, cancelling stdin read."); trace!("paths: Stream dropped, cancelling stdin read.");
@ -77,6 +86,7 @@ impl Args
#[inline] #[inline]
pub fn parse_args() -> eyre::Result<Args> pub fn parse_args() -> eyre::Result<Args>
{ {
//return Ok(Args { paths: None });
todo!("parse(std::env::args().skip(1))") todo!("parse(std::env::args().skip(1))")
} }

@ -114,6 +114,7 @@ async fn main() -> eyre::Result<()> {
}; };
info!("Walked {} files", walk); info!("Walked {} files", walk);
// Write the output in a blocking task - There's not much benefit from async here.
tokio::task::spawn_blocking(move || -> eyre::Result<()> { tokio::task::spawn_blocking(move || -> eyre::Result<()> {
use std::io::Write; use std::io::Write;
use std::os::unix::prelude::*; use std::os::unix::prelude::*;
@ -121,7 +122,7 @@ async fn main() -> eyre::Result<()> {
let lock = std::io::stdout().lock(); let lock = std::io::stdout().lock();
std::io::BufWriter::new(lock) std::io::BufWriter::new(lock)
}; };
trace!("Writing ordered results to stdout... (buffered)"); trace!("Writing ordered results to stdout... (buffered, sync)");
for info in set.into_iter() for info in set.into_iter()
{ {
stdout.write_all(info.path().as_os_str().as_bytes()) stdout.write_all(info.path().as_os_str().as_bytes())
@ -132,7 +133,7 @@ async fn main() -> eyre::Result<()> {
stdout.flush().wrap_err("Failed to flush buffered output to stdout")?; stdout.flush().wrap_err("Failed to flush buffered output to stdout")?;
Ok(()) Ok(())
}).await.wrap_err("Writer task panic")? }).await.wrap_err("Writer (blocking) task panic")?
.wrap_err("Failed to write results to stdout")?; .wrap_err("Failed to write results to stdout")?;
trace!("Finished output task"); trace!("Finished output task");

@ -100,6 +100,7 @@ where F: FnMut(State, PathBuf)
let mut read = tokio::fs::read_dir(whence).await.wrap_err("Failed to read-dir")?; let mut read = tokio::fs::read_dir(whence).await.wrap_err("Failed to read-dir")?;
let sender = &state.current.output_sender; let sender = &state.current.output_sender;
let file_info = state.create_file_info_generator(); let file_info = state.create_file_info_generator();
let mut n = 0;
while let Some(fso) = read.next_entry().await.wrap_err("Failed to enumerate directory entry")? while let Some(fso) = read.next_entry().await.wrap_err("Failed to enumerate directory entry")?
{ {
let metadata = match fso.metadata().await { let metadata = match fso.metadata().await {
@ -113,13 +114,16 @@ where F: FnMut(State, PathBuf)
if let Err(_) = sender.send(file_info(fso.path(), metadata)).await { if let Err(_) = sender.send(file_info(fso.path(), metadata)).await {
warn!("Worker shut down, stopping iteration"); warn!("Worker shut down, stopping iteration");
break; break;
} else {
n += 1;
} }
} else if metadata.is_dir() { } else if metadata.is_dir() {
let Some(state) = state.create_child() else { continue }; let Some(state) = state.create_child() else { continue };
push_child(state, fso.path()); push_child(state, fso.path());
} // Ignore symlinks. } // Ignore symlinks.
} }
todo!("Walk to directory and output its files into `state`'s tx XXX: Does this function need to exist? We could just do this in walk_inner() directly: Explicit boxing doesn't need to be done as we're working with joinhandles and backing tasks") Ok(n)
//todo!("Walk to directory and output its files into `state`'s tx XXX: Does this function need to exist? We could just do this in walk_inner() directly: Explicit boxing doesn't need to be done as we're working with joinhandles and backing tasks")
} }
/// This function is called recursively for each subdirectory in `whence` pertaining to the recursion rules. /// This function is called recursively for each subdirectory in `whence` pertaining to the recursion rules.
@ -140,13 +144,22 @@ async fn walk_inner(state: State, whence: PathBuf) -> eyre::Result<usize>
// XXX: Maybe use mpsc for this instead: We send the JoinHandle's to a rx being looped on in a `join!` in the outer `async move {}` scope at the same time as this one. When the sender is dropped, the channel will close. We can join each child of the stream concurrently with `futures` (probably?) and bubble up panics when they are found. // XXX: Maybe use mpsc for this instead: We send the JoinHandle's to a rx being looped on in a `join!` in the outer `async move {}` scope at the same time as this one. When the sender is dropped, the channel will close. We can join each child of the stream concurrently with `futures` (probably?) and bubble up panics when they are found.
let mut children = Vec::new(); let mut children = Vec::new();
// Number of *files* sent to tx from this iteration. // Number of *files* sent to tx from this iteration.
let counted = walk_directory(&state, |state, whence| {
fn walk_inner2(state: State, whence: PathBuf) -> BoxFuture<'static, eyre::Result<usize>> let counted = if whence.is_dir() {
{ walk_directory(&state, |state, whence| {
walk_inner(state, whence).boxed() fn walk_inner2(state: State, whence: PathBuf) -> BoxFuture<'static, eyre::Result<usize>>
{
walk_inner(state, whence).boxed()
}
children.push(walk_inner2(state, whence));
}, &whence).await?
} else {
let metadata = tokio::fs::metadata(&whence).await.wrap_err("Failed to stat top-level file")?;
if let Err(_) = state.current.output_sender.send(work::FileInfo::new(Arc::clone(&state.shared.worker_config), whence, metadata)).await {
return Err(eyre!("Failed to send top-level file to backing sorter: Sorter closed."));
} }
children.push(walk_inner2(state, whence)); 1
}, &whence).await?; };
Ok((counted, children)) Ok((counted, children))
}).await.expect("Panic in backing walker thread"); }).await.expect("Panic in backing walker thread");
@ -183,5 +196,5 @@ pub fn start_walk(cfg: Config, worker: Arc<work::Config>, whence: impl AsRef<Pat
worker_config: worker, worker_config: worker,
}), }),
}, whence.as_ref().to_owned()) }, whence.as_ref().to_owned())
//.flatten() //.flatten()
} }

Loading…
Cancel
Save