Skip to content

Added support for configuring number of threads, and thread name, used by POOL in task::executor #690

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from

Conversation

mward
Copy link

@mward mward commented Jan 29, 2020

This update allows user of this library to, optionally, configure the number of threads, and thread names, used by task::executor::pool::POOL.

example:

/// execute this before calling 'task::block_on()'
let mut config = RuntimeConfig::new();
config.num_thread(4).thread_name("my-thread");
assert!(config.finalize().is_ok());

/// now only 4 worker threads will be created (named 'my-thread')
task::block_on(...)

@k-nasa
Copy link
Member

k-nasa commented Feb 5, 2020

Thanks for PR!

I don't know why it is necessary.
But, I think this is a good feature.

I can't decide to merge this feature.

@yoshuawuyts @stjepang

Any opinions on incorporating this feature?
(I'll do a code review in the future)

@mward
Copy link
Author

mward commented Feb 5, 2020

@k-nasa thanks for taking a look at this.

This PR by no means is meant to be the definitive implementation for supporting a Runtime's configuration. I wanted to added support for configuring the number of threads without altering the current design. From reading the issues it appears that a bigger re-design will be coming.

As to your comment "I don't know why it is necessary": it is often essential to have finer grained control of the resources a program utilizes. It is not uncommon for processes to be given cpu affinity, whether it be a program written in rust or another process running on the same server. These types of configurations will be necessary for some.

Thanks again

… as well as thread name, used by POOL in task::executor
@k-nasa
Copy link
Member

k-nasa commented Mar 2, 2020

ping @yoshuawuyts @stjepang

@yoshuawuyts
Copy link
Contributor

@mward thanks for opening this issue! We've been talking about how to configure threadpool sizes in the past, and the best method for that right now seems to be to follow Rayon's lead and read it from env vars if set. That allows sysadmins to change the limits without requiring it to be recompiled.

This patch is a bit different: it introduces new types inside the program to set these limits. We agree this could be useful, but we'd like to see global limits through env vars be possible first.

@skade
Copy link
Collaborator

skade commented Mar 3, 2020

As we take great care to not expose any runtime details to users through the async-std interface, I'd not use such an interface. A runtime-specific number of ENV vars makes sense, through.

@awaited-hare
Copy link

As we take great care to not expose any runtime details to users through the async-std interface, I'd not use such an interface. A runtime-specific number of ENV vars makes sense, through.

I found this issue when I try to find a way to limit the number of threads used in async_std. Has this ENV var been implemented? I cannot find any relevant information in the documentation.

@heroin-moose
Copy link

heroin-moose commented May 18, 2020

Also on a system with huge (think 64+) number of cores, heavy parallel running of a small (consider something like logger(1) implemented in rust) binary will be kinda inefficient, since your program makes two or three "logical" syscalls (like reading from /etc/passwd, opening /dev/log and writing to it), but before it runs 64+ heavy clone() calls that it will not utilize anyway.

@heroin-moose
Copy link

heroin-moose commented Dec 26, 2020

Also there is another problem: the PID number is limited to 32k by default on Linux and BSD. So on a system with large number of CPUs it will take less than 1024 processes (e.g. 256 on modern Epycs that have 128 processors when HT enabled) to fill up the PID space entirely. And since privsep model is finally getting popular the actual number of running programs may be lower. And in most cases people really don't need that much threads.

@D1plo1d
Copy link

D1plo1d commented Apr 11, 2021

Any update on this? I'd like to set the number of threads and CPU affinity so that my 3D printer's web server runs on num_cpus - 1 enabling my latency-sensitive control loop code to have a core to itself. This looks like it solves the num_cpus - 1 problem and I'd be happy to add a PR for cpu affinity using https://docs.rs/nix/0.17.0/nix/sched/fn.sched_setaffinity.html if we can get this merged.

Edit: Just seeing cpu affinity is inherited by forked processes so really there's no changes to async-std for CPU affinity needed beyond this PR if I call sched_setaffinity before starting the runtime. This PR would be all I'd need.

@jbr
Copy link
Contributor

jbr commented Apr 12, 2021

@D1plo1d do the changes in #774 meet your needs? I believe this issue was closed by that

@jbr jbr closed this Apr 12, 2021
@heroin-moose
Copy link

heroin-moose commented Apr 12, 2021

This change requires either system administrator or a user to know what's going on. Or requires a programmer to create a wrapper around the program.

@jbr
Copy link
Contributor

jbr commented Apr 12, 2021

@heroin-moose in this comparatively uncommon situation, you can also set the env from within rust if you really need to:

fn main() {
    std::env::set_var("ASYNC_STD_THREAD_COUNT", "3");
    std::env::set_var("ASYNC_STD_THREAD_NAME", "my-app-threads");
    async_std::task::block_on(async {
        //
    });
}

@D1plo1d
Copy link

D1plo1d commented Apr 12, 2021

@jbr Yes that worked for me, thanks for the quick reply! Full example from my code in case anyone else stumbles across this in future trying to do something similar:

fn main() -> Result<()> {
    use nix::sched::{CpuSet, sched_setaffinity};
    use nix::unistd::Pid;

    let logical_cpus = num_cpus::get();

    // Restrict the server to any logical CPU except for the one that is reserved for
    // the printer driver processes (eg. teg-marlin).
    let mut cpu_set = CpuSet::new();
    for cpu_index in 1..logical_cpus {
        cpu_set.set(cpu_index)?;
    }
    sched_setaffinity(Pid::from_raw(0), &cpu_set)?;

    // Run one async-std thread for each logical CPU except for the one that is reserved for
    // the printer driver processes (eg. teg-marlin).
    let threads = std::cmp::max(logical_cpus.saturating_sub(1), 1);
    std::env::set_var("ASYNC_STD_THREAD_COUNT", threads.to_string());

    // Start the runtime
    async_std::task::block_on(app())
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants