Skip to content

Commit d9e20e0

Browse files
committed
calibration
1 parent ba2e59d commit d9e20e0

File tree

2 files changed

+79
-14
lines changed

2 files changed

+79
-14
lines changed

bin/cli/src/main.rs

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,28 @@ use spn_node::{Node, NodeContext, SerialBidder, SerialContext, SerialMonitor, Se
2323
#[command(author, version, about, long_about = None)]
2424
enum Args {
2525
/// Calibrate the prover.
26-
Calibrate,
26+
Calibrate(CalibrateArgs),
2727
/// Run the prover with previously benchmarked parameters.
2828
Prove(ProveArgs),
2929
}
3030

31+
/// The arguments for the `calibrate` command.
32+
#[derive(Debug, Clone, Parser)]
33+
struct CalibrateArgs {
34+
/// The cost per hour of the prover in USD.
35+
#[arg(long, help = "Cost per hour in USD, e.g. 0.80")]
36+
usd_cost_per_hour: f64,
37+
/// The expected utilization rate of the prover.
38+
#[arg(long, help = "Expected utilization rate, e.g. 0.5")]
39+
utilization_rate: f64,
40+
/// The target profit margin of the prover.
41+
#[arg(long, help = "Target profit margin, e.g. 0.1")]
42+
profit_margin: f64,
43+
/// The price of $PROVE in USD.
44+
#[arg(long, help = "Price of $PROVE in USD, e.g. 1.00")]
45+
prove_price: f64,
46+
}
47+
3148
/// The arguments for the `prove` command.
3249
#[derive(Debug, Clone, Parser)]
3350
struct ProveArgs {
@@ -64,7 +81,7 @@ async fn main() -> Result<()> {
6481

6582
// Run the command.
6683
match cli {
67-
Args::Calibrate => {
84+
Args::Calibrate(args) => {
6885
// Create the ELF.
6986
const SPN_FIBONACCI_ELF: &[u8] = include_elf!("spn-fibonacci-program");
7087

@@ -74,7 +91,8 @@ async fn main() -> Result<()> {
7491
stdin.write(&n);
7592

7693
// Run the calibrator to get the metrics.
77-
let calibrator = SinglePassCalibrator::new(SPN_FIBONACCI_ELF.to_vec(), stdin);
94+
println!("Starting calibration...");
95+
let calibrator = SinglePassCalibrator::new(SPN_FIBONACCI_ELF.to_vec(), stdin, args.usd_cost_per_hour, args.utilization_rate, args.profit_margin);
7896
let metrics =
7997
calibrator.calibrate().map_err(|e| anyhow!("failed to calibrate: {}", e))?;
8098

@@ -91,12 +109,28 @@ async fn main() -> Result<()> {
91109
// Create table data.
92110
let data = vec![
93111
CalibrationMetricsTable {
94-
name: "Prover Throughput".to_string(),
95-
value: format!("{} gas/second", metrics.throughput),
112+
name: "Cost Per Hour".to_string(),
113+
value: format!("${}", args.usd_cost_per_hour),
114+
},
115+
CalibrationMetricsTable {
116+
name: "Utilization Rate".to_string(),
117+
value: format!("{}%", args.utilization_rate * 100.0),
118+
},
119+
CalibrationMetricsTable {
120+
name: "Profit Margin".to_string(),
121+
value: format!("{}%", args.profit_margin * 100.0),
122+
},
123+
CalibrationMetricsTable {
124+
name: "Price of $PROVE".to_string(),
125+
value: format!("${}", args.prove_price),
126+
},
127+
CalibrationMetricsTable {
128+
name: "Estimated Throughput".to_string(),
129+
value: format!("{} pgus/second", metrics.pgus_per_second),
96130
},
97131
CalibrationMetricsTable {
98-
name: "Recommended Bid".to_string(),
99-
value: format!("{} gas per USDC", metrics.bid_amount),
132+
name: "Estimated Bid Price".to_string(),
133+
value: format!("{} $PROVE per 1B PGUs", metrics.pgu_price * args.prove_price),
100134
},
101135
];
102136

crates/calibrator/src/lib.rs

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ pub trait Calibrator {
1919
#[derive(Debug, Clone, Copy, Default)]
2020
pub struct CalibratorMetrics {
2121
/// The prover gas per second that the prover can process.
22-
pub throughput: f64,
22+
pub pgus_per_second: f64,
2323
/// The recommended bid amount for the prover.
24-
pub bid_amount: u64,
24+
pub pgu_price: f64,
2525
}
2626

2727
/// The default implementation of a calibrator.
@@ -31,13 +31,25 @@ pub struct SinglePassCalibrator {
3131
pub elf: Vec<u8>,
3232
/// The input stream to use for the calibration.
3333
pub stdin: SP1Stdin,
34+
/// The cost per hour of the instance (USD).
35+
pub cost_per_hour: f64,
36+
/// The expected average utilization rate of the instance.
37+
pub utilization_rate: f64,
38+
/// The target profit margin for the prover.
39+
pub profit_margin: f64,
3440
}
3541

3642
impl SinglePassCalibrator {
3743
/// Create a new [`SinglePassCalibrator`].
3844
#[must_use]
39-
pub fn new(elf: Vec<u8>, stdin: SP1Stdin) -> Self {
40-
Self { elf, stdin }
45+
pub fn new(
46+
elf: Vec<u8>,
47+
stdin: SP1Stdin,
48+
cost_per_hour: f64,
49+
utilization_rate: f64,
50+
profit_margin: f64,
51+
) -> Self {
52+
Self { elf, stdin, cost_per_hour, utilization_rate, profit_margin }
4153
}
4254
}
4355

@@ -67,10 +79,26 @@ impl Calibrator for SinglePassCalibrator {
6779

6880
// Calculate duration and throughput.
6981
let duration = start.elapsed();
70-
let throughput = prover_gas as f64 / duration.as_secs_f64();
82+
let pgus_per_second = prover_gas as f64 / duration.as_secs_f64();
83+
84+
// Calculate the price per pgu using a simple economic model..
85+
//
86+
// The economic model is based on the following assumptions:
87+
// - The prover has a consistent cost per hour.
88+
// - The prover has a consistent utilization rate.
89+
// - The prover wants to maximize its profit.
90+
//
91+
// The model is based on the following formula:
92+
//
93+
// bidPricePerPGU = (costPerHour / averageUtilizationRate) * (1 + profitMargin) / maxThroughputPerHour
94+
//
95+
let pgus_per_hour = pgus_per_second * 3600.0;
96+
let utilized_pgus_per_hour = pgus_per_hour * self.utilization_rate;
97+
let optimal_pgu_price = self.cost_per_hour / utilized_pgus_per_hour;
98+
let pgu_price = optimal_pgu_price * (1.0 + self.profit_margin);
7199

72100
// Return the metrics.
73-
Ok(CalibratorMetrics { throughput, bid_amount: 1 })
101+
Ok(CalibratorMetrics { pgus_per_second, pgu_price })
74102
}
75103
}
76104

@@ -92,7 +120,10 @@ mod tests {
92120
stdin.write(&n);
93121

94122
// Create the calibrator.
95-
let calibrator = SinglePassCalibrator::new(elf, stdin);
123+
let cost_per_hour = 0.1;
124+
let utilization_rate = 0.5;
125+
let profit_margin = 0.1;
126+
let calibrator = SinglePassCalibrator::new(elf, stdin, cost_per_hour, utilization_rate, profit_margin);
96127

97128
// Calibrate the prover.
98129
let metrics = calibrator.calibrate().unwrap();

0 commit comments

Comments
 (0)