mirror of
https://github.com/rosenpass/rosenpass.git
synced 2025-12-06 04:40:55 -08:00
Use serde for JSON-encoding benchmark data
This commit is contained in:
committed by
Jan Winkelmann
parent
15ae4b4ae5
commit
7908359eab
5
Cargo.lock
generated
5
Cargo.lock
generated
@@ -2068,6 +2068,7 @@ dependencies = [
|
|||||||
"rosenpass-wireguard-broker",
|
"rosenpass-wireguard-broker",
|
||||||
"rustix",
|
"rustix",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"serial_test",
|
"serial_test",
|
||||||
"signal-hook",
|
"signal-hook",
|
||||||
"signal-hook-mio",
|
"signal-hook-mio",
|
||||||
@@ -2376,9 +2377,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.139"
|
version = "1.0.140"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6"
|
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ test_bin = "0.4.0"
|
|||||||
criterion = "0.5.1"
|
criterion = "0.5.1"
|
||||||
allocator-api2-tests = "0.2.15"
|
allocator-api2-tests = "0.2.15"
|
||||||
procspawn = { version = "1.0.1", features = ["test-support"] }
|
procspawn = { version = "1.0.1", features = ["test-support"] }
|
||||||
|
serde_json = { version = "1.0.140" }
|
||||||
|
|
||||||
#Broker dependencies (might need cleanup or changes)
|
#Broker dependencies (might need cleanup or changes)
|
||||||
wireguard-uapi = { version = "3.0.0", features = ["xplatform"] }
|
wireguard-uapi = { version = "3.0.0", features = ["xplatform"] }
|
||||||
|
|||||||
@@ -91,9 +91,9 @@ serial_test = { workspace = true }
|
|||||||
procspawn = { workspace = true }
|
procspawn = { workspace = true }
|
||||||
tempfile = { workspace = true }
|
tempfile = { workspace = true }
|
||||||
rustix = { workspace = true }
|
rustix = { workspace = true }
|
||||||
|
serde_json = { workspace = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
#default = ["experiment_libcrux_all"]
|
|
||||||
experiment_cookie_dos_mitigation = []
|
experiment_cookie_dos_mitigation = []
|
||||||
experiment_memfd_secret = ["rosenpass-wireguard-broker/experiment_memfd_secret"]
|
experiment_memfd_secret = ["rosenpass-wireguard-broker/experiment_memfd_secret"]
|
||||||
experiment_libcrux_all = ["rosenpass-ciphers/experiment_libcrux_all"]
|
experiment_libcrux_all = ["rosenpass-ciphers/experiment_libcrux_all"]
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
use std::io::{self, Write};
|
use std::{
|
||||||
use std::time::{Duration, Instant};
|
collections::HashMap,
|
||||||
use std::{collections::HashMap, hint::black_box, ops::DerefMut};
|
hint::black_box,
|
||||||
|
ops::DerefMut,
|
||||||
|
time::{Duration, Instant},
|
||||||
|
};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
@@ -9,11 +12,12 @@ use libcrux_test_utils::tracing::{EventType, Trace as _};
|
|||||||
use rosenpass_cipher_traits::primitives::Kem;
|
use rosenpass_cipher_traits::primitives::Kem;
|
||||||
use rosenpass_ciphers::StaticKem;
|
use rosenpass_ciphers::StaticKem;
|
||||||
use rosenpass_secret_memory::secret_policy_try_use_memfd_secrets;
|
use rosenpass_secret_memory::secret_policy_try_use_memfd_secrets;
|
||||||
use rosenpass_util::trace_bench::RpEventType;
|
use rosenpass_util::trace_bench::RpEvent;
|
||||||
|
|
||||||
use rosenpass::protocol::basic_types::{MsgBuf, SPk, SSk, SymKey};
|
use rosenpass::protocol::basic_types::{MsgBuf, SPk, SSk, SymKey};
|
||||||
use rosenpass::protocol::osk_domain_separator::OskDomainSeparator;
|
use rosenpass::protocol::osk_domain_separator::OskDomainSeparator;
|
||||||
use rosenpass::protocol::{CryptoServer, HandleMsgResult, PeerPtr, ProtocolVersion};
|
use rosenpass::protocol::{CryptoServer, HandleMsgResult, PeerPtr, ProtocolVersion};
|
||||||
|
use serde::ser::SerializeStruct;
|
||||||
|
|
||||||
const ITERATIONS: usize = 100;
|
const ITERATIONS: usize = 100;
|
||||||
|
|
||||||
@@ -124,15 +128,30 @@ fn main() {
|
|||||||
(v02, &v03_with_marker[1..])
|
(v02, &v03_with_marker[1..])
|
||||||
};
|
};
|
||||||
|
|
||||||
// Perform statistical analysis on both trace sections and write results as JSON
|
// Perform statistical analysis on both trace sections
|
||||||
write_json_arrays(
|
let analysis_v02 = statistical_analysis(trace_v02);
|
||||||
&mut std::io::stdout(), // Write to standard output
|
let analysis_v03 = statistical_analysis(trace_v03);
|
||||||
vec![
|
|
||||||
("V02", statistical_analysis(trace_v02.to_vec())),
|
// Transform analysis results to JSON-encodable data type
|
||||||
("V03", statistical_analysis(trace_v03.to_vec())),
|
let stats_v02 = analysis_v02
|
||||||
],
|
.iter()
|
||||||
)
|
.map(|(label, agg_stat)| JsonAggregateStat {
|
||||||
.expect("error writing json data");
|
protocol_version: "V02",
|
||||||
|
label,
|
||||||
|
agg_stat,
|
||||||
|
});
|
||||||
|
let stats_v03 = analysis_v03
|
||||||
|
.iter()
|
||||||
|
.map(|(label, agg_stat)| JsonAggregateStat {
|
||||||
|
protocol_version: "V03",
|
||||||
|
label,
|
||||||
|
agg_stat: &agg_stat,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Write results as JSON
|
||||||
|
let stats_all: Vec<_> = stats_v02.chain(stats_v03).collect();
|
||||||
|
let stats_json = serde_json::to_string_pretty(&stats_all).expect("error encoding to json");
|
||||||
|
println!("{stats_json}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Performs a simple statistical analysis:
|
/// Performs a simple statistical analysis:
|
||||||
@@ -140,7 +159,7 @@ fn main() {
|
|||||||
/// - extracts durations of spamns
|
/// - extracts durations of spamns
|
||||||
/// - filters out empty bins
|
/// - filters out empty bins
|
||||||
/// - calculates aggregate statistics (mean, std dev)
|
/// - calculates aggregate statistics (mean, std dev)
|
||||||
fn statistical_analysis(trace: Vec<RpEventType>) -> Vec<(&'static str, AggregateStat<Duration>)> {
|
fn statistical_analysis(trace: &[RpEvent]) -> Vec<(&'static str, AggregateStat<Duration>)> {
|
||||||
bin_events(trace)
|
bin_events(trace)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(label, spans)| (label, extract_span_durations(label, spans.as_slice())))
|
.map(|(label, spans)| (label, extract_span_durations(label, spans.as_slice())))
|
||||||
@@ -149,44 +168,6 @@ fn statistical_analysis(trace: Vec<RpEventType>) -> Vec<(&'static str, Aggregate
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Takes an iterator of ("protocol_version", iterator_of_stats) pairs and writes them
|
|
||||||
/// as a single flat JSON array to the provided writer.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
/// * `w` - The writer to output JSON to (e.g., stdout, file).
|
|
||||||
/// * `item_groups` - An iterator producing tuples `(version, stats): (&'static str, II)`.
|
|
||||||
/// Here `II` is itself an iterator producing `(label, agg_stat): (&'static str, AggregateStat<Duration>)`,
|
|
||||||
/// where the label is the label of the span, e.g. "IHI2".
|
|
||||||
///
|
|
||||||
/// # Type Parameters
|
|
||||||
/// * `W` - A type that implements `std::io::Write`.
|
|
||||||
/// * `II` - An iterator type yielding (`&'static str`, `AggregateStat<Duration>`).
|
|
||||||
fn write_json_arrays<W: Write, II: IntoIterator<Item = (&'static str, AggregateStat<Duration>)>>(
|
|
||||||
w: &mut W,
|
|
||||||
item_groups: impl IntoIterator<Item = (&'static str, II)>,
|
|
||||||
) -> io::Result<()> {
|
|
||||||
// Flatten the groups into a single iterator of (protocol_version, label, stats)
|
|
||||||
let iter = item_groups.into_iter().flat_map(|(version, items)| {
|
|
||||||
items
|
|
||||||
.into_iter()
|
|
||||||
.map(move |(label, agg_stat)| (version, label, agg_stat))
|
|
||||||
});
|
|
||||||
let mut delim = ""; // Start with no delimiter
|
|
||||||
|
|
||||||
// Start the JSON array
|
|
||||||
write!(w, "[")?;
|
|
||||||
|
|
||||||
// Write the flattened statistics as JSON objects, separated by commas.
|
|
||||||
for (version, label, agg_stat) in iter {
|
|
||||||
write!(w, "{delim}")?; // Write delimiter (empty for first item, "," for subsequent)
|
|
||||||
agg_stat.write_json_ns(label, version, w)?; // Write the JSON object for the stat entry
|
|
||||||
delim = ","; // Set delimiter for the next iteration
|
|
||||||
}
|
|
||||||
|
|
||||||
// End the JSON array
|
|
||||||
write!(w, "]")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Used to group benchmark results in visualizations
|
/// Used to group benchmark results in visualizations
|
||||||
enum RunTimeGroup {
|
enum RunTimeGroup {
|
||||||
/// For particularly long operations.
|
/// For particularly long operations.
|
||||||
@@ -239,13 +220,13 @@ enum StatEntry {
|
|||||||
|
|
||||||
/// Takes a flat list of events and organizes them into a HashMap where keys
|
/// Takes a flat list of events and organizes them into a HashMap where keys
|
||||||
/// are event labels and values are vectors of events with that label.
|
/// are event labels and values are vectors of events with that label.
|
||||||
fn bin_events(events: Vec<RpEventType>) -> HashMap<&'static str, Vec<RpEventType>> {
|
fn bin_events(events: &[RpEvent]) -> HashMap<&'static str, Vec<RpEvent>> {
|
||||||
let mut spans = HashMap::<_, Vec<_>>::new();
|
let mut spans = HashMap::<_, Vec<_>>::new();
|
||||||
for event in events {
|
for event in events {
|
||||||
// Get the vector for the event's label, or create a new one
|
// Get the vector for the event's label, or create a new one
|
||||||
let spans_for_label = spans.entry(event.label).or_default();
|
let spans_for_label = spans.entry(event.label).or_default();
|
||||||
// Add the event to the vector
|
// Add the event to the vector
|
||||||
spans_for_label.push(event);
|
spans_for_label.push(event.clone());
|
||||||
}
|
}
|
||||||
spans
|
spans
|
||||||
}
|
}
|
||||||
@@ -253,7 +234,7 @@ fn bin_events(events: Vec<RpEventType>) -> HashMap<&'static str, Vec<RpEventType
|
|||||||
/// Processes a list of events (assumed to be for the same label), matching
|
/// Processes a list of events (assumed to be for the same label), matching
|
||||||
/// `SpanOpen` and `SpanClose` events to calculate the duration of each span.
|
/// `SpanOpen` and `SpanClose` events to calculate the duration of each span.
|
||||||
/// It handles potentially interleaved spans correctly.
|
/// It handles potentially interleaved spans correctly.
|
||||||
fn extract_span_durations(label: &str, events: &[RpEventType]) -> Vec<Duration> {
|
fn extract_span_durations(label: &str, events: &[RpEvent]) -> Vec<Duration> {
|
||||||
let mut processing_list: Vec<StatEntry> = vec![]; // List to track open spans and final durations
|
let mut processing_list: Vec<StatEntry> = vec![]; // List to track open spans and final durations
|
||||||
|
|
||||||
for entry in events {
|
for entry in events {
|
||||||
@@ -313,6 +294,7 @@ fn extract_span_durations(label: &str, events: &[RpEventType]) -> Vec<Duration>
|
|||||||
/// Stores the mean, standard deviation, relative standard deviation (sd/mean),
|
/// Stores the mean, standard deviation, relative standard deviation (sd/mean),
|
||||||
/// and the number of samples used for calculation.
|
/// and the number of samples used for calculation.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
#[allow(dead_code)]
|
||||||
struct AggregateStat<T> {
|
struct AggregateStat<T> {
|
||||||
/// Average duration.
|
/// Average duration.
|
||||||
mean_duration: T,
|
mean_duration: T,
|
||||||
@@ -362,32 +344,33 @@ impl AggregateStat<Duration> {
|
|||||||
sample_size,
|
sample_size,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Writes the statistics as a JSON object to the provided writer.
|
struct JsonAggregateStat<'a, T> {
|
||||||
/// Includes metadata like label, protocol_version, OS, architecture, and run time group.
|
agg_stat: &'a AggregateStat<T>,
|
||||||
///
|
label: &'a str,
|
||||||
/// # Arguments
|
protocol_version: &'a str,
|
||||||
/// * `label` - The specific benchmark/span label.
|
}
|
||||||
/// * `protocol_version` - Version of the protocol that is benchmarked.
|
|
||||||
/// * `w` - The output writer (must implement `std::io::Write`).
|
impl<'a> serde::Serialize for JsonAggregateStat<'a, Duration> {
|
||||||
fn write_json_ns(
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
&self,
|
where
|
||||||
label: &str,
|
S: serde::Serializer,
|
||||||
protocol_version: &str,
|
{
|
||||||
w: &mut impl io::Write,
|
let mut stat = serializer.serialize_struct("AggregateStat", 9)?;
|
||||||
) -> io::Result<()> {
|
stat.serialize_field("name", self.label)?;
|
||||||
// Format the JSON string using measured values and environment constants
|
stat.serialize_field("unit", "ns/iter")?;
|
||||||
writeln!(
|
stat.serialize_field("value", &self.agg_stat.mean_duration.as_nanos().to_string())?;
|
||||||
w,
|
stat.serialize_field(
|
||||||
r#"{{"name":"{name}", "unit":"ns/iter", "value":"{value}", "range":"± {range}", "protocol version":"{protocol_version}", "sample size":"{sample_size}", "operating system":"{os}", "architecture":"{arch}", "run time":"{run_time}"}}"#,
|
"range",
|
||||||
name = label, // Benchmark name
|
&format!("± {}", self.agg_stat.sd_duration.as_nanos()),
|
||||||
value = self.mean_duration.as_nanos(), // Mean duration in nanoseconds
|
)?;
|
||||||
range = self.sd_duration.as_nanos(), // Standard deviation in nanoseconds
|
stat.serialize_field("protocol version", self.protocol_version)?;
|
||||||
sample_size = self.sample_size, // Number of samples
|
stat.serialize_field("sample size", &self.agg_stat.sample_size)?;
|
||||||
os = std::env::consts::OS, // Operating system
|
stat.serialize_field("operating system", std::env::consts::OS)?;
|
||||||
arch = std::env::consts::ARCH, // CPU architecture
|
stat.serialize_field("architecture", std::env::consts::ARCH)?;
|
||||||
run_time = run_time_group(label), // Run time group category (long, medium, etc.)
|
stat.serialize_field("run time", &run_time_group(self.label).to_string())?;
|
||||||
protocol_version = protocol_version // Overall protocol_version (e.g., protocol version)
|
|
||||||
)
|
stat.end()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ static TRACE: OnceLock<RpTrace> = OnceLock::new();
|
|||||||
pub type RpTrace = tracing::MutexTrace<&'static str, Instant>;
|
pub type RpTrace = tracing::MutexTrace<&'static str, Instant>;
|
||||||
|
|
||||||
/// The trace event type used to trace Rosenpass for performance measurement.
|
/// The trace event type used to trace Rosenpass for performance measurement.
|
||||||
pub type RpEventType = tracing::TraceEvent<&'static str, Instant>;
|
pub type RpEvent = tracing::TraceEvent<&'static str, Instant>;
|
||||||
|
|
||||||
// Re-export to make functionality available and callers don't need to also directly depend on
|
// Re-export to make functionality available and callers don't need to also directly depend on
|
||||||
// [`libcrux_test_utils`].
|
// [`libcrux_test_utils`].
|
||||||
|
|||||||
Reference in New Issue
Block a user