From cdf9fb109fbb2dbb55e3dcdf80c43157ba3cc563 Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Tue, 11 Jun 2024 18:52:25 +0300 Subject: [PATCH 1/2] Extract benchmarking options into structs --- .../bin/subspace-farmer/commands/benchmark.rs | 125 ++++++++---------- 1 file changed, 58 insertions(+), 67 deletions(-) diff --git a/crates/subspace-farmer/src/bin/subspace-farmer/commands/benchmark.rs b/crates/subspace-farmer/src/bin/subspace-farmer/commands/benchmark.rs index d3348e3b4d..365d3ef470 100644 --- a/crates/subspace-farmer/src/bin/subspace-farmer/commands/benchmark.rs +++ b/crates/subspace-farmer/src/bin/subspace-farmer/commands/benchmark.rs @@ -1,6 +1,6 @@ use crate::PosTable; use anyhow::anyhow; -use clap::Subcommand; +use clap::{Parser, Subcommand}; use criterion::{black_box, BatchSize, Criterion, Throughput}; use parking_lot::Mutex; use std::collections::HashSet; @@ -19,77 +19,67 @@ use subspace_farmer_components::sector::sector_size; use subspace_proof_of_space::Table; use subspace_rpc_primitives::SlotInfo; +#[derive(Debug, Parser)] +pub(crate) struct AuditOptions { + /// Number of samples to collect for benchmarking purposes + #[arg(long, default_value_t = 10)] + sample_size: usize, + /// Also run `single` benchmark (only useful for developers, not used by default) + #[arg(long)] + with_single: bool, + /// Disk farm to audit + /// + /// Example: + /// /path/to/directory + disk_farm: PathBuf, + /// Optional filter for benchmarks, must correspond to a part of benchmark name in order for benchmark to run + filter: Option, +} + +#[derive(Debug, Parser)] +pub(crate) struct ProveOptions { + /// Number of samples to collect for benchmarking purposes + #[arg(long, default_value_t = 10)] + sample_size: usize, + /// Also run `single` benchmark (only useful for developers, not used by default) + #[arg(long)] + with_single: bool, + /// Disk farm to prove + /// + /// Example: + /// /path/to/directory + disk_farm: PathBuf, + /// Optional filter for benchmarks, must correspond to a part of benchmark name in order for benchmark to run + filter: Option, + /// Limit number of sectors audited to specified number, this limits amount of memory used by benchmark (normal + /// farming process doesn't use this much RAM) + #[arg(long)] + limit_sector_count: Option, +} + /// Arguments for benchmark #[derive(Debug, Subcommand)] pub(crate) enum BenchmarkArgs { /// Auditing benchmark - Audit { - /// Number of samples to collect for benchmarking purposes - #[arg(long, default_value_t = 10)] - sample_size: usize, - /// Also run `single` benchmark (only useful for developers, not used by default) - #[arg(long)] - with_single: bool, - /// Disk farm to audit - /// - /// Example: - /// /path/to/directory - disk_farm: PathBuf, - /// Optional filter for benchmarks, must correspond to a part of benchmark name in order for benchmark to run - filter: Option, - }, + Audit(AuditOptions), /// Proving benchmark - Prove { - /// Number of samples to collect for benchmarking purposes - #[arg(long, default_value_t = 10)] - sample_size: usize, - /// Also run `single` benchmark (only useful for developers, not used by default) - #[arg(long)] - with_single: bool, - /// Disk farm to prove - /// - /// Example: - /// /path/to/directory - disk_farm: PathBuf, - /// Optional filter for benchmarks, must correspond to a part of benchmark name in order for benchmark to run - filter: Option, - /// Limit number of sectors audited to specified number, this limits amount of memory used by benchmark (normal - /// farming process doesn't use this much RAM) - #[arg(long)] - limit_sector_count: Option, - }, + Prove(ProveOptions), } pub(crate) fn benchmark(benchmark_args: BenchmarkArgs) -> anyhow::Result<()> { match benchmark_args { - BenchmarkArgs::Audit { - sample_size, - with_single, - disk_farm, - filter, - } => audit(sample_size, with_single, disk_farm, filter), - BenchmarkArgs::Prove { - sample_size, - with_single, - disk_farm, - filter, - limit_sector_count, - } => prove( - sample_size, - with_single, - disk_farm, - filter, - limit_sector_count, - ), + BenchmarkArgs::Audit(audit_options) => audit(audit_options), + BenchmarkArgs::Prove(prove_options) => prove(prove_options), } } -fn audit( - sample_size: usize, - with_single: bool, - disk_farm: PathBuf, - filter: Option, -) -> anyhow::Result<()> { +fn audit(audit_options: AuditOptions) -> anyhow::Result<()> { + let AuditOptions { + sample_size, + with_single, + disk_farm, + filter, + } = audit_options; let (single_disk_farm_info, disk_farm) = match SingleDiskFarm::collect_summary(disk_farm) { SingleDiskFarmSummary::Found { info, directory } => (info, directory), SingleDiskFarmSummary::NotFound { directory } => { @@ -246,13 +236,14 @@ fn audit( Ok(()) } -fn prove( - sample_size: usize, - with_single: bool, - disk_farm: PathBuf, - filter: Option, - limit_sector_count: Option, -) -> anyhow::Result<()> { +fn prove(prove_options: ProveOptions) -> anyhow::Result<()> { + let ProveOptions { + sample_size, + with_single, + disk_farm, + filter, + limit_sector_count, + } = prove_options; let (single_disk_farm_info, disk_farm) = match SingleDiskFarm::collect_summary(disk_farm) { SingleDiskFarmSummary::Found { info, directory } => (info, directory), SingleDiskFarmSummary::NotFound { directory } => { From c975dfd54c1f673b870851486b437bd7a0c92abe Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Tue, 11 Jun 2024 18:59:27 +0300 Subject: [PATCH 2/2] More accurate farming benchmarks by using the same thread pool size --- .../bin/subspace-farmer/commands/benchmark.rs | 41 ++++++++++++++++++- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/crates/subspace-farmer/src/bin/subspace-farmer/commands/benchmark.rs b/crates/subspace-farmer/src/bin/subspace-farmer/commands/benchmark.rs index 365d3ef470..c781e356e5 100644 --- a/crates/subspace-farmer/src/bin/subspace-farmer/commands/benchmark.rs +++ b/crates/subspace-farmer/src/bin/subspace-farmer/commands/benchmark.rs @@ -3,6 +3,7 @@ use anyhow::anyhow; use clap::{Parser, Subcommand}; use criterion::{black_box, BatchSize, Criterion, Throughput}; use parking_lot::Mutex; +use rayon::{ThreadPool, ThreadPoolBuildError, ThreadPoolBuilder}; use std::collections::HashSet; use std::fs::OpenOptions; use std::num::NonZeroUsize; @@ -14,6 +15,7 @@ use subspace_farmer::single_disk_farm::farming::rayon_files::RayonFiles; use subspace_farmer::single_disk_farm::farming::{PlotAudit, PlotAuditOptions}; use subspace_farmer::single_disk_farm::unbuffered_io_file_windows::UnbufferedIoFileWindows; use subspace_farmer::single_disk_farm::{SingleDiskFarm, SingleDiskFarmSummary}; +use subspace_farmer::utils::{recommended_number_of_farming_threads, tokio_rayon_spawn_handler}; use subspace_farmer_components::reading::ReadSectorRecordChunksMode; use subspace_farmer_components::sector::sector_size; use subspace_proof_of_space::Table; @@ -27,6 +29,12 @@ pub(crate) struct AuditOptions { /// Also run `single` benchmark (only useful for developers, not used by default) #[arg(long)] with_single: bool, + /// Size of PER FARM thread pool used for farming (mostly for blocking I/O, but also for some + /// compute-intensive operations during proving), defaults to number of logical CPUs + /// available on UMA system and number of logical CPUs in first NUMA node on NUMA system, but + /// not more than 32 threads + #[arg(long)] + farming_thread_pool_size: Option, /// Disk farm to audit /// /// Example: @@ -44,6 +52,12 @@ pub(crate) struct ProveOptions { /// Also run `single` benchmark (only useful for developers, not used by default) #[arg(long)] with_single: bool, + /// Size of PER FARM thread pool used for farming (mostly for blocking I/O, but also for some + /// compute-intensive operations during proving), defaults to number of logical CPUs + /// available on UMA system and number of logical CPUs in first NUMA node on NUMA system, but + /// not more than 32 threads + #[arg(long)] + farming_thread_pool_size: Option, /// Disk farm to prove /// /// Example: @@ -66,10 +80,30 @@ pub(crate) enum BenchmarkArgs { Prove(ProveOptions), } +fn create_thread_pool( + farming_thread_pool_size: Option, +) -> Result { + let farming_thread_pool_size = farming_thread_pool_size + .map(|farming_thread_pool_size| farming_thread_pool_size.get()) + .unwrap_or_else(recommended_number_of_farming_threads); + + ThreadPoolBuilder::new() + .thread_name(move |thread_index| format!("benchmark.{thread_index}")) + .num_threads(farming_thread_pool_size) + .spawn_handler(tokio_rayon_spawn_handler()) + .build() +} + pub(crate) fn benchmark(benchmark_args: BenchmarkArgs) -> anyhow::Result<()> { match benchmark_args { - BenchmarkArgs::Audit(audit_options) => audit(audit_options), - BenchmarkArgs::Prove(prove_options) => prove(prove_options), + BenchmarkArgs::Audit(audit_options) => { + let thread_pool = create_thread_pool(audit_options.farming_thread_pool_size)?; + thread_pool.install(|| audit(audit_options)) + } + BenchmarkArgs::Prove(prove_options) => { + let thread_pool = create_thread_pool(prove_options.farming_thread_pool_size)?; + thread_pool.install(|| prove(prove_options)) + } } } @@ -77,6 +111,7 @@ fn audit(audit_options: AuditOptions) -> anyhow::Result<()> { let AuditOptions { sample_size, with_single, + farming_thread_pool_size: _, disk_farm, filter, } = audit_options; @@ -240,10 +275,12 @@ fn prove(prove_options: ProveOptions) -> anyhow::Result<()> { let ProveOptions { sample_size, with_single, + farming_thread_pool_size: _, disk_farm, filter, limit_sector_count, } = prove_options; + let (single_disk_farm_info, disk_farm) = match SingleDiskFarm::collect_summary(disk_farm) { SingleDiskFarmSummary::Found { info, directory } => (info, directory), SingleDiskFarmSummary::NotFound { directory } => {