Skip to content

Commit

Permalink
Add proving benchmark to subspace-farmer
Browse files Browse the repository at this point in the history
  • Loading branch information
nazar-pc committed Nov 6, 2023
1 parent 9170a7b commit c239f3c
Show file tree
Hide file tree
Showing 3 changed files with 180 additions and 11 deletions.
172 changes: 167 additions & 5 deletions crates/subspace-farmer/src/bin/subspace-farmer/commands/benchmark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ use subspace_rpc_primitives::SlotInfo;
/// Arguments for benchmark
#[derive(Debug, Subcommand)]
pub(crate) enum BenchmarkArgs {
/// Audit benchmark
/// Auditing benchmark
Audit {
/// Number of samples to collect for benchmarking purposes
#[arg(long, default_value_t = 10)]
sample_size: usize,
/// Disk farm to audit
Expand All @@ -31,6 +32,23 @@ pub(crate) enum BenchmarkArgs {
/// Optional filter for benchmarks, must correspond to a part of benchmark name in order for benchmark to run
filter: Option<String>,
},
/// Proving benchmark
Prove {
/// Number of samples to collect for benchmarking purposes
#[arg(long, default_value_t = 10)]
sample_size: usize,
/// 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<String>,
/// 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<usize>,
},
}

pub(crate) fn benchmark(benchmark_args: BenchmarkArgs) -> anyhow::Result<()> {
Expand All @@ -40,6 +58,12 @@ pub(crate) fn benchmark(benchmark_args: BenchmarkArgs) -> anyhow::Result<()> {
disk_farm,
filter,
} => audit(sample_size, disk_farm, filter),
BenchmarkArgs::Prove {
sample_size,
disk_farm,
filter,
limit_sector_count,
} => prove(sample_size, disk_farm, filter, limit_sector_count),
}
}

Expand Down Expand Up @@ -88,10 +112,9 @@ fn audit(sample_size: usize, disk_farm: PathBuf, filter: Option<String>) -> anyh
.read(true)
.open(disk_farm.join(SingleDiskFarm::PLOT_FILE))
.map_err(|error| anyhow::anyhow!("Failed to open plot: {error}"))?;
let plot_audit = PlotAudit::new(&plot);

group.bench_function("plot/single", |b| {
let plot_audit = PlotAudit::new(&plot);

b.iter_batched(
rand::random,
|global_challenge| {
Expand Down Expand Up @@ -122,10 +145,9 @@ fn audit(sample_size: usize, disk_farm: PathBuf, filter: Option<String>) -> anyh
{
let plot = RayonFiles::open(&disk_farm.join(SingleDiskFarm::PLOT_FILE))
.map_err(|error| anyhow::anyhow!("Failed to open plot: {error}"))?;
let plot_audit = PlotAudit::new(&plot);

group.bench_function("plot/rayon", |b| {
let plot_audit = PlotAudit::new(&plot);

b.iter_batched(
rand::random,
|global_challenge| {
Expand Down Expand Up @@ -159,3 +181,143 @@ fn audit(sample_size: usize, disk_farm: PathBuf, filter: Option<String>) -> anyh

Ok(())
}

fn prove(
sample_size: usize,
disk_farm: PathBuf,
filter: Option<String>,
limit_sector_count: Option<usize>,
) -> anyhow::Result<()> {
let (single_disk_farm_info, disk_farm) = match SingleDiskFarm::collect_summary(disk_farm) {
SingleDiskFarmSummary::Found { info, directory } => (info, directory),
SingleDiskFarmSummary::NotFound { directory } => {
return Err(anyhow!(
"No single disk farm info found, make sure {} is a valid path to the farm and \
process have permissions to access it",
directory.display()
));
}
SingleDiskFarmSummary::Error { directory, error } => {
return Err(anyhow!(
"Failed to open single disk farm info, make sure {} is a valid path to the farm \
and process have permissions to access it: {error}",
directory.display()
));
}
};

let kzg = Kzg::new(embedded_kzg_settings());
let erasure_coding = ErasureCoding::new(
NonZeroUsize::new(Record::NUM_S_BUCKETS.next_power_of_two().ilog2() as usize)
.expect("Not zero; qed"),
)
.map_err(|error| anyhow::anyhow!(error))?;
let table_generator = Mutex::new(PosTable::generator());

let mut sectors_metadata = SingleDiskFarm::read_all_sectors_metadata(&disk_farm)
.map_err(|error| anyhow::anyhow!("Failed to read sectors metadata: {error}"))?;
if let Some(limit_sector_count) = limit_sector_count {
sectors_metadata.truncate(limit_sector_count);
};

let mut criterion = Criterion::default().sample_size(sample_size);
if let Some(filter) = filter {
criterion = criterion.with_filter(filter);
}
{
let mut group = criterion.benchmark_group("prove");
{
let plot = OpenOptions::new()
.read(true)
.open(disk_farm.join(SingleDiskFarm::PLOT_FILE))
.map_err(|error| anyhow::anyhow!("Failed to open plot: {error}"))?;
let plot_audit = PlotAudit::new(&plot);
let options = PlotAuditOptions::<PosTable> {
public_key: single_disk_farm_info.public_key(),
reward_address: single_disk_farm_info.public_key(),
slot_info: SlotInfo {
slot_number: 0,
global_challenge: rand::random(),
// Solution is guaranteed to be found
solution_range: SolutionRange::MAX,
// Solution is guaranteed to be found
voting_solution_range: SolutionRange::MAX,
},
sectors_metadata: &sectors_metadata,
kzg: &kzg,
erasure_coding: &erasure_coding,
maybe_sector_being_modified: None,
table_generator: &table_generator,
};

let mut audit_results = plot_audit.audit(options);

group.bench_function("plot/single", |b| {
b.iter_batched(
|| {
if let Some(result) = audit_results.pop() {
return result;
}

audit_results = plot_audit.audit(options);

audit_results.pop().unwrap()
},
|(_sector_index, mut provable_solutions)| {
while (provable_solutions.next()).is_none() {
// Try to create one solution and exit
}
},
BatchSize::SmallInput,
)
});
}
{
let plot = RayonFiles::open(&disk_farm.join(SingleDiskFarm::PLOT_FILE))
.map_err(|error| anyhow::anyhow!("Failed to open plot: {error}"))?;
let plot_audit = PlotAudit::new(&plot);
let options = PlotAuditOptions::<PosTable> {
public_key: single_disk_farm_info.public_key(),
reward_address: single_disk_farm_info.public_key(),
slot_info: SlotInfo {
slot_number: 0,
global_challenge: rand::random(),
// Solution is guaranteed to be found
solution_range: SolutionRange::MAX,
// Solution is guaranteed to be found
voting_solution_range: SolutionRange::MAX,
},
sectors_metadata: &sectors_metadata,
kzg: &kzg,
erasure_coding: &erasure_coding,
maybe_sector_being_modified: None,
table_generator: &table_generator,
};
let mut audit_results = plot_audit.audit(options);

group.bench_function("plot/rayon", |b| {
b.iter_batched(
|| {
if let Some(result) = audit_results.pop() {
return result;
}

audit_results = plot_audit.audit(options);

audit_results.pop().unwrap()
},
|(_sector_index, mut provable_solutions)| {
while (provable_solutions.next()).is_none() {
// Try to create one solution and exit
}
},
BatchSize::SmallInput,
)
});
}
}

criterion.final_summary();

Ok(())
}
12 changes: 12 additions & 0 deletions crates/subspace-farmer/src/single_disk_farm/farming.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ where
}

/// Plot audit options
#[derive(Debug)]
pub struct PlotAuditOptions<'a, PosTable>
where
PosTable: Table,
Expand All @@ -102,6 +103,17 @@ where
pub table_generator: &'a Mutex<PosTable::Generator>,
}

impl<'a, PosTable> Clone for PlotAuditOptions<'a, PosTable>
where
PosTable: Table,
{
fn clone(&self) -> Self {
*self
}
}

impl<'a, PosTable> Copy for PlotAuditOptions<'a, PosTable> where PosTable: Table {}

/// Plot auditing implementation
pub struct PlotAudit<Plot>(Plot)
where
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,7 @@ pub struct RayonFiles {

impl ReadAtSync for RayonFiles {
fn read_at(&self, buf: &mut [u8], offset: usize) -> io::Result<()> {
let thread_index = rayon::current_thread_index().ok_or_else(|| {
io::Error::new(
io::ErrorKind::Other,
"Reads must be called from rayon worker thread",
)
})?;
let thread_index = rayon::current_thread_index().unwrap_or_default();
let file = self.files.get(thread_index).ok_or_else(|| {
io::Error::new(io::ErrorKind::Other, "No files entry for this rayon thread")
})?;
Expand Down

0 comments on commit c239f3c

Please sign in to comment.