Skip to content

Question: par_iter slower than shell version; threads not 100% busy #540

@bdrewery

Description

@bdrewery

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()
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions