add builtin cost model

Co-authored-by: rvcas <x@rvcas.dev>
This commit is contained in:
Kasey White 2022-07-07 23:16:51 -04:00 committed by Kasey White
parent e8aa013aa4
commit 045dc8fed8
3 changed files with 239 additions and 81 deletions

View File

@ -3,7 +3,10 @@ use std::fmt::Display;
use crate::{ use crate::{
builtins::DefaultFunction, builtins::DefaultFunction,
debruijn::{self, Converter}, debruijn::{self, Converter},
machine::{Costs, ExBudget, Machine}, machine::{
cost_model::{ExBudget, MachineCosts},
Machine,
},
}; };
/// This represents a program in Untyped Plutus Core. /// This represents a program in Untyped Plutus Core.
@ -404,7 +407,7 @@ impl From<Term<FakeNamedDeBruijn>> for Term<NamedDeBruijn> {
impl Program<NamedDeBruijn> { impl Program<NamedDeBruijn> {
pub fn eval(&self) -> Result<Term<NamedDeBruijn>, crate::machine::Error> { pub fn eval(&self) -> Result<Term<NamedDeBruijn>, crate::machine::Error> {
let mut machine = Machine::new(Costs::default(), ExBudget::default(), 200); let mut machine = Machine::new(MachineCosts::default(), ExBudget::default(), 200);
let (term, _, _) = machine.run(&self.term)?; let (term, _, _) = machine.run(&self.term)?;

View File

@ -3,12 +3,14 @@ use crate::{
builtins::DefaultFunction, builtins::DefaultFunction,
}; };
pub mod cost_model;
mod error; mod error;
use cost_model::{ExBudget, MachineCosts, StepKind};
pub use error::Error; pub use error::Error;
pub struct Machine { pub struct Machine {
costs: Costs, costs: MachineCosts,
ex_budget: ExBudget, ex_budget: ExBudget,
frames: Vec<Context>, frames: Vec<Context>,
slippage: u32, slippage: u32,
@ -17,7 +19,7 @@ pub struct Machine {
} }
impl Machine { impl Machine {
pub fn new(costs: Costs, initial_budget: ExBudget, slippage: u32) -> Machine { pub fn new(costs: MachineCosts, initial_budget: ExBudget, slippage: u32) -> Machine {
Machine { Machine {
costs, costs,
ex_budget: initial_budget, ex_budget: initial_budget,
@ -86,7 +88,10 @@ impl Machine {
self.compute(body) self.compute(body)
} }
Term::Error => Err(Error::EvaluationFailure), Term::Error => Err(Error::EvaluationFailure),
Term::Builtin(_) => todo!(), Term::Builtin(_bn) => {
self.step_and_maybe_spend(StepKind::Builtin)?;
todo!()
}
} }
} }
@ -241,6 +246,8 @@ impl Machine {
self.unbudgeted_steps[i] = 0; self.unbudgeted_steps[i] = 0;
} }
self.unbudgeted_steps[7] = 0;
Ok(()) Ok(())
} }
@ -279,78 +286,3 @@ pub enum Value {
// BuiltinRuntime (CekValue uni fun) // BuiltinRuntime (CekValue uni fun)
), ),
} }
/// Can be negative
#[derive(Debug, Clone, PartialEq, Copy, Default)]
pub struct ExBudget {
mem: i32,
cpu: i32,
}
impl ExBudget {
pub fn occurences(&mut self, n: i32) {
self.mem *= n;
self.cpu *= n;
}
}
#[repr(u8)]
pub enum StepKind {
Constant = 0,
Var = 1,
Lambda = 2,
Apply = 3,
Delay = 4,
Force = 5,
Builtin = 6,
// DO NOT USE THIS IN `step_and_maybe_spend`
StartUp = 7,
}
impl TryFrom<u8> for StepKind {
type Error = error::Error;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(StepKind::Constant),
1 => Ok(StepKind::Var),
2 => Ok(StepKind::Lambda),
3 => Ok(StepKind::Apply),
4 => Ok(StepKind::Delay),
5 => Ok(StepKind::Force),
6 => Ok(StepKind::Builtin),
v => Err(error::Error::InvalidStepKind(v)),
}
}
}
/// There's no entry for Error since we'll be exiting anyway; also, what would
/// happen if calling 'Error' caused the budget to be exceeded?
#[derive(Default)]
pub struct Costs {
startup: ExBudget,
var: ExBudget,
constant: ExBudget,
lambda: ExBudget,
delay: ExBudget,
force: ExBudget,
apply: ExBudget,
/// Just the cost of evaluating a Builtin node, not the builtin itself.
builtin: ExBudget,
}
impl Costs {
/// Get the cost of a step
pub fn get(&self, step: StepKind) -> ExBudget {
match step {
StepKind::Constant => self.constant,
StepKind::Var => self.var,
StepKind::Lambda => self.lambda,
StepKind::Apply => self.apply,
StepKind::Delay => self.delay,
StepKind::Force => self.force,
StepKind::Builtin => self.builtin,
StepKind::StartUp => self.startup,
}
}
}

View File

@ -0,0 +1,223 @@
/// Can be negative
#[derive(Debug, Clone, PartialEq, Copy, Default)]
pub struct ExBudget {
pub mem: i32,
pub cpu: i32,
}
impl ExBudget {
pub fn occurences(&mut self, n: i32) {
self.mem *= n;
self.cpu *= n;
}
}
pub struct CostModel {
pub machine_costs: MachineCosts,
}
/// There's no entry for Error since we'll be exiting anyway; also, what would
/// happen if calling 'Error' caused the budget to be exceeded?
#[derive(Default)]
pub struct MachineCosts {
startup: ExBudget,
var: ExBudget,
constant: ExBudget,
lambda: ExBudget,
delay: ExBudget,
force: ExBudget,
apply: ExBudget,
/// Just the cost of evaluating a Builtin node, not the builtin itself.
builtin: ExBudget,
}
impl MachineCosts {
/// Get the cost of a step
pub fn get(&self, step: StepKind) -> ExBudget {
match step {
StepKind::Constant => self.constant,
StepKind::Var => self.var,
StepKind::Lambda => self.lambda,
StepKind::Apply => self.apply,
StepKind::Delay => self.delay,
StepKind::Force => self.force,
StepKind::Builtin => self.builtin,
StepKind::StartUp => self.startup,
}
}
}
pub struct BuiltinCosts {
pub add_integer: CostingFun<TwoArguments>,
pub subtract_integer: CostingFun<TwoArguments>,
pub multiply_integer: CostingFun<TwoArguments>,
pub divide_integer: CostingFun<TwoArguments>,
pub quotient_integer: CostingFun<TwoArguments>,
pub remainder_integer: CostingFun<TwoArguments>,
pub mod_integer: CostingFun<TwoArguments>,
pub equals_integer: CostingFun<TwoArguments>,
pub less_than_integer: CostingFun<TwoArguments>,
pub less_than_equals_integer: CostingFun<TwoArguments>,
// Bytestrings
pub append_byte_string: CostingFun<TwoArguments>,
pub cons_byte_string: CostingFun<TwoArguments>,
pub slice_byte_string: CostingFun<ThreeArguments>,
pub length_of_byte_string: CostingFun<OneArgument>,
pub index_byte_string: CostingFun<TwoArguments>,
pub equals_byte_string: CostingFun<TwoArguments>,
pub less_than_byte_string: CostingFun<TwoArguments>,
pub less_than_equals_byte_string: CostingFun<TwoArguments>,
// Cryptography and hashes
pub sha2_256: CostingFun<OneArgument>,
pub sha3_256: CostingFun<OneArgument>,
pub blake2b_256: CostingFun<OneArgument>,
pub verify_ed25519_signature: CostingFun<ThreeArguments>,
pub verify_ecdsa_secp256k1_signature: CostingFun<ThreeArguments>,
pub verify_schnorr_secp256k1_signature: CostingFun<ThreeArguments>,
// Strings
pub append_string: CostingFun<TwoArguments>,
pub equals_string: CostingFun<TwoArguments>,
pub encode_utf8: CostingFun<OneArgument>,
pub decode_utf8: CostingFun<OneArgument>,
// Bool
pub if_then_else: CostingFun<ThreeArguments>,
// Unit
pub choose_unit: CostingFun<TwoArguments>,
// Tracing
pub trace: CostingFun<TwoArguments>,
// Pairs
pub fst_pair: CostingFun<OneArgument>,
pub snd_pair: CostingFun<OneArgument>,
// Lists
pub choose_list: CostingFun<ThreeArguments>,
pub mk_cons: CostingFun<TwoArguments>,
pub head_list: CostingFun<OneArgument>,
pub tail_list: CostingFun<OneArgument>,
pub null_list: CostingFun<OneArgument>,
// Data
pub choose_data: CostingFun<SixArguments>,
pub constr_data: CostingFun<TwoArguments>,
pub map_data: CostingFun<OneArgument>,
pub list_data: CostingFun<OneArgument>,
pub i_data: CostingFun<OneArgument>,
pub b_data: CostingFun<OneArgument>,
pub un_constr_data: CostingFun<OneArgument>,
pub un_map_data: CostingFun<OneArgument>,
pub un_list_data: CostingFun<OneArgument>,
pub un_i_data: CostingFun<OneArgument>,
pub un_b_data: CostingFun<OneArgument>,
pub equals_data: CostingFun<TwoArguments>,
// Misc constructors
pub mk_pair_data: CostingFun<TwoArguments>,
pub mk_nil_data: CostingFun<OneArgument>,
pub mk_nil_pair_data: CostingFun<OneArgument>,
pub serialise_data: CostingFun<OneArgument>,
}
pub struct CostingFun<T> {
pub memory: T,
pub cpu: T,
}
pub enum OneArgument {
ConstantCost(isize),
LinearCost(LinearSize),
}
pub enum TwoArguments {
ConstantCost(isize),
LinearInX(LinearSize),
LinearInY(LinearSize),
AddedSizes(AddedSizes),
SubtractedSizes(SubtractedSizes),
MultipliedSizes(MultipliedSizes),
MinSize(MinSize),
MaxSize(MaxSize),
LinearOnDiagonal(ConstantOrLinear),
ConstAboveDiagonal(ConstantOrTwoArguments),
ConstBelowDiagonal(ConstantOrTwoArguments),
}
pub enum ThreeArguments {
ConstantCost(isize),
AddedSizes(AddedSizes),
LinearInX(LinearSize),
LinearInY(LinearSize),
LinearInZ(LinearSize),
}
pub enum SixArguments {
ConstantCost(isize),
}
pub struct LinearSize {
pub intercept: isize,
pub slope: isize,
}
pub struct AddedSizes {
pub intercept: isize,
pub slope: isize,
}
pub struct SubtractedSizes {
pub intercept: isize,
pub slope: isize,
pub minimum: isize,
}
pub struct MultipliedSizes {
pub intercept: isize,
pub slope: isize,
}
pub struct MinSize {
pub intercept: isize,
pub slope: isize,
}
pub struct MaxSize {
pub intercept: isize,
pub slope: isize,
}
pub struct ConstantOrLinear {
pub constant: isize,
pub intercept: isize,
pub slope: isize,
}
pub struct ConstantOrTwoArguments {
pub constant: isize,
pub model: Box<TwoArguments>,
}
#[repr(u8)]
pub enum StepKind {
Constant = 0,
Var = 1,
Lambda = 2,
Apply = 3,
Delay = 4,
Force = 5,
Builtin = 6,
// DO NOT USE THIS IN `step_and_maybe_spend`
StartUp = 7,
}
impl TryFrom<u8> for StepKind {
type Error = super::error::Error;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(StepKind::Constant),
1 => Ok(StepKind::Var),
2 => Ok(StepKind::Lambda),
3 => Ok(StepKind::Apply),
4 => Ok(StepKind::Delay),
5 => Ok(StepKind::Force),
6 => Ok(StepKind::Builtin),
v => Err(super::error::Error::InvalidStepKind(v)),
}
}
}