Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Farmer audit benchmark #2124

Merged
merged 6 commits into from
Oct 16, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/subspace-farmer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ blake2 = "0.10.6"
blake3 = { version = "1.4.1", default-features = false }
bytesize = "1.3.0"
clap = { version = "4.4.3", features = ["color", "derive"] }
criterion = { version = "0.5.1", default-features = false, features = ["rayon", "async"] }
derive_more = "0.99.17"
event-listener-primitives = "2.0.1"
fdlimit = "0.2"
Expand Down
5 changes: 5 additions & 0 deletions crates/subspace-farmer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ This will connect to local node and will try to solve on every slot notification

*NOTE: You need to have a `subspace-node` running before starting farmer, otherwise it will not be able to start*

### Benchmark auditing
```
target/production/subspace-farmer benchmark audit /path/to/farm
```

### Show information about the farm
```
target/production/subspace-farmer info /path/to/farm
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub(crate) mod benchmark;
pub(crate) mod farm;
mod info;
mod scrub;
Expand Down
140 changes: 140 additions & 0 deletions crates/subspace-farmer/src/bin/subspace-farmer/commands/benchmark.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
use crate::PosTable;
use anyhow::anyhow;
use clap::Subcommand;
use criterion::async_executor::AsyncExecutor;
use criterion::{black_box, BatchSize, Criterion, Throughput};
#[cfg(windows)]
use memmap2::Mmap;
use parking_lot::Mutex;
use std::fs::OpenOptions;
use std::future::Future;
use std::num::NonZeroUsize;
use std::path::PathBuf;
use subspace_core_primitives::crypto::kzg::{embedded_kzg_settings, Kzg};
use subspace_core_primitives::{Record, SolutionRange};
use subspace_erasure_coding::ErasureCoding;
use subspace_farmer::single_disk_farm::farming::{plot_audit, PlotAuditOptions};
use subspace_farmer::single_disk_farm::{SingleDiskFarm, SingleDiskFarmSummary};
use subspace_farmer_components::sector::sector_size;
use subspace_proof_of_space::Table;
use subspace_rpc_primitives::SlotInfo;
use tokio::runtime::Handle;

struct TokioAsyncExecutor(Handle);

impl AsyncExecutor for TokioAsyncExecutor {
fn block_on<T>(&self, future: impl Future<Output = T>) -> T {
tokio::task::block_in_place(|| self.0.block_on(future))
}
}

impl TokioAsyncExecutor {
fn new() -> Self {
Self(Handle::current())
}
}

/// Arguments for benchmark
#[derive(Debug, Subcommand)]
pub(crate) enum BenchmarkArgs {
/// Audit benchmark
Audit {
/// Disk farm to audit
///
/// Example:
/// /path/to/directory
disk_farm: PathBuf,
#[arg(long, default_value_t = 10)]
sample_size: usize,
},
}

pub(crate) async fn benchmark(benchmark_args: BenchmarkArgs) -> anyhow::Result<()> {
match benchmark_args {
BenchmarkArgs::Audit {
disk_farm,
sample_size,
} => audit(disk_farm, sample_size).await,
}
}

async fn audit(disk_farm: PathBuf, sample_size: 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 sector_size = sector_size(single_disk_farm_info.pieces_in_sector());
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).unwrap(),
nazar-pc marked this conversation as resolved.
Show resolved Hide resolved
)
.map_err(|error| anyhow::anyhow!(error))?;
let table_generator = Mutex::new(PosTable::generator());

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

let plot_file = OpenOptions::new()
.read(true)
.open(disk_farm.join(SingleDiskFarm::PLOT_FILE))
.map_err(|error| anyhow::anyhow!("Failed to open single disk farm: {error}"))?;
#[cfg(windows)]
let plot_mmap = unsafe { Mmap::map(&plot_file)? };

let mut criterion = Criterion::default().sample_size(sample_size);
criterion
.benchmark_group("audit")
.throughput(Throughput::Bytes(
sector_size as u64 * sectors_metadata.len() as u64,
))
.bench_function("plot", |b| {
b.to_async(TokioAsyncExecutor::new()).iter_batched(
rand::random,
|global_challenge| {
let options = PlotAuditOptions::<PosTable> {
public_key: single_disk_farm_info.public_key(),
reward_address: single_disk_farm_info.public_key(),
sector_size,
slot_info: SlotInfo {
slot_number: 0,
global_challenge,
// No solution will be found, pure audit
solution_range: SolutionRange::MIN,
// No solution will be found, pure audit
voting_solution_range: SolutionRange::MIN,
},
sectors_metadata: &sectors_metadata,
kzg: &kzg,
erasure_coding: &erasure_coding,
#[cfg(not(windows))]
plot_file: &plot_file,
#[cfg(windows)]
plot_mmap: &plot_mmap,
maybe_sector_being_modified: None,
table_generator: &table_generator,
};

black_box(plot_audit(black_box(options)))
},
BatchSize::SmallInput,
)
});

criterion.final_summary();

Ok(())
}
6 changes: 6 additions & 0 deletions crates/subspace-farmer/src/bin/subspace-farmer/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ type PosTable = ChiaTable;
enum Command {
/// Start a farmer, does plotting and farming
Farm(commands::farm::FarmingArgs),
/// Run various benchmarks
#[clap(subcommand)]
Benchmark(commands::benchmark::BenchmarkArgs),
/// Print information about farm and its content
Info {
/// One or more farm located at specified path.
Expand Down Expand Up @@ -77,6 +80,9 @@ async fn main() -> anyhow::Result<()> {
Command::Farm(farming_args) => {
commands::farm::farm::<PosTable>(farming_args).await?;
}
Command::Benchmark(benchmark_args) => {
commands::benchmark::benchmark(benchmark_args).await?;
}
Command::Info { disk_farms } => {
if disk_farms.is_empty() {
info!("No farm was specified, so there is nothing to do");
Expand Down
58 changes: 56 additions & 2 deletions crates/subspace-farmer/src/single_disk_farm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -560,8 +560,8 @@ impl Drop for SingleDiskFarm {
}

impl SingleDiskFarm {
const PLOT_FILE: &'static str = "plot.bin";
const METADATA_FILE: &'static str = "metadata.bin";
pub const PLOT_FILE: &'static str = "plot.bin";
pub const METADATA_FILE: &'static str = "metadata.bin";
const SUPPORTED_PLOT_VERSION: u8 = 0;

/// Create new single disk farm instance
Expand Down Expand Up @@ -1137,6 +1137,60 @@ impl SingleDiskFarm {
}
}

/// Read all sectors metadata
pub fn read_all_sectors_metadata(
directory: &Path,
) -> io::Result<Vec<SectorMetadataChecksummed>> {
let mut metadata_file = OpenOptions::new()
.read(true)
.open(directory.join(Self::METADATA_FILE))?;

let metadata_size = metadata_file.seek(SeekFrom::End(0))?;
let sector_metadata_size = SectorMetadataChecksummed::encoded_size();

let mut metadata_header_bytes = vec![0; PlotMetadataHeader::encoded_size()];
metadata_file.read_exact_at(&mut metadata_header_bytes, 0)?;

let metadata_header = PlotMetadataHeader::decode(&mut metadata_header_bytes.as_ref())
.map_err(|error| {
io::Error::new(
io::ErrorKind::Other,
format!("Failed to decode metadata header: {}", error),
)
})?;

if metadata_header.version != SingleDiskFarm::SUPPORTED_PLOT_VERSION {
return Err(io::Error::new(
io::ErrorKind::Other,
format!("Unsupported metadata version {}", metadata_header.version),
));
}

let mut sectors_metadata = Vec::<SectorMetadataChecksummed>::with_capacity(
((metadata_size - RESERVED_PLOT_METADATA) / sector_metadata_size as u64) as usize,
);

let mut sector_metadata_bytes = vec![0; sector_metadata_size];
for sector_index in 0..metadata_header.plotted_sector_count {
metadata_file.read_exact_at(
&mut sector_metadata_bytes,
RESERVED_PLOT_METADATA + sector_metadata_size as u64 * u64::from(sector_index),
)?;
sectors_metadata.push(
SectorMetadataChecksummed::decode(&mut sector_metadata_bytes.as_ref()).map_err(
|error| {
io::Error::new(
io::ErrorKind::Other,
format!("Failed to decode sector metadata: {}", error),
)
},
)?,
);
}

Ok(sectors_metadata)
}

/// ID of this farm
pub fn id(&self) -> &SingleDiskFarmId {
self.single_disk_farm_info.id()
Expand Down
11 changes: 6 additions & 5 deletions docs/farming.md
Original file line number Diff line number Diff line change
Expand Up @@ -319,11 +319,12 @@ There are extra commands and parameters you can use on farmer or node, use the `

Below are some helpful samples:

- `./FARMER_FILE_NAME info PATH_TO_FARM` : show information about the farm at `PATH_TO_FARM`
- `./FARMER_FILE_NAME scrub PATH_TO_FARM` : Scrub the farm to find and fix farm at `PATH_TO_FARM` corruption
- `./FARMER_FILE_NAME wipe PATH_TO_FARM` : erases everything related to farmer if data were stored in `PATH_TO_FARM`
- `./NODE_FILE_NAME --base-path NODE_DATA_PATH --chain gemini-3f ...` : start node and store data in `NODE_DATA_PATH` instead of default location
- `./NODE_FILE_NAME purge-chain --base-path NODE_DATA_PATH --chain gemini-3f` : erases data related to the node if data were stored in `NODE_DATA_PATH`
- `./FARMER_FILE_NAME benchmark audit PATH_TO_FARM`: benchmark auditing performance of the farm at `PATH_TO_FARM`
- `./FARMER_FILE_NAME info PATH_TO_FARM`: show information about the farm at `PATH_TO_FARM`
- `./FARMER_FILE_NAME scrub PATH_TO_FARM`: Scrub the farm to find and fix farm at `PATH_TO_FARM` corruption
- `./FARMER_FILE_NAME wipe PATH_TO_FARM`: erases everything related to farmer if data were stored in `PATH_TO_FARM`
- `./NODE_FILE_NAME --base-path NODE_DATA_PATH --chain gemini-3f ...`: start node and store data in `NODE_DATA_PATH` instead of default location
- `./NODE_FILE_NAME purge-chain --base-path NODE_DATA_PATH --chain gemini-3f`: erases data related to the node if data were stored in `NODE_DATA_PATH`

Examples:
```bash
Expand Down
Loading