Skip to content

Commit

Permalink
Merge pull request #2379 from AleoHQ/test/execution-cost
Browse files Browse the repository at this point in the history
[Test] Execution cost.
  • Loading branch information
howardwu committed Mar 20, 2024
2 parents e9d5f8f + f066df8 commit dd01117
Show file tree
Hide file tree
Showing 2 changed files with 384 additions and 87 deletions.
178 changes: 91 additions & 87 deletions synthesizer/process/src/cost.rs
Expand Up @@ -74,97 +74,90 @@ pub fn execution_cost<N: Network>(process: &Process<N>, execution: &Execution<N>
Ok((total_cost, (storage_cost, finalize_cost)))
}

/// Returns the minimum number of microcredits required to run the finalize.
pub fn cost_in_microcredits<N: Network>(stack: &Stack<N>, function_name: &Identifier<N>) -> Result<u64> {
/// A helper function to determine the plaintext type in bytes.
fn plaintext_size_in_bytes<N: Network>(stack: &Stack<N>, plaintext_type: &PlaintextType<N>) -> Result<u64> {
match plaintext_type {
PlaintextType::Literal(literal_type) => Ok(literal_type.size_in_bytes::<N>() as u64),
PlaintextType::Struct(struct_name) => {
// Retrieve the struct from the stack.
let struct_ = stack.program().get_struct(struct_name)?;
// Retrieve the size of the struct name.
let size_of_name = struct_.name().to_bytes_le()?.len() as u64;
// Retrieve the size of all the members of the struct.
let size_of_members = struct_.members().iter().try_fold(0u64, |acc, (_, member_type)| {
acc.checked_add(plaintext_size_in_bytes(stack, member_type)?).ok_or(anyhow!(
"Overflowed while computing the size of the struct '{}/{struct_name}' - {member_type}",
stack.program_id()
))
})?;
// Return the size of the struct.
Ok(size_of_name.saturating_add(size_of_members))
}
PlaintextType::Array(array_type) => {
// Retrieve the number of elements in the array.
let num_elements = **array_type.length() as u64;
// Compute the size of an array element.
let size_of_element = plaintext_size_in_bytes(stack, array_type.next_element_type())?;
// Return the size of the array.
Ok(num_elements.saturating_mul(size_of_element))
}
}
}

/// A helper function to compute the following: base_cost + (byte_multiplier * size_of_operands).
fn cost_in_size<'a, N: Network>(
stack: &Stack<N>,
finalize: &Finalize<N>,
operands: impl IntoIterator<Item = &'a Operand<N>>,
byte_multiplier: u64,
base_cost: u64,
) -> Result<u64> {
// Retrieve the finalize types.
let finalize_types = stack.get_finalize_types(finalize.name())?;
// Compute the size of the operands.
let size_of_operands = operands.into_iter().try_fold(0u64, |acc, operand| {
// Determine the size of the operand.
let operand_size = match finalize_types.get_type_from_operand(stack, operand)? {
FinalizeType::Plaintext(plaintext_type) => plaintext_size_in_bytes(stack, &plaintext_type)?,
FinalizeType::Future(future) => {
bail!("Future '{future}' is not a valid operand in the finalize scope");
}
};
// Safely add the size to the accumulator.
acc.checked_add(operand_size).ok_or(anyhow!(
"Overflowed while computing the size of the operand '{operand}' in '{}/{}' (finalize)",
stack.program_id(),
finalize.name()
))
})?;
// Return the cost.
Ok(base_cost.saturating_add(byte_multiplier.saturating_mul(size_of_operands)))
}
/// Finalize costs for compute heavy operations, derived as:
/// `BASE_COST + (PER_BYTE_COST * SIZE_IN_BYTES)`.

// Finalize costs for compute heavy operations, derived as:
// `BASE_COST + (PER_BYTE_COST * SIZE_IN_BYTES)`.
const CAST_BASE_COST: u64 = 500;
const CAST_PER_BYTE_COST: u64 = 30;

const CAST_BASE_COST: u64 = 500;
const CAST_PER_BYTE_COST: u64 = 30;
const HASH_BASE_COST: u64 = 10_000;
const HASH_PER_BYTE_COST: u64 = 30;

const HASH_BASE_COST: u64 = 10_000;
const HASH_PER_BYTE_COST: u64 = 30;
const HASH_BHP_BASE_COST: u64 = 50_000;
const HASH_BHP_PER_BYTE_COST: u64 = 300;

const HASH_BHP_BASE_COST: u64 = 50_000;
const HASH_BHP_PER_BYTE_COST: u64 = 300;
const HASH_PSD_BASE_COST: u64 = 40_000;
const HASH_PSD_PER_BYTE_COST: u64 = 75;

const HASH_PSD_BASE_COST: u64 = 40_000;
const HASH_PSD_PER_BYTE_COST: u64 = 75;
const MAPPING_BASE_COST: u64 = 10_000;
const MAPPING_PER_BYTE_COST: u64 = 10;

const MAPPING_BASE_COST: u64 = 10_000;
const MAPPING_PER_BYTE_COST: u64 = 10;
const SET_BASE_COST: u64 = 10_000;
const SET_PER_BYTE_COST: u64 = 100;

const SET_BASE_COST: u64 = 10_000;
const SET_PER_BYTE_COST: u64 = 100;
/// A helper function to determine the plaintext type in bytes.
fn plaintext_size_in_bytes<N: Network>(stack: &Stack<N>, plaintext_type: &PlaintextType<N>) -> Result<u64> {
match plaintext_type {
PlaintextType::Literal(literal_type) => Ok(literal_type.size_in_bytes::<N>() as u64),
PlaintextType::Struct(struct_name) => {
// Retrieve the struct from the stack.
let struct_ = stack.program().get_struct(struct_name)?;
// Retrieve the size of the struct name.
let size_of_name = struct_.name().to_bytes_le()?.len() as u64;
// Retrieve the size of all the members of the struct.
let size_of_members = struct_.members().iter().try_fold(0u64, |acc, (_, member_type)| {
acc.checked_add(plaintext_size_in_bytes(stack, member_type)?).ok_or(anyhow!(
"Overflowed while computing the size of the struct '{}/{struct_name}' - {member_type}",
stack.program_id()
))
})?;
// Return the size of the struct.
Ok(size_of_name.saturating_add(size_of_members))
}
PlaintextType::Array(array_type) => {
// Retrieve the number of elements in the array.
let num_elements = **array_type.length() as u64;
// Compute the size of an array element.
let size_of_element = plaintext_size_in_bytes(stack, array_type.next_element_type())?;
// Return the size of the array.
Ok(num_elements.saturating_mul(size_of_element))
}
}
}

// Retrieve the finalize logic.
let Some(finalize) = stack.get_function_ref(function_name)?.finalize_logic() else {
// Return a finalize cost of 0, if the function does not have a finalize scope.
return Ok(0);
};
/// A helper function to compute the following: base_cost + (byte_multiplier * size_of_operands).
fn cost_in_size<'a, N: Network>(
stack: &Stack<N>,
finalize: &Finalize<N>,
operands: impl IntoIterator<Item = &'a Operand<N>>,
byte_multiplier: u64,
base_cost: u64,
) -> Result<u64> {
// Retrieve the finalize types.
let finalize_types = stack.get_finalize_types(finalize.name())?;
// Compute the size of the operands.
let size_of_operands = operands.into_iter().try_fold(0u64, |acc, operand| {
// Determine the size of the operand.
let operand_size = match finalize_types.get_type_from_operand(stack, operand)? {
FinalizeType::Plaintext(plaintext_type) => plaintext_size_in_bytes(stack, &plaintext_type)?,
FinalizeType::Future(future) => {
bail!("Future '{future}' is not a valid operand in the finalize scope");
}
};
// Safely add the size to the accumulator.
acc.checked_add(operand_size).ok_or(anyhow!(
"Overflowed while computing the size of the operand '{operand}' in '{}/{}' (finalize)",
stack.program_id(),
finalize.name()
))
})?;
// Return the cost.
Ok(base_cost.saturating_add(byte_multiplier.saturating_mul(size_of_operands)))
}

// Measure the cost of each command.
let cost = |command: &Command<N>| match command {
/// Returns the the cost of a command in a finalize scope.
pub fn cost_per_command<N: Network>(stack: &Stack<N>, finalize: &Finalize<N>, command: &Command<N>) -> Result<u64> {
match command {
Command::Instruction(Instruction::Abs(_)) => Ok(500),
Command::Instruction(Instruction::AbsWrapped(_)) => Ok(500),
Command::Instruction(Instruction::Add(_)) => Ok(500),
Expand Down Expand Up @@ -358,8 +351,16 @@ pub fn cost_in_microcredits<N: Network>(stack: &Stack<N>, function_name: &Identi
}
Command::BranchEq(_) | Command::BranchNeq(_) => Ok(500),
Command::Position(_) => Ok(100),
};
}
}

/// Returns the minimum number of microcredits required to run the finalize.
pub fn cost_in_microcredits<N: Network>(stack: &Stack<N>, function_name: &Identifier<N>) -> Result<u64> {
// Retrieve the finalize logic.
let Some(finalize) = stack.get_function_ref(function_name)?.finalize_logic() else {
// Return a finalize cost of 0, if the function does not have a finalize scope.
return Ok(0);
};
// Get the cost of finalizing all futures.
let mut future_cost = 0u64;
for input in finalize.inputs() {
Expand All @@ -372,9 +373,12 @@ pub fn cost_in_microcredits<N: Network>(stack: &Stack<N>, function_name: &Identi
.ok_or(anyhow!("Finalize cost overflowed"))?;
}
}

// Aggregate the cost of all commands in the program.
finalize.commands().iter().map(cost).try_fold(future_cost, |acc, res| {
res.and_then(|x| acc.checked_add(x).ok_or(anyhow!("Finalize cost overflowed")))
})
finalize
.commands()
.iter()
.map(|command| cost_per_command(stack, finalize, command))
.try_fold(future_cost, |acc, res| {
res.and_then(|x| acc.checked_add(x).ok_or(anyhow!("Finalize cost overflowed")))
})
}

0 comments on commit dd01117

Please sign in to comment.