Move Throughput into sc-sysinfo (#12368)

* move Throughput to sc-sysinfo

* replace u64

* fix in tests

* change Throughput

* refactored Throughput

* fixes

* moved tests & fixes

* custom serializer

* note

* fix serializer

* forgot to remove

* deserialize

* functioning deserialization :)

* try to make clipply happy

* Serialize as function

* test HwBench

* rename

* fix serialization

* deserialize as function

* unused import

* move serialize/deserialize

* don't serialize none

* remove nonsense

* remove nonsense comment :P

* fixes

* remove all the todos

* return enum

* fixes

* fix nit

* improve docs & readability

* Update client/sysinfo/src/sysinfo.rs

Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* fix all the nits

* rename

* fix

* Update client/sysinfo/src/sysinfo.rs

Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>

* remove unit from serialization

* Update utils/frame/benchmarking-cli/src/machine/hardware.rs

Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
This commit is contained in:
Sergej Sakac
2022-11-04 18:13:57 +01:00
committed by GitHub
parent 6ba635fcff
commit 65b285e632
8 changed files with 236 additions and 144 deletions
@@ -18,8 +18,40 @@
//! Contains types to define hardware requirements.
use lazy_static::lazy_static;
use serde::{Deserialize, Serialize};
use std::fmt;
use sc_sysinfo::Throughput;
use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
use sp_std::{fmt, fmt::Formatter};
/// Serializes throughput into MiBs and represents it as `f64`.
fn serialize_throughput_as_f64<S>(throughput: &Throughput, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_f64(throughput.as_mibs())
}
struct ThroughputVisitor;
impl<'de> Visitor<'de> for ThroughputVisitor {
type Value = Throughput;
fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
formatter.write_str("A value that is a f64.")
}
fn visit_f64<E>(self, value: f64) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Throughput::from_mibs(value))
}
}
fn deserialize_throughput<'de, D>(deserializer: D) -> Result<Throughput, D::Error>
where
D: Deserializer<'de>,
{
Ok(deserializer.deserialize_f64(ThroughputVisitor))?
}
lazy_static! {
/// The hardware requirements as measured on reference hardware.
@@ -45,6 +77,10 @@ pub struct Requirement {
/// The metric to measure.
pub metric: Metric,
/// The minimal throughput that needs to be archived for this requirement.
#[serde(
serialize_with = "serialize_throughput_as_f64",
deserialize_with = "deserialize_throughput"
)]
pub minimum: Throughput,
}
@@ -65,17 +101,6 @@ pub enum Metric {
DiskRndWrite,
}
/// Throughput as measured in bytes per second.
#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq)]
pub enum Throughput {
/// KiB/s
KiBs(f64),
/// MiB/s
MiBs(f64),
/// GiB/s
GiBs(f64),
}
impl Metric {
/// The category of the metric.
pub fn category(&self) -> &'static str {
@@ -98,70 +123,9 @@ impl Metric {
}
}
const KIBIBYTE: f64 = 1024.0;
impl Throughput {
/// The unit of the metric.
pub fn unit(&self) -> &'static str {
match self {
Self::KiBs(_) => "KiB/s",
Self::MiBs(_) => "MiB/s",
Self::GiBs(_) => "GiB/s",
}
}
/// [`Self`] as number of byte/s.
pub fn to_bs(&self) -> f64 {
self.to_kibs() * KIBIBYTE
}
/// [`Self`] as number of kibibyte/s.
pub fn to_kibs(&self) -> f64 {
self.to_mibs() * KIBIBYTE
}
/// [`Self`] as number of mebibyte/s.
pub fn to_mibs(&self) -> f64 {
self.to_gibs() * KIBIBYTE
}
/// [`Self`] as number of gibibyte/s.
pub fn to_gibs(&self) -> f64 {
match self {
Self::KiBs(k) => *k / (KIBIBYTE * KIBIBYTE),
Self::MiBs(m) => *m / KIBIBYTE,
Self::GiBs(g) => *g,
}
}
/// Normalizes [`Self`] to use the larges unit possible.
pub fn normalize(&self) -> Self {
let bs = self.to_bs();
if bs >= KIBIBYTE * KIBIBYTE * KIBIBYTE {
Self::GiBs(self.to_gibs())
} else if bs >= KIBIBYTE * KIBIBYTE {
Self::MiBs(self.to_mibs())
} else {
Self::KiBs(self.to_kibs())
}
}
}
impl fmt::Display for Throughput {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let normalized = self.normalize();
match normalized {
Self::KiBs(s) | Self::MiBs(s) | Self::GiBs(s) =>
write!(f, "{:.2?} {}", s, normalized.unit()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use sp_runtime::assert_eq_error_rate_float;
/// `SUBSTRATE_REFERENCE_HARDWARE` can be en- and decoded.
#[test]
@@ -171,21 +135,4 @@ mod tests {
assert_eq!(decoded, SUBSTRATE_REFERENCE_HARDWARE.clone());
}
/// Test the [`Throughput`].
#[test]
fn throughput_works() {
/// Float precision.
const EPS: f64 = 0.1;
let gib = Throughput::GiBs(14.324);
assert_eq_error_rate_float!(14.324, gib.to_gibs(), EPS);
assert_eq_error_rate_float!(14667.776, gib.to_mibs(), EPS);
assert_eq_error_rate_float!(14667.776 * 1024.0, gib.to_kibs(), EPS);
assert_eq!("14.32 GiB/s", gib.to_string());
assert_eq!("14.32 GiB/s", gib.normalize().to_string());
let mib = Throughput::MiBs(1029.0);
assert_eq!("1.00 GiB/s", mib.to_string());
}
}
@@ -30,11 +30,11 @@ use sc_cli::{CliConfiguration, Result, SharedParams};
use sc_service::Configuration;
use sc_sysinfo::{
benchmark_cpu, benchmark_disk_random_writes, benchmark_disk_sequential_writes,
benchmark_memory, benchmark_sr25519_verify, ExecutionLimit,
benchmark_memory, benchmark_sr25519_verify, ExecutionLimit, Throughput,
};
use crate::shared::check_build_profile;
pub use hardware::{Metric, Requirement, Requirements, Throughput, SUBSTRATE_REFERENCE_HARDWARE};
pub use hardware::{Metric, Requirement, Requirements, SUBSTRATE_REFERENCE_HARDWARE};
/// Command to benchmark the hardware.
///
@@ -128,8 +128,9 @@ impl MachineCmd {
/// Benchmarks a specific metric of the hardware and judges the resulting score.
fn run_benchmark(&self, requirement: &Requirement, dir: &Path) -> Result<BenchResult> {
// Dispatch the concrete function from `sc-sysinfo`.
let score = self.measure(&requirement.metric, dir)?;
let rel_score = score.to_bs() / requirement.minimum.to_bs();
let rel_score = score.as_bytes() / requirement.minimum.as_bytes();
// Sanity check if the result is off by factor >100x.
if rel_score >= 100.0 || rel_score <= 0.01 {
@@ -147,13 +148,11 @@ impl MachineCmd {
let memory_limit = ExecutionLimit::from_secs_f32(self.memory_duration);
let score = match metric {
Metric::Blake2256 => Throughput::MiBs(benchmark_cpu(hash_limit) as f64),
Metric::Sr25519Verify => Throughput::MiBs(benchmark_sr25519_verify(verify_limit)),
Metric::MemCopy => Throughput::MiBs(benchmark_memory(memory_limit) as f64),
Metric::DiskSeqWrite =>
Throughput::MiBs(benchmark_disk_sequential_writes(disk_limit, dir)? as f64),
Metric::DiskRndWrite =>
Throughput::MiBs(benchmark_disk_random_writes(disk_limit, dir)? as f64),
Metric::Blake2256 => benchmark_cpu(hash_limit),
Metric::Sr25519Verify => benchmark_sr25519_verify(verify_limit),
Metric::MemCopy => benchmark_memory(memory_limit),
Metric::DiskSeqWrite => benchmark_disk_sequential_writes(disk_limit, dir)?,
Metric::DiskRndWrite => benchmark_disk_random_writes(disk_limit, dir)?,
};
Ok(score)
}
@@ -1,32 +1,22 @@
[
{
"metric": "Blake2256",
"minimum": {
"MiBs": 1029.0
}
"minimum": 1029.0
},
{
"metric": "Sr25519Verify",
"minimum": {
"KiBs": 666.0
}
"minimum": 0.650391
},
{
"metric": "MemCopy",
"minimum": {
"GiBs": 14.323
}
"minimum": 14666.752
},
{
"metric": "DiskSeqWrite",
"minimum": {
"MiBs": 450.0
}
"minimum": 450.0
},
{
"metric": "DiskRndWrite",
"minimum": {
"MiBs": 200.0
}
"minimum": 200.0
}
]