blob: e3ad6e4f3f0764ffdf10b02783d72fcf22d2f0b2 [file] [log] [blame]
use crate::args::simple::BenchmarkKind;
use colored::Colorize;
use std::collections::HashSet;
use std::{
fmt::{Display, Formatter},
time::Duration,
};
use tokio::time::Instant;
#[derive(Debug, Clone, PartialEq)]
pub struct BenchmarkResult {
pub kind: BenchmarkKind,
pub start_timestamp: Instant,
pub end_timestamp: Instant,
pub average_latency: Duration,
pub total_size_bytes: u64,
pub total_messages: u64,
}
pub struct BenchmarkResults {
results: Vec<BenchmarkResult>,
}
impl From<Vec<BenchmarkResult>> for BenchmarkResults {
fn from(results: Vec<BenchmarkResult>) -> Self {
Self { results }
}
}
#[derive(Debug, Clone, PartialEq)]
struct BenchmarkStatistics {
total_throughput: f64,
messages_per_second: f64,
average_latency: f64,
average_throughput: f64,
total_duration: f64,
}
#[derive(Debug, Clone)]
struct ImpossibleBenchmarkKind;
impl BenchmarkResults {
fn get_test_type(&self) -> Result<BenchmarkKind, ImpossibleBenchmarkKind> {
let result_kinds = self
.results
.iter()
.map(|r| r.kind)
.collect::<HashSet<BenchmarkKind>>();
match (
result_kinds.contains(&BenchmarkKind::Poll),
result_kinds.contains(&BenchmarkKind::Send),
) {
(true, true) => Ok(BenchmarkKind::SendAndPoll),
(true, false) => Ok(BenchmarkKind::Poll),
(false, true) => Ok(BenchmarkKind::Send),
(false, false) => Err(ImpossibleBenchmarkKind),
}
}
fn calculate_statitics<F>(&self, mut predicate: F) -> BenchmarkStatistics
where
F: FnMut(&&BenchmarkResult) -> bool,
{
let total_size_bytes = self
.results
.iter()
.filter(&mut predicate)
.map(|r| r.total_size_bytes)
.sum::<u64>();
let total_duration = (self
.results
.iter()
.filter(&mut predicate)
.map(|r| r.end_timestamp - r.start_timestamp)
.sum::<Duration>()
/ self.results.len() as u32)
.as_secs_f64();
let total_messages = self
.results
.iter()
.filter(&mut predicate)
.map(|r| r.total_messages)
.sum::<u64>();
let average_latency = (self
.results
.iter()
.filter(&mut predicate)
.map(|r| r.average_latency)
.sum::<Duration>()
/ self.results.len() as u32)
.as_secs_f64()
* 1000.0;
let average_throughput =
total_size_bytes as f64 / total_duration / 1e6 / self.results.len() as f64;
let total_throughput = total_size_bytes as f64 / total_duration / 1e6;
let messages_per_second = total_messages as f64 / total_duration;
BenchmarkStatistics {
total_throughput,
messages_per_second,
average_latency,
average_throughput,
total_duration,
}
}
}
impl Display for BenchmarkResults {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
if let Ok(test_type) = self.get_test_type() {
if test_type == BenchmarkKind::SendAndPoll {
let producer_statics = self.calculate_statitics(|x| x.kind == BenchmarkKind::Send);
let consumer_statics = self.calculate_statitics(|x| x.kind == BenchmarkKind::Poll);
let producer_info = format!("Producer results: total throughput: {:.2} MB/s, {:.0} messages/s, average latency: {:.2} ms, average throughput: {:.2} MB/s, total duration: {:.2} s",
producer_statics.total_throughput, producer_statics.messages_per_second, producer_statics.average_latency, producer_statics.average_throughput, producer_statics.total_duration).green();
let consumer_info = format!("Consumer results: total throughput: {:.2} MB/s, {:.0} messages/s, average latency: {:.2} ms, average throughput: {:.2} MB/s, total duration: {:.2} s",
consumer_statics.total_throughput, consumer_statics.messages_per_second, consumer_statics.average_latency, consumer_statics.average_throughput, consumer_statics.total_duration).green();
writeln!(f, "{}, {}", producer_info, consumer_info)?;
}
}
let results = self.calculate_statitics(|x| {
x.kind == BenchmarkKind::Send || x.kind == BenchmarkKind::Poll
});
let summary_info = format!("Results: total throughput: {:.2} MB/s, {:.0} messages/s, average latency: {:.2} ms, average throughput: {:.2} MB/s, total duration: {:.2} s",
results.total_throughput, results.messages_per_second, results.average_latency, results.average_throughput, results.total_duration).green();
writeln!(f, "{}", summary_info)
}
}