blueprint_benchmarking/lib.rs
1pub use tokio;
2
3/// The runtime trait that all runtimes must implement.
4pub trait Runtime {
5 /// Runs the given future to completion on the runtime.
6 fn block_on<F>(&self, future: F) -> F::Output
7 where
8 F: blueprint_std::future::Future;
9}
10
11/// The [`tokio`](https://crates.io/crates/tokio) runtime.
12///
13/// This will execute the benchmark using the `tokio` runtime.
14#[derive(Debug, Clone, Copy)]
15pub struct TokioRuntime;
16
17impl Runtime for TokioRuntime {
18 fn block_on<F>(&self, future: F) -> F::Output
19 where
20 F: blueprint_std::future::Future,
21 {
22 let rt = tokio::runtime::Handle::current();
23 rt.block_on(future)
24 }
25}
26
27/// A benchmarking harness.
28#[derive(Debug)]
29#[allow(dead_code)]
30pub struct Bencher<R> {
31 /// The runtime to use for running benchmarks.
32 runtime: R,
33 /// The time at which the benchmark started.
34 started_at: blueprint_std::time::Instant,
35 /// The max number of cores for this benchmark.
36 cores: usize,
37}
38
39/// The results of a benchmark.
40///
41/// This implements [`Display`] to provide a human-readable summary of the benchmark.
42///
43/// [`Display`]: core::fmt::Display
44#[derive(Debug, Clone)]
45pub struct BenchmarkSummary {
46 /// The name of the benchmark.
47 pub name: String,
48 /// The job identifier.
49 pub job_id: u8,
50 /// The duration of the benchmark.
51 pub elapsed: blueprint_std::time::Duration,
52 /// The number of cores the benchmark was run with.
53 pub cores: usize,
54 /// The amount of memory used by the benchmark (in bytes).
55 pub ram_usage: u64,
56}
57
58impl<R: Runtime> Bencher<R> {
59 /// Create a new benchmark harness.
60 ///
61 /// # Examples
62 ///
63 /// ```
64 /// use blueprint_benchmarking::{Bencher, TokioRuntime};
65 ///
66 /// const THREADS: usize = 4;
67 ///
68 /// let bencher = Bencher::new(THREADS, TokioRuntime);
69 /// ```
70 pub fn new(threads: usize, runtime: R) -> Self {
71 Self {
72 runtime,
73 started_at: blueprint_std::time::Instant::now(),
74 cores: threads,
75 }
76 }
77
78 /// Runs the given future on the [`Runtime`].
79 ///
80 /// # Examples
81 ///
82 /// ```no_run
83 /// use blueprint_benchmarking::{Bencher, TokioRuntime};
84 ///
85 /// const THREADS: usize = 4;
86 ///
87 /// let bencher = Bencher::new(THREADS, TokioRuntime);
88 /// bencher.block_on(async {
89 /// // Do some work...
90 /// });
91 /// ```
92 pub fn block_on<F>(&self, future: F) -> F::Output
93 where
94 F: blueprint_std::future::Future,
95 {
96 self.runtime.block_on(future)
97 }
98
99 /// Ends the benchmark and returns a summary.
100 ///
101 /// # Panics
102 ///
103 /// This will panic in the event it cannot determine the process ID.
104 ///
105 /// # Examples
106 ///
107 /// ```no_run
108 /// use blueprint_benchmarking::{Bencher, TokioRuntime};
109 /// const THREADS: usize = 4;
110 ///
111 /// let bencher = Bencher::new(THREADS, TokioRuntime);
112 /// bencher.block_on(async {
113 /// // Do some work...
114 /// });
115 ///
116 /// let summary = bencher.stop("my_benchmark", 0);
117 /// println!("{}", summary);
118 /// ```
119 #[cfg(feature = "std")] // TODO: Benchmark execution time for WASM?
120 #[allow(clippy::needless_pass_by_value)]
121 pub fn stop<N: ToString>(&self, name: N, job_id: u8) -> BenchmarkSummary {
122 let pid = sysinfo::get_current_pid().expect("Failed to get current process ID");
123 let s = sysinfo::System::new_all();
124 let process = s
125 .process(pid)
126 .expect("Failed to get current process from the system");
127 let ram_usage = process.memory();
128 BenchmarkSummary {
129 name: name.to_string(),
130 job_id,
131 elapsed: self.started_at.elapsed(),
132 cores: self.cores,
133 ram_usage,
134 }
135 }
136}
137
138impl blueprint_std::fmt::Display for BenchmarkSummary {
139 #[allow(clippy::cast_precision_loss)]
140 fn fmt(&self, f: &mut blueprint_std::fmt::Formatter<'_>) -> blueprint_std::fmt::Result {
141 const KB: f32 = 1024.00;
142 const MB: f32 = 1024.00 * KB;
143 const GB: f32 = 1024.00 * MB;
144 let ram_usage = self.ram_usage as f32;
145 let (ram_usage, unit) = if ram_usage < KB {
146 (ram_usage, "B")
147 } else if ram_usage < MB {
148 (ram_usage / KB, "KB")
149 } else if ram_usage < GB {
150 (ram_usage / MB, "MB")
151 } else {
152 (ram_usage / GB, "GB")
153 };
154
155 write!(
156 f,
157 "Benchmark: {}\nJob ID: {}\nElapsed: {:?}\nvCPU: {}\nRAM Usage: {ram_usage:.2} {unit}\n",
158 self.name, self.job_id, self.elapsed, self.cores,
159 )
160 }
161}