-
Notifications
You must be signed in to change notification settings - Fork 542
Description
If there's a better place to ask this please close and direct me there but this seems like it is a lack of Documentation or a bug. It could simply be that Rayon's parallelism model is not the right one for my task.
My main problem is that iter_par
seems to start up with NCPU threads (16) and then eventually
backs off to 3-4 at a time while the rest just sleep. The same functionality in (quite complex) shell runs in 190 seconds, but 250 seconds in rust (--release). The code being ran in parallel is very expensive since it does a fork+exec.
I've tried .with_max_len(1)
and other combinations like using std::usize::MAX
but nothing seems to help.
What I was looking for was a way to have a shared work queue that keeps all threads busy, sending work to the next available thread rather than splitting the work up "evenly". That already sounds like it's not what Rayon is intended for, but with the work stealing feature I figured it should be a decent library for prototyping, but was surprised by the results.
Details
I'm also on day 3 of learning Rust.
Using rayon 1.0.0.
let mut portvars: FnvHashMap<&String, FnvHashMap<String, String>> =
FnvHashMap::with_capacity_and_hasher(ports.len(), Default::default());
portvars.par_extend(ports.par_iter().with_max_len(1).map(|port|
(port, port_var_fetch(&config, port, &fetchvars))
));
Here ports
is a Vec<String>
with 26000 items. I have 16 CPU.
port_var_fetch
runs make
to gather variables from Makefiles (on FreeBSD make -V
can be used for this, I'm not sure what the equivalent in gmake
is).
port_var_fetch
I've listed the port_var_fetch
in case its implementation is relevant.
This is the fetchvars
variable that is passed in.
let mut fetchvars = Vec::new();
fetchvars.push(("PKGNAME", "PKGNAME"));
fetchvars.push(("MAINTAINER", "MAINTAINER"));
fetchvars.push(("FLAVORS", "FLAVORS"));
fetchvars.push(("FLAVOR", "FLAVOR"));
fetchvars.push(("CATEGORIES", "CATEGORIES"));
fetchvars.push(("IGNORE", "IGNORE"));
fetchvars.push(("DEPEND_SPECIALS", "${_DEPEND_SPECIALS:C,^${PORTSDIR}/,,}"));
fetchvars.push(("SELECTED_OPTIONS", "SELECTED_OPTIONS:O"));
fetchvars.push(("LIB_DEPENDS", "LIB_DEPENDS"));
fetchvars.push(("RUN_DEPENDS", "RUN_DEPENDS"));
fn port_var_fetch(config: &HashMap<String, String>,
port: &str, vars: &Vec<(&str, &str)>) -> FnvHashMap<String, String> {
let mut args = Vec::with_capacity(vars.len() * 2);
for var in vars {
args.push("-V");
args.push(var.1);
}
let results = pmake(&config, port, args);
let mut result: FnvHashMap<String, String> =
FnvHashMap::with_capacity_and_hasher(vars.len(), Default::default());
result.extend(vars.into_iter().zip(results.lines())
.map(|(var, value)| ((*var).0.into(), value.into())));
result
}
fn pmake(config: &HashMap<String, String>,
origin: &str, args: Vec<&str>) -> String {
make(&(config.get("PORTSDIR").unwrap().to_string() + "/" + origin), args)
}
fn make(dir: &str, args: Vec<&str>) -> String {
let mut cmd = Command::new("/usr/bin/make");
cmd.stdin(Stdio::inherit())
.stderr(Stdio::inherit())
.arg("-C").arg(dir)
.args(args);
let output = cmd.output().expect("Make failed");
String::from_utf8_lossy(&output.stdout).trim().to_string()
}