SchbenchConfig

Struct SchbenchConfig 

Source
pub struct SchbenchConfig {
    pub message_threads: usize,
    pub worker_threads: usize,
    pub cache_footprint_kib: usize,
    pub operations: usize,
    pub sleep_usec: u64,
    pub skip_locking: bool,
    pub requests_per_sec: usize,
    pub auto_rps: usize,
    pub split_percent: Option<usize>,
    pub pipe_transfer_bytes: usize,
}
Expand description

User-facing config for the Schbench workload. Declarative config for the Schbench workload. Construct via SchbenchConfig::default (schbench’s own defaults) plus the chainable setters, e.g. SchbenchConfig::default().message_threads(2).worker_threads(4). Derives Clone/Debug/PartialEq/Eq/Hash/serde; the builder shape follows WorkloadConfig, but Eq+Hash (which WorkloadConfig and WorkSpec omit because of their transitive f64) are available here since every field is integer/bool – the ktstr f64-free leaf-config convention.

§schbench(8) CLI parity

This port re-expresses schbench’s default (matrix-work) mode natively, so its tunables are config fields and topology rather than CLI flags. The mapping to schbench’s option table (schbench.c:138-187):

schbench flagktstr
-m message-threadsmessage_threads
-t threadsworker_threads (workers per message thread; 0 = ceil(cpuset_cpus / message_threads), see below)
-F cache_footprintcache_footprint_kib
-n operationsoperations
-s sleep_usecsleep_usec
-L no-lockingskip_locking
-R rpsrequests_per_sec
-A auto-rpsauto_rps
--split (long-only)split_percent (None = no split, all-private)
-p pipe (also --pipe)pipe_transfer_bytes (0 = off; memory-transfer mode, no matrix work)
-r runtimein-VM: the scenario engine’s run window (the engine runs until stop); host-side: the run_secs argument to run_standalone

§Set by ktstr topology, not a flag

  • -t default: with worker_threads = 0, ktstr matches schbench’s -t 0-default – it divides the CPU count across the message threads, ceil(cpus / message_threads) per thread (schbench.c:1849-1852), so the total worker count stays near the CPU count. ktstr scopes “cpus” to the allocated guest cpuset (the worker’s sched_getaffinity mask, set by the scenario’s topology / CgroupDef) rather than schbench’s get_nprocs, so the total is ≈ the cpuset’s CPU count. An explicit non-zero worker_threads is workers-per-message-thread in both.
  • -M (message-cpus) / -W (worker-cpus) thread pinning: ktstr places threads through its affinity / cpuset layer, so there is deliberately no per-thread-pin knob.

§Observability flags -> the metric API

schbench’s -w (warmuptime), -i (intervaltime), -z (zerotime), -j (json), and -J (jobname) shape its streaming stderr/JSON report. ktstr’s numbers flow through the metric API instead – per-phase attribution and the sidecar – so these have no flag equivalent. ktstr-schbench-validate reproduces schbench’s stderr-table shape for a side-by-side comparison.

§Split mode (--split)

Some(p) partitions cache_footprint_kib into a per-thread private matrix (p%) and ONE process-global shared matrix (100-p%) that every worker multiplies into concurrently, reproducing schbench’s cross-core shared-working-set cache contention (schbench.c:1390-1404, :1858-1863). ktstr models the shared matrix with AtomicU64 Relaxed accesses. Like schbench’s emitted code, the shared kernel keeps the running sum in a register and STORES it to each shared C cell on every inner (k) iteration – C is write-only in the loop (A and B are loaded each k, C is never reloaded), and that per-k store is what generates the contention. Both gcc and clang keep the per-k store: do_some_math reads m1/m2/m3 as offsets into one base pointer, so neither can prove the m3 store doesn’t alias the next k’s m1/m2 loads. On x86-64 a Relaxed load/store lowers to a plain MOV (no LOCK), so the contention is identical to schbench’s plain shared-memory race – but sound (atomics, no data race), with zero unsafe. None (default) is the legacy all-private single matrix.

§Pipe mode (-p)

pipe_transfer_bytes > 0 REPLACES the matrix workload with schbench’s memory-transfer simulation (schbench.c:177, pipe_test). It rides the message-handshake path: the message thread memsets each woken worker’s per-thread page to 1 (schbench.c:980-981) and the worker memsets its own page to 2 before blocking (schbench.c:1003-1004), pipe_transfer_bytes bytes each per handshake cycle (clamped to 1 MiB, PIPE_TRANSFER_BUFFER). do_work and the think-sleep are skipped (schbench.c:1448), so the only per-cycle work is the wakeup handshake + the two memsets; the report is the PER-WORKER memory-transfer throughput (avg worker transfer = the aggregate rate divided by the worker count, schbench.c:1697,1942-1943,1979) alongside the wakeup-latency table, not request latency.

ktstr does NOT compose -p with -R: in pipe mode it always runs the message-handshake waker (so BOTH pipe memsets fire — a full transfer) and never starts the RPS injector. schbench instead COMPOSES them, half-broken: it has no precedence (-R alone picks the waker, schbench.c:1594), so -p -R runs the RPS injector while the worker-side memset still fires unconditionally (schbench.c:1003-1004) but the waker-side memset — which lives only in xlist_wake_all (schbench.c:980-981) — does not, yielding a degenerate half-pipe. ktstr’s full pipe is the more faithful -p behavior; the realistic use is -p without -R. schbench also zeroes warmuptime in pipe mode (schbench.c:296); ktstr has no warmuptime concept, so that is a no-op here.

§Modes not ported

  • -C (calibrate): a tuning aid that times schbench’s own work loop and forces -L (schbench.c:166, :389). Intentionally out of scope – ktstr measures through the metric path.

Fields§

§message_threads: usize

Number of message threads (schbench.c -m, default 1).

§worker_threads: usize

Worker threads per message thread (schbench.c -t). 0 resolves to ceil(cpuset_cpus / message_threads) – the CPU count of the allocated guest cpuset (the worker’s sched_getaffinity mask, per ruling) divided across the message threads, matching schbench’s 0-default (schbench.c:1849-1852) scoped to the cpuset rather than get_nprocs. See resolve_worker_count and the CLI-parity section above.

§cache_footprint_kib: usize

Per-worker matrix cache footprint in KiB (schbench.c -F, default 256); sets the matrix dimension.

§operations: usize

Matrix multiplications per work cycle (schbench.c -n, default 5).

§sleep_usec: u64

Think-time sleep before the matrix work, microseconds (schbench.c -s, default 100); simulates networking. 0 disables.

§skip_locking: bool

Skip the per-CPU lock around the matrix work (schbench.c -L, default false: locking on).

§requests_per_sec: usize

Fixed request rate, requests/second (schbench.c -R, default 0 = off). 0 selects the default message-handshake mode (each worker is woken by its message thread); non-zero switches to the RPS-injector mode, where a dedicated thread enqueues requests_per_sec requests/second round-robin across the workers (schbench.c run_rps_thread, :1258).

§auto_rps: usize

Auto-RPS target CPU-busy percentage (schbench.c -A, default 0 = off). Non-zero turns on closed-loop rate control: a once-per-second control thread grows/shrinks the live request rate toward this host-busy% target (schbench.c auto_scale_rps, :1180). Setting it seeds the rate to 10 when requests_per_sec is 0 (schbench.c:286), so auto-RPS starts low and climbs.

§split_percent: Option<usize>

Percent of the cache footprint that is PRIVATE per worker thread (schbench.c --split, long-only, 0-100). None = no split: schbench’s legacy all-private single matrix (schbench.c:1405-1408, :1879-1880). Some(p) partitions cache_footprint_kib into a per-thread private matrix (p%) and ONE process-global shared matrix (100-p%) that every worker multiplies into concurrently, reproducing schbench’s cross-core shared-working-set cache contention (schbench.c:1390-1404, :1858-1863). Some(0) = all shared, Some(100) = all private (same matrix sizes as None, but routed through the split branch, matching schbench’s split_specified path). Out-of-range Some(p > 100) panics when the engine consumes it (schbench.c:362-365 exits on the same); the builder also debug-asserts the bound.

§pipe_transfer_bytes: usize

Pipe-mode transfer size in bytes (schbench.c -p/--pipe, default 0 = off, clamped to 1 MiB PIPE_TRANSFER_BUFFER). Non-zero REPLACES the matrix workload with schbench’s memory-transfer simulation: the message thread memsets each woken worker’s per-thread page to 1 and the worker memsets its own page to 2 (schbench.c:980-981/:1003-1004), pipe_transfer_bytes bytes each per cycle, while do_work + the think-sleep are skipped (schbench.c:1448). Reports PER-WORKER memory-transfer throughput (avg worker transfer) rather than request latency.

Implementations§

Source§

impl SchbenchConfig

Source

pub fn message_threads(self, n: usize) -> Self

Set the number of message threads (schbench -m).

Source

pub fn worker_threads(self, n: usize) -> Self

Set worker threads per message thread (schbench -t); 0 = one per allocated CPU.

Source

pub fn cache_footprint_kib(self, kib: usize) -> Self

Set the per-worker matrix cache footprint in KiB (schbench -F).

Source

pub fn operations(self, n: usize) -> Self

Set the matrix multiplications per work cycle (schbench -n).

Source

pub fn sleep_usec(self, usec: u64) -> Self

Set the think-time sleep in microseconds (schbench -s); 0 disables.

Source

pub fn skip_locking(self, skip: bool) -> Self

Skip the per-CPU lock around the matrix work (schbench -L).

Source

pub fn requests_per_sec(self, rps: usize) -> Self

Set the fixed request rate in requests/second (schbench -R); 0 selects the default message-handshake mode, non-zero the RPS-injector mode.

Source

pub fn auto_rps(self, target_pct: usize) -> Self

Set the auto-RPS target host-busy percentage (schbench -A); 0 disables auto-scaling. Non-zero seeds the rate to 10 when requests_per_sec is 0.

Source

pub fn split_percent(self, percent: Option<usize>) -> Self

Set the private/shared cache-footprint split percentage (schbench --split); None (default) = no split, all-private single matrix. Some(p) requires p <= 100: the builder debug-asserts it for early feedback, and the engine hard-panics on an out-of-range value at the consumption boundary in run – the analog of schbench exiting on a bad --split (schbench.c:362-365).

Source

pub fn pipe_transfer_bytes(self, bytes: usize) -> Self

Set the pipe-mode transfer size in bytes (schbench -p/--pipe); 0 (default) = off (the matrix workload). Non-zero switches to schbench’s memory-transfer simulation; values above 1 MiB (PIPE_TRANSFER_BUFFER) are clamped when the engine consumes it (schbench clamps the same, schbench.c:291-294).

Trait Implementations§

Source§

impl Clone for SchbenchConfig

Source§

fn clone(&self) -> SchbenchConfig

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for SchbenchConfig

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for SchbenchConfig

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl<'de> Deserialize<'de> for SchbenchConfig

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Hash for SchbenchConfig

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl PartialEq for SchbenchConfig

Source§

fn eq(&self, other: &SchbenchConfig) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Serialize for SchbenchConfig

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl Eq for SchbenchConfig

Source§

impl StructuralPartialEq for SchbenchConfig

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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> Pointable for T

§

const ALIGN: usize

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
§

impl<T> PolicyExt for T
where T: ?Sized,

§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] only if self and other return Action::Follow. Read more
§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

§

impl<T> MaybeSend for T
where T: Send,

§

impl<T> MaybeSend for T
where T: Send,