diff --git a/bin/node/cli/src/service.rs b/bin/node/cli/src/service.rs index cdee61af3f500..ace24f186d4c2 100644 --- a/bin/node/cli/src/service.rs +++ b/bin/node/cli/src/service.rs @@ -21,6 +21,7 @@ //! Service implementation. Specialized wrapper over substrate service. use codec::Encode; +use frame_benchmarking_cli::SUBSTRATE_REFERENCE_HARDWARE; use frame_system_rpc_runtime_api::AccountNonceApi; use futures::prelude::*; use kitchensink_runtime::RuntimeApi; @@ -320,7 +321,11 @@ pub fn new_full_base( let hwbench = if !disable_hardware_benchmarks { config.database.path().map(|database_path| { let _ = std::fs::create_dir_all(&database_path); - sc_sysinfo::gather_hwbench(Some(database_path)) + sc_sysinfo::gather_hwbench( + Some(database_path), + SUBSTRATE_REFERENCE_HARDWARE.clone(), + config.role.is_authority(), + ) }) } else { None diff --git a/client/sysinfo/src/lib.rs b/client/sysinfo/src/lib.rs index cef5a4d210df1..f623bdae53e66 100644 --- a/client/sysinfo/src/lib.rs +++ b/client/sysinfo/src/lib.rs @@ -29,7 +29,8 @@ mod sysinfo_linux; pub use sysinfo::{ benchmark_cpu, benchmark_disk_random_writes, benchmark_disk_sequential_writes, benchmark_memory, benchmark_sr25519_verify, gather_hwbench, gather_sysinfo, - serialize_throughput, serialize_throughput_option, Throughput, + serialize_throughput, serialize_throughput_option, Metric, Requirement, Requirements, + Throughput, }; /// The operating system part of the current target triplet. diff --git a/client/sysinfo/src/sysinfo.rs b/client/sysinfo/src/sysinfo.rs index c66a6f6a62aed..800df4b8a41a9 100644 --- a/client/sysinfo/src/sysinfo.rs +++ b/client/sysinfo/src/sysinfo.rs @@ -21,10 +21,10 @@ use crate::{ExecutionLimit, HwBench}; use sc_telemetry::SysInfo; use sp_core::{sr25519, Pair}; use sp_io::crypto::sr25519_verify; -use sp_std::{fmt, prelude::*}; +use sp_std::{fmt, fmt::Formatter, prelude::*}; use rand::{seq::SliceRandom, Rng, RngCore}; -use serde::Serializer; +use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer}; use std::{ fs::File, io::{Seek, SeekFrom, Write}, @@ -33,6 +33,43 @@ use std::{ time::{Duration, Instant}, }; +/// A single hardware metric. +#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq)] +pub enum Metric { + /// SR25519 signature verification. + Sr25519Verify, + /// Blake2-256 hashing algorithm. + Blake2256, + /// Copying data in RAM. + MemCopy, + /// Disk sequential write. + DiskSeqWrite, + /// Disk random write. + DiskRndWrite, +} + +impl Metric { + /// The category of the metric. + pub fn category(&self) -> &'static str { + match self { + Self::Sr25519Verify | Self::Blake2256 => "CPU", + Self::MemCopy => "Memory", + Self::DiskSeqWrite | Self::DiskRndWrite => "Disk", + } + } + + /// The name of the metric. It is always prefixed by the [`self.category()`]. + pub fn name(&self) -> &'static str { + match self { + Self::Sr25519Verify => "SR25519-Verify", + Self::Blake2256 => "BLAKE2-256", + Self::MemCopy => "Copy", + Self::DiskSeqWrite => "Seq Write", + Self::DiskRndWrite => "Rnd Write", + } + } +} + /// The unit in which the [`Throughput`] (bytes per second) is denoted. pub enum Unit { GiBs, @@ -137,6 +174,54 @@ where serializer.serialize_none() } +/// Serializes throughput into MiBs and represents it as `f64`. +fn serialize_throughput_as_f64(throughput: &Throughput, serializer: S) -> Result +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(self, value: f64) -> Result + where + E: serde::de::Error, + { + Ok(Throughput::from_mibs(value)) + } +} + +fn deserialize_throughput<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + Ok(deserializer.deserialize_f64(ThroughputVisitor))? +} + +/// Multiple requirements for the hardware. +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +pub struct Requirements(pub Vec); + +/// A single requirement for the hardware. +#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq)] +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, +} + #[inline(always)] pub(crate) fn benchmark( name: &str, @@ -503,8 +588,14 @@ pub fn benchmark_sr25519_verify(limit: ExecutionLimit) -> Throughput { /// Benchmarks the hardware and returns the results of those benchmarks. /// -/// Optionally accepts a path to a `scratch_directory` to use to benchmark the disk. -pub fn gather_hwbench(scratch_directory: Option<&Path>) -> HwBench { +/// Optionally accepts a path to a `scratch_directory` to use to benchmark the +/// disk. Also accepts the `requirements` for the hardware benchmark and a +/// boolean to specify if the node is an authority. +pub fn gather_hwbench( + scratch_directory: Option<&Path>, + requirements: Requirements, + is_authority: bool, +) -> HwBench { #[allow(unused_mut)] let mut hwbench = HwBench { cpu_hashrate_score: benchmark_cpu(DEFAULT_CPU_EXECUTION_LIMIT), @@ -534,9 +625,45 @@ pub fn gather_hwbench(scratch_directory: Option<&Path>) -> HwBench { }; } + if is_authority { + ensure_requirements(hwbench.clone(), requirements); + } + hwbench } +fn ensure_requirements(hwbench: HwBench, requirements: Requirements) { + let mut failed = 0; + for requirement in requirements.0.iter() { + match requirement.metric { + Metric::Blake2256 => + if requirement.minimum > hwbench.cpu_hashrate_score { + failed += 1; + }, + Metric::MemCopy => + if requirement.minimum > hwbench.memory_memcpy_score { + failed += 1; + }, + Metric::DiskSeqWrite => + if let Some(score) = hwbench.disk_sequential_write_score { + if requirement.minimum > score { + failed += 1; + } + }, + Metric::DiskRndWrite => + if let Some(score) = hwbench.disk_random_write_score { + if requirement.minimum > score { + failed += 1; + } + }, + Metric::Sr25519Verify => {}, + } + } + if failed != 0 { + log::warn!("⚠️ Your hardware performance score was less than expected for role 'Authority'. See https://wiki.polkadot.network/docs/maintain-guides-how-to-validate-polkadot#reference-hardware"); + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/utils/frame/benchmarking-cli/src/lib.rs b/utils/frame/benchmarking-cli/src/lib.rs index a44a208b16ae9..5723a8038fdbb 100644 --- a/utils/frame/benchmarking-cli/src/lib.rs +++ b/utils/frame/benchmarking-cli/src/lib.rs @@ -27,7 +27,7 @@ mod storage; pub use block::BlockCmd; pub use extrinsic::{ExtrinsicBuilder, ExtrinsicCmd, ExtrinsicFactory}; -pub use machine::{MachineCmd, Requirements, SUBSTRATE_REFERENCE_HARDWARE}; +pub use machine::{MachineCmd, SUBSTRATE_REFERENCE_HARDWARE}; pub use overhead::OverheadCmd; pub use pallet::PalletCmd; pub use sc_service::BasePath; diff --git a/utils/frame/benchmarking-cli/src/machine/hardware.rs b/utils/frame/benchmarking-cli/src/machine/hardware.rs index 50c88ec74646c..318c193d7c08c 100644 --- a/utils/frame/benchmarking-cli/src/machine/hardware.rs +++ b/utils/frame/benchmarking-cli/src/machine/hardware.rs @@ -18,40 +18,7 @@ //! Contains types to define hardware requirements. use lazy_static::lazy_static; -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(throughput: &Throughput, serializer: S) -> Result -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(self, value: f64) -> Result - where - E: serde::de::Error, - { - Ok(Throughput::from_mibs(value)) - } -} - -fn deserialize_throughput<'de, D>(deserializer: D) -> Result -where - D: Deserializer<'de>, -{ - Ok(deserializer.deserialize_f64(ThroughputVisitor))? -} +use sc_sysinfo::Requirements; lazy_static! { /// The hardware requirements as measured on reference hardware. @@ -67,62 +34,6 @@ lazy_static! { }; } -/// Multiple requirements for the hardware. -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] -pub struct Requirements(pub Vec); - -/// A single requirement for the hardware. -#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq)] -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, -} - -/// A single hardware metric. -/// -/// The implementation of these is in `sc-sysinfo`. -#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq)] -pub enum Metric { - /// SR25519 signature verification. - Sr25519Verify, - /// Blake2-256 hashing algorithm. - Blake2256, - /// Copying data in RAM. - MemCopy, - /// Disk sequential write. - DiskSeqWrite, - /// Disk random write. - DiskRndWrite, -} - -impl Metric { - /// The category of the metric. - pub fn category(&self) -> &'static str { - match self { - Self::Sr25519Verify | Self::Blake2256 => "CPU", - Self::MemCopy => "Memory", - Self::DiskSeqWrite | Self::DiskRndWrite => "Disk", - } - } - - /// The name of the metric. It is always prefixed by the [`self::category()`]. - pub fn name(&self) -> &'static str { - match self { - Self::Sr25519Verify => "SR25519-Verify", - Self::Blake2256 => "BLAKE2-256", - Self::MemCopy => "Copy", - Self::DiskSeqWrite => "Seq Write", - Self::DiskRndWrite => "Rnd Write", - } - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/utils/frame/benchmarking-cli/src/machine/mod.rs b/utils/frame/benchmarking-cli/src/machine/mod.rs index 82b4e5be7358e..bcffef255e5b5 100644 --- a/utils/frame/benchmarking-cli/src/machine/mod.rs +++ b/utils/frame/benchmarking-cli/src/machine/mod.rs @@ -30,11 +30,12 @@ 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, Throughput, + benchmark_memory, benchmark_sr25519_verify, ExecutionLimit, Metric, Requirement, Requirements, + Throughput, }; use crate::shared::check_build_profile; -pub use hardware::{Metric, Requirement, Requirements, SUBSTRATE_REFERENCE_HARDWARE}; +pub use hardware::SUBSTRATE_REFERENCE_HARDWARE; /// Command to benchmark the hardware. ///