pub(crate) struct Opts {Show 26 fields
pub(crate) slice_us_underutil: u64,
pub(crate) slice_us_overutil: u64,
pub(crate) interval: f64,
pub(crate) tune_interval: f64,
pub(crate) load_half_life: f64,
pub(crate) cache_level: u32,
pub(crate) cpumasks: Vec<String>,
pub(crate) greedy_threshold: u32,
pub(crate) greedy_threshold_x_numa: u32,
pub(crate) no_load_balance: bool,
pub(crate) kthreads_local: bool,
pub(crate) balanced_kworkers: bool,
pub(crate) fifo_sched: bool,
pub(crate) direct_greedy_under: f64,
pub(crate) kick_greedy_under: f64,
pub(crate) direct_greedy_numa: bool,
pub(crate) partial: bool,
pub(crate) mempolicy_affinity: bool,
pub(crate) stats: Option<f64>,
pub(crate) monitor: Option<f64>,
pub(crate) exit_dump_len: u32,
pub(crate) verbose: u8,
pub(crate) version: bool,
pub(crate) help_stats: bool,
pub(crate) perf: u32,
pub libbpf: LibbpfOpts,
}Expand description
scx_rusty: A multi-domain BPF / userspace hybrid scheduler
The BPF part does simple vtime or round robin scheduling in each domain while tracking average load of each domain and duty cycle of each task.
The userspace part performs two roles. First, it makes higher frequency (100ms) tuning decisions. It identifies CPUs which are not too heavily loaded and marks them so that they can pull tasks from other overloaded domains on the fly.
Second, it drives lower frequency (2s) load balancing. It determines whether load balancing is necessary by comparing domain load averages. If there are large enough load differences, it examines upto 1024 recently active tasks on the domain to determine which should be migrated.
The overhead of userspace operations is low. Load balancing is not performed frequently, but work-conservation is still maintained through tuning and greedy execution. Load balancing itself is not that expensive either. It only accesses per-domain load metrics to determine the domains that need load balancing, as well as limited number of per-task metrics for each pushing domain.
An earlier variant of this scheduler was used to balance across six domains, each representing a chiplet in a six-chiplet AMD processor, and could match the performance of production setup using CFS.
WARNING: scx_rusty currently assumes that all domains have equal processing power and at similar distances from each other. This limitation will be removed in the future.
Fields§
§slice_us_underutil: u64Scheduling slice duration for under-utilized hosts, in microseconds.
slice_us_overutil: u64Scheduling slice duration for over-utilized hosts, in microseconds.
interval: f64Load balance interval in seconds.
tune_interval: f64The tuner runs at a higher frequency than the load balancer to dynamically tune scheduling behavior. Tuning interval in seconds.
load_half_life: f64The half-life of task and domain load running averages in seconds.
cache_level: u32Build domains according to how CPUs are grouped at this cache level as determined by /sys/devices/system/cpu/cpuX/cache/indexI/id.
cpumasks: Vec<String>Instead of using cache locality, set the cpumask for each domain manually. Provide multiple –cpumasks, one for each domain. E.g. –cpumasks 0xff_00ff –cpumasks 0xff00 will create two domains, with the corresponding CPUs belonging to each domain. Each CPU must belong to precisely one domain.
greedy_threshold: u32When non-zero, enable greedy task stealing. When a domain is idle, a cpu will attempt to steal tasks from another domain as follows:
- Try to consume a task from the current domain
- Try to consume a task from another domain in the current NUMA node (or globally, if running on a single-socket system), if the domain has at least this specified number of tasks enqueued.
See greedy_threshold_x_numa to enable task stealing across NUMA nodes. Tasks stolen in this manner are not permanently stolen from their domain.
greedy_threshold_x_numa: u32When non-zero, enable greedy task stealing across NUMA nodes. The order of greedy task stealing follows greedy-threshold as described above, and greedy-threshold must be nonzero to enable task stealing across NUMA nodes.
no_load_balance: boolDisable load balancing. Unless disabled, userspace will periodically calculate the load factor of each domain and instruct BPF which processes to move.
kthreads_local: boolPut per-cpu kthreads directly into local dsq’s.
balanced_kworkers: boolIn recent kernels (>=v6.6), the kernel is responsible for balancing kworkers across L3 cache domains. Exclude them from load-balancing to avoid conflicting operations. Greedy executions still apply.
fifo_sched: boolUse FIFO scheduling instead of weighted vtime scheduling.
direct_greedy_under: f64Idle CPUs with utilization lower than this will get remote tasks directly pushed onto them. 0 disables, 100 always enables.
kick_greedy_under: f64Idle CPUs with utilization lower than this may get kicked to accelerate stealing when a task is queued on a saturated remote domain. 0 disables, 100 enables always.
direct_greedy_numa: boolWhether tasks can be pushed directly to idle CPUs on NUMA nodes different than their domain’s node. If direct-greedy-under is disabled, this option is a no-op. Otherwise, if this option is set to false (default), tasks will only be directly pushed to idle CPUs if they reside on the same NUMA node as the task’s domain.
partial: boolIf specified, only tasks which have their scheduling policy set to SCHED_EXT using sched_setscheduler(2) are switched. Otherwise, all tasks are switched.
mempolicy_affinity: boolEnables soft NUMA affinity for tasks that use set_mempolicy. This may improve performance in some scenarios when using mempolicies.
stats: Option<f64>Enable stats monitoring with the specified interval.
monitor: Option<f64>Run in stats monitoring mode with the specified interval. The scheduler is not launched.
exit_dump_len: u32Exit debug dump buffer length. 0 indicates default.
verbose: u8Enable verbose output, including libbpf details. Specify multiple times to increase verbosity.
version: boolPrint version and exit.
help_stats: boolShow descriptions for statistics.
perf: u32Tunable for prioritizing CPU performance by configuring the CPU frequency governor. Valid values are [0, 1024]. Higher values prioritize performance, lower values prioritize energy efficiency. When in doubt, use 0 or 1024.
libbpf: LibbpfOptsTrait Implementations§
Source§impl Args for Opts
impl Args for Opts
Source§fn group_id() -> Option<Id>
fn group_id() -> Option<Id>
ArgGroup::id][crate::ArgGroup::id] for this set of argumentsSource§fn augment_args<'b>(__clap_app: Command) -> Command
fn augment_args<'b>(__clap_app: Command) -> Command
Source§fn augment_args_for_update<'b>(__clap_app: Command) -> Command
fn augment_args_for_update<'b>(__clap_app: Command) -> Command
Command] so it can instantiate self via
[FromArgMatches::update_from_arg_matches_mut] Read moreSource§impl FromArgMatches for Opts
impl FromArgMatches for Opts
Source§fn from_arg_matches(__clap_arg_matches: &ArgMatches) -> Result<Self, Error>
fn from_arg_matches(__clap_arg_matches: &ArgMatches) -> Result<Self, Error>
Source§fn from_arg_matches_mut(
__clap_arg_matches: &mut ArgMatches,
) -> Result<Self, Error>
fn from_arg_matches_mut( __clap_arg_matches: &mut ArgMatches, ) -> Result<Self, Error>
Source§fn update_from_arg_matches(
&mut self,
__clap_arg_matches: &ArgMatches,
) -> Result<(), Error>
fn update_from_arg_matches( &mut self, __clap_arg_matches: &ArgMatches, ) -> Result<(), Error>
ArgMatches to self.Source§fn update_from_arg_matches_mut(
&mut self,
__clap_arg_matches: &mut ArgMatches,
) -> Result<(), Error>
fn update_from_arg_matches_mut( &mut self, __clap_arg_matches: &mut ArgMatches, ) -> Result<(), Error>
ArgMatches to self.Source§impl Parser for Opts
impl Parser for Opts
§fn parse_from<I, T>(itr: I) -> Self
fn parse_from<I, T>(itr: I) -> Self
§fn try_parse_from<I, T>(itr: I) -> Result<Self, Error>
fn try_parse_from<I, T>(itr: I) -> Result<Self, Error>
§fn update_from<I, T>(&mut self, itr: I)
fn update_from<I, T>(&mut self, itr: I)
§fn try_update_from<I, T>(&mut self, itr: I) -> Result<(), Error>
fn try_update_from<I, T>(&mut self, itr: I) -> Result<(), Error>
Auto Trait Implementations§
impl Freeze for Opts
impl RefUnwindSafe for Opts
impl Send for Opts
impl Sync for Opts
impl Unpin for Opts
impl UnwindSafe for Opts
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
§impl<T> Conv for T
impl<T> Conv for T
§impl<T> FmtForward for T
impl<T> FmtForward for T
§fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
self to use its Binary implementation when Debug-formatted.§fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
self to use its Display implementation when
Debug-formatted.§fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
self to use its LowerExp implementation when
Debug-formatted.§fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
self to use its LowerHex implementation when
Debug-formatted.§fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
self to use its Octal implementation when Debug-formatted.§fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
self to use its Pointer implementation when
Debug-formatted.§fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
self to use its UpperExp implementation when
Debug-formatted.§fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
self to use its UpperHex implementation when
Debug-formatted.§fn fmt_list(self) -> FmtList<Self>where
&'a Self: for<'a> IntoIterator,
fn fmt_list(self) -> FmtList<Self>where
&'a Self: for<'a> IntoIterator,
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read more§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read more§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
self, then passes self.as_ref() into the pipe function.§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
self, then passes self.as_mut() into the pipe
function.§fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self, then passes self.deref() into the pipe function.§impl<T> Pointable for T
impl<T> Pointable for T
§impl<T> Tap for T
impl<T> Tap for T
§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B> of a value. Read more§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B> of a value. Read more§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R> view of a value. Read more§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R> view of a value. Read more§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target of a value. Read more§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target of a value. Read more§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap() only in debug builds, and is erased in release builds.§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut() only in debug builds, and is erased in release
builds.§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.tap_borrow() only in debug builds, and is erased in release
builds.§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.tap_borrow_mut() only in debug builds, and is erased in release
builds.§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.tap_ref() only in debug builds, and is erased in release
builds.§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.tap_ref_mut() only in debug builds, and is erased in release
builds.§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref() only in debug builds, and is erased in release
builds.