`: P95 latency
+//! - ``: P99 latency
+//! - ``: P999 latency (99.9%)
+//!
+//! ## Throughput + Latency + Latency CDF Mode (when both `latency` and `cdf` are `true`)
+//!
+//! When `cdf` is enabled, the latency CDF data will be printed at the end of the same line as the
+//! latency metrics above. In that case, the output will be like the following:
+//!
+//! ```txt
+//! phase 0 repeat 0 duration 1.00 elapsed 1.00 total 1000000 mops 1.00
+//! phase 0 repeat 1 duration 1.00 elapsed 2.00 total 1000000 mops 1.00
+//! phase 0 repeat 2 duration 1.00 elapsed 3.00 total 1000000 mops 1.00
+//! phase 0 finish . duration 1.00 elapsed 3.00 total 3000000 mops 1.00 min_us 0.05 max_us 100.00 avg_us 50.00 p50_us 50.00 p95_us 95.00 p99_us 99.00 p999_us 100.00 cdf_us percentile ...
+//! ```
+//! Since the latency metrics vary a lot between different benchmarks/runs, the number of data
+//! points of the CDF is different. Therefore, it is printed at the end of the output only. It is
+//! printed as a tuple of ` ` where `` is the latency in microseconds and
+//! `` is the percentile of the accumulated operations with latency higher than between
+//! ` - 1` and ``, inclusively, ranging from 0 to 100 (two digit precision).
+//! There can be arbitrary number of tuples. The output ends when the maximum recorded latency is
+//! reached.
+//!
+//! An example of the CDF data will look like:
+//!
+//! ```txt
+//! cdf_us percentile 1 0.00 2 0.00 3 0.00 4 10.00 5 20.00 6 20.00 ...
+//! ```
+//!
+//! It means there are not data points at 1/2/3 microseconds. At 4 microseconds, there are 10% data
+//! points. At 5 microseconds, there are another 10% data points which makes the total percentile
+//! 20.00. At 6 microseconds, there are no data points so the percentile is still 20.00. Users can
+//! post-process the output and make a smooth CDF plot out of it.
+//!
+//! # Server Mode
+//! A key-value client/server implementation is available in kvbench. The server can be backed by
+//! an arbitrary key-value store defined by a TOML file as in a benchmark, and the server can be
+//! started using the `server` mode of the built-in command-line interface.
+//!
+//! To benchmark the server's performance, users can use the built-in client implementation.
+//!
+//! **Reference**
+//!
+//! - [`cmdline()`]: the usage of the default command-line interface.
+//! - [`stores::remote`]: the available options of the key-value store client.
use serde::{Deserialize, Serialize};
use std::cell::RefCell;
@@ -83,7 +241,7 @@ pub enum Operation {
Scan { key: Box<[u8]>, n: usize },
}
-/// A request sent by a client to a server.
+/// A request submitted by an asynchronous store.
#[derive(Serialize, Deserialize, Eq, PartialEq, Clone, Debug)]
pub struct Request {
/// The (usually unique) identifier of the request, or custom data.
@@ -93,7 +251,7 @@ pub struct Request {
pub op: Operation,
}
-/// A response sent by a server to a client.
+/// A response received by an asynchronous store.
#[derive(Serialize, Deserialize, Eq, PartialEq, Clone, Debug)]
pub struct Response {
/// The `id` of the corresponding request.
@@ -153,14 +311,16 @@ impl AsyncResponder for RefCell> {
}
}
-pub mod bench;
+mod bench;
mod cmdline;
-pub mod server;
+mod server;
pub mod stores;
pub mod thread;
-pub mod workload;
+mod workload;
+pub use bench::{BenchmarkOpt, GlobalOpt};
pub use cmdline::cmdline;
+pub use workload::WorkloadOpt;
pub extern crate inventory;
pub extern crate toml;
diff --git a/src/stores.rs b/src/stores.rs
index f8b7469..e9cf6f6 100644
--- a/src/stores.rs
+++ b/src/stores.rs
@@ -1,27 +1,10 @@
//! Adapters for built-in and external key-value stores.
//!
-//! ## Configuration Format
+//! ## Built-in Stores
//!
-//! The configuration of a key-value store is stored in a dictionary named `map`. Therefore, a
-//! store's configuration file looks like the following:
-//!
-//! ```toml
-//! [map]
-//! name = "..."
-//! # option1 = ...
-//! # option2 = ...
-//!
-//! ...
-//! ```
-//! The field `name` must be given and it should be equal to the name registered by the store.
-//! Other than `name`, all the fields are parsed as a string map and will be hand over to the
-//! constructor of the store's constructor function. For available options other than `name`, one
-//! can refer to the module-level documentation of a specific store.
-//!
-//! Similar to the `[global]` secition of a benchmark, the options in a `[map]` section can also
-//! be overwritten via environment variables.
-//! For example, if the user needs to override `x` in `[map]`, setting the environment variable
-//! `map.x` will get the job done.
+//! The usage of built-in stores can be found in the module-level documentations. Please note that
+//! it may be necessary to enable specific features of the crate to enable a certain built-in
+//! store.
//!
//! ## Registering New Stores
//!
diff --git a/src/thread.rs b/src/thread.rs
index 92fa562..fe480d3 100644
--- a/src/thread.rs
+++ b/src/thread.rs
@@ -11,22 +11,30 @@
//! it is with the [`JoinHandle`]. Because the purpose is not general spawn-join but solely for
//! benchmark code, which does not use any return values.
+/// A join handle returned by a spawn function.
pub trait JoinHandle {
+ /// Join the thread, consume the boxed self.
fn join(self: Box);
}
+/// A thread management abstraction.
pub trait Thread {
+ /// Spawn a new thread using a boxed closure.
fn spawn(&self, f: Box) -> Box;
+ /// Yield the current thread.
fn yield_now(&self);
+ /// Pin the current thread to a certain CPU core.
fn pin(&self, core: usize);
}
+/// A zero-sized wrapper for [`std::thread`] functions.
#[derive(Clone)]
-pub(crate) struct DefaultThread;
+pub struct DefaultThread;
-pub(crate) struct DefaultJoinHandle(std::thread::JoinHandle<()>);
+/// A wrapper for [`std::thread::JoinHandle`].
+pub struct DefaultJoinHandle(std::thread::JoinHandle<()>);
impl JoinHandle for DefaultJoinHandle {
fn join(self: Box) {
diff --git a/src/workload.rs b/src/workload.rs
index 333b406..7f31ecf 100644
--- a/src/workload.rs
+++ b/src/workload.rs
@@ -148,37 +148,66 @@ impl KeyGenerator {
/// A set of workload parameters that can be deserialized from a TOML string.
///
-/// This struct is used for interacting with workload configuration files and also create new
-/// [`Workload`] instances. Some options are wrapped in an `Option` type to ease writing
-/// configuration files. If users would like to create a [`Workload`] instance directly using these
-/// options, all fields must be present.
+/// **Note 1**: If an option not explicitly marked optional and it is not specified by both the file
+/// and the global option, its default value will be applied. If it has no default value, an error
+/// will be raised. The precedence of a value is: file > global (after env overridden) > default.
+///
+/// **Note 2**: the sum of all `*_perc` options must be equal to 100.
#[derive(Deserialize, Clone, Debug, PartialEq)]
pub struct WorkloadOpt {
- /// Percentage of `SET` operations (optional, default 0).
+ /// Percentage of `SET` operations.
+ ///
+ /// Must be a non-negative integer if given.
+ ///
+ /// Default: 0.
pub set_perc: Option,
- /// Percentage of `GET` operations (optional, default 0).
+ /// Percentage of `GET` operations.
+ ///
+ /// Must be a non-negative integer if given.
+ ///
+ /// Default: 0.
pub get_perc: Option,
- /// Percentage of `DELETE` operations (optional, default 0).
+ /// Percentage of `DELETE` operations.
+ ///
+ /// Must be a non-negative integer if given.
+ ///
+ /// Default: 0.
pub del_perc: Option,
- /// Percentage of `SCAN` operations (optional, default 0).
+ /// Percentage of `SCAN` operations.
+ ///
+ /// Must be a non-negative integer if given.
+ ///
+ /// Default: 0.
pub scan_perc: Option,
- /// The number of iterations per `SCAN` (only used when `scan_perc` is non-zero, default 10).
+ /// The number of iterations per `SCAN`.
+ ///
+ /// Must be a positive integer if provided.
+ ///
+ /// Default: 10.
pub scan_n: Option,
/// Key length in bytes.
+ ///
+ /// Must be a positive integer.
pub klen: Option,
/// Value length in bytes.
+ ///
+ /// Must be a positive integer.
pub vlen: Option,
/// Minimum key.
+ ///
+ /// Must be a non-negative integer.
pub kmin: Option,
/// Maximum key.
+ ///
+ /// Must be greater than `kmin`.
pub kmax: Option,
/// Key distribution.
@@ -196,12 +225,16 @@ pub struct WorkloadOpt {
/// - "latest": just like Zipfian but the hotspot is the latest key written to the store.
pub dist: String,
- /// The theta parameter for Zipfian distribution. (Optional, default 1.0)
+ /// The theta parameter for Zipfian distribution.
+ ///
+ /// Default: 1.0.
pub zipf_theta: Option,
- /// The hotspot location for Zipfian distribution. (Optional, default 0.0)
+ /// The hotspot location for Zipfian distribution.
///
/// 0.0 means the first key. 0.5 means approximately the middle in the key space.
+ ///
+ /// Default: 0.0.
pub zipf_hotspot: Option,
}