- update cost calculation to closely proposed fee schedule #16984
This commit is contained in:
parent
17314f4a95
commit
675fa6993b
|
@ -1,10 +1,7 @@
|
||||||
//! 'cost_model` provides service to estimate a transaction's cost
|
//! 'cost_model` provides service to estimate a transaction's cost
|
||||||
//! It does so by analyzing accounts the transaction touches, and instructions
|
//! following proposed fee schedule #16984; Relevant cluster cost
|
||||||
//! it includes. Using historical data as guideline, it estimates cost of
|
//! measuring is described by #19627
|
||||||
//! reading/writing account, the sum of that comes up to "account access cost";
|
//!
|
||||||
//! Instructions take time to execute, both historical and runtime data are
|
|
||||||
//! used to determine each instruction's execution time, the sum of that
|
|
||||||
//! is transaction's "execution cost"
|
|
||||||
//! The main function is `calculate_cost` which returns &TransactionCost.
|
//! The main function is `calculate_cost` which returns &TransactionCost.
|
||||||
//!
|
//!
|
||||||
use crate::execute_cost_table::ExecuteCostTable;
|
use crate::execute_cost_table::ExecuteCostTable;
|
||||||
|
@ -28,16 +25,12 @@ pub enum CostModelError {
|
||||||
WouldExceedAccountMaxLimit,
|
WouldExceedAccountMaxLimit,
|
||||||
}
|
}
|
||||||
|
|
||||||
// cost of transaction is made of account_access_cost and instruction execution_cost
|
|
||||||
// where
|
|
||||||
// account_access_cost is the sum of read/write/sign all accounts included in the transaction
|
|
||||||
// read is cheaper than write.
|
|
||||||
// execution_cost is the sum of all instructions execution cost, which is
|
|
||||||
// observed during runtime and feedback by Replay
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct TransactionCost {
|
pub struct TransactionCost {
|
||||||
pub writable_accounts: Vec<Pubkey>,
|
pub writable_accounts: Vec<Pubkey>,
|
||||||
pub account_access_cost: u64,
|
pub signature_cost: u64,
|
||||||
|
pub write_lock_cost: u64,
|
||||||
|
pub data_bytes_cost: u64,
|
||||||
pub execution_cost: u64,
|
pub execution_cost: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,9 +44,15 @@ impl TransactionCost {
|
||||||
|
|
||||||
pub fn reset(&mut self) {
|
pub fn reset(&mut self) {
|
||||||
self.writable_accounts.clear();
|
self.writable_accounts.clear();
|
||||||
self.account_access_cost = 0;
|
self.signature_cost = 0;
|
||||||
|
self.write_lock_cost = 0;
|
||||||
|
self.data_bytes_cost = 0;
|
||||||
self.execution_cost = 0;
|
self.execution_cost = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn sum(&self) -> u64 {
|
||||||
|
self.signature_cost + self.write_lock_cost + self.data_bytes_cost + self.execution_cost
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -68,7 +67,7 @@ pub struct CostModel {
|
||||||
|
|
||||||
impl Default for CostModel {
|
impl Default for CostModel {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
CostModel::new(ACCOUNT_COST_MAX, BLOCK_COST_MAX)
|
CostModel::new(MAX_WRITABLE_ACCOUNT_UNITS, MAX_BLOCK_UNITS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,22 +90,29 @@ impl CostModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn initialize_cost_table(&mut self, cost_table: &[(Pubkey, u64)]) {
|
pub fn initialize_cost_table(&mut self, cost_table: &[(Pubkey, u64)]) {
|
||||||
for (program_id, cost) in cost_table {
|
cost_table
|
||||||
match self.upsert_instruction_cost(program_id, *cost) {
|
.iter()
|
||||||
Ok(c) => {
|
.map(|(key, cost)| (key, cost))
|
||||||
debug!(
|
.chain(BUILT_IN_INSTRUCTION_COSTS.iter())
|
||||||
"initiating cost table, instruction {:?} has cost {}",
|
.for_each(|(program_id, cost)| {
|
||||||
program_id, c
|
match self
|
||||||
);
|
.instruction_execution_cost_table
|
||||||
|
.upsert(program_id, *cost)
|
||||||
|
{
|
||||||
|
Some(c) => {
|
||||||
|
debug!(
|
||||||
|
"initiating cost table, instruction {:?} has cost {}",
|
||||||
|
program_id, c
|
||||||
|
);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
debug!(
|
||||||
|
"initiating cost table, failed for instruction {:?}",
|
||||||
|
program_id
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
});
|
||||||
debug!(
|
|
||||||
"initiating cost table, failed for instruction {:?}, err: {}",
|
|
||||||
program_id, err
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
debug!(
|
debug!(
|
||||||
"restored cost model instruction cost table from blockstore, current values: {:?}",
|
"restored cost model instruction cost table from blockstore, current values: {:?}",
|
||||||
self.get_instruction_cost_table()
|
self.get_instruction_cost_table()
|
||||||
|
@ -120,21 +126,11 @@ impl CostModel {
|
||||||
) -> &TransactionCost {
|
) -> &TransactionCost {
|
||||||
self.transaction_cost.reset();
|
self.transaction_cost.reset();
|
||||||
|
|
||||||
// calculate transaction exeution cost
|
self.transaction_cost.signature_cost = self.get_signature_cost(transaction);
|
||||||
self.transaction_cost.execution_cost = self.find_transaction_cost(transaction);
|
self.get_write_lock_cost(transaction, demote_program_write_locks);
|
||||||
|
self.transaction_cost.data_bytes_cost = self.get_data_bytes_cost(transaction);
|
||||||
|
self.transaction_cost.execution_cost = self.get_transaction_cost(transaction);
|
||||||
|
|
||||||
// calculate account access cost
|
|
||||||
let message = transaction.message();
|
|
||||||
message.account_keys_iter().enumerate().for_each(|(i, k)| {
|
|
||||||
let is_writable = message.is_writable(i, demote_program_write_locks);
|
|
||||||
|
|
||||||
if is_writable {
|
|
||||||
self.transaction_cost.writable_accounts.push(*k);
|
|
||||||
self.transaction_cost.account_access_cost += ACCOUNT_WRITE_COST;
|
|
||||||
} else {
|
|
||||||
self.transaction_cost.account_access_cost += ACCOUNT_READ_COST;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
debug!(
|
debug!(
|
||||||
"transaction {:?} has cost {:?}",
|
"transaction {:?} has cost {:?}",
|
||||||
transaction, self.transaction_cost
|
transaction, self.transaction_cost
|
||||||
|
@ -142,7 +138,6 @@ impl CostModel {
|
||||||
&self.transaction_cost
|
&self.transaction_cost
|
||||||
}
|
}
|
||||||
|
|
||||||
// To update or insert instruction cost to table.
|
|
||||||
pub fn upsert_instruction_cost(
|
pub fn upsert_instruction_cost(
|
||||||
&mut self,
|
&mut self,
|
||||||
program_key: &Pubkey,
|
program_key: &Pubkey,
|
||||||
|
@ -160,6 +155,52 @@ impl CostModel {
|
||||||
self.instruction_execution_cost_table.get_cost_table()
|
self.instruction_execution_cost_table.get_cost_table()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_signature_cost(&self, transaction: &SanitizedTransaction) -> u64 {
|
||||||
|
transaction.signatures().len() as u64 * SIGNATURE_COST
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_write_lock_cost(
|
||||||
|
&mut self,
|
||||||
|
transaction: &SanitizedTransaction,
|
||||||
|
demote_program_write_locks: bool,
|
||||||
|
) {
|
||||||
|
let message = transaction.message();
|
||||||
|
message.account_keys_iter().enumerate().for_each(|(i, k)| {
|
||||||
|
let is_writable = message.is_writable(i, demote_program_write_locks);
|
||||||
|
|
||||||
|
if is_writable {
|
||||||
|
self.transaction_cost.writable_accounts.push(*k);
|
||||||
|
self.transaction_cost.write_lock_cost += WRITE_LOCK_UNITS;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_data_bytes_cost(&self, transaction: &SanitizedTransaction) -> u64 {
|
||||||
|
let mut data_bytes_cost: u64 = 0;
|
||||||
|
transaction
|
||||||
|
.message()
|
||||||
|
.program_instructions_iter()
|
||||||
|
.for_each(|(_, ix)| {
|
||||||
|
data_bytes_cost += ix.data.len() as u64 / DATA_BYTES_UNITS;
|
||||||
|
});
|
||||||
|
data_bytes_cost
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_transaction_cost(&self, transaction: &SanitizedTransaction) -> u64 {
|
||||||
|
let mut cost: u64 = 0;
|
||||||
|
|
||||||
|
for (program_id, instruction) in transaction.message().program_instructions_iter() {
|
||||||
|
let instruction_cost = self.find_instruction_cost(program_id);
|
||||||
|
trace!(
|
||||||
|
"instruction {:?} has cost of {}",
|
||||||
|
instruction,
|
||||||
|
instruction_cost
|
||||||
|
);
|
||||||
|
cost = cost.saturating_add(instruction_cost);
|
||||||
|
}
|
||||||
|
cost
|
||||||
|
}
|
||||||
|
|
||||||
fn find_instruction_cost(&self, program_key: &Pubkey) -> u64 {
|
fn find_instruction_cost(&self, program_key: &Pubkey) -> u64 {
|
||||||
match self.instruction_execution_cost_table.get_cost(program_key) {
|
match self.instruction_execution_cost_table.get_cost(program_key) {
|
||||||
Some(cost) => *cost,
|
Some(cost) => *cost,
|
||||||
|
@ -173,21 +214,6 @@ impl CostModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_transaction_cost(&self, transaction: &SanitizedTransaction) -> u64 {
|
|
||||||
let mut cost: u64 = 0;
|
|
||||||
|
|
||||||
for (program_id, instruction) in transaction.message().program_instructions_iter() {
|
|
||||||
let instruction_cost = self.find_instruction_cost(program_id);
|
|
||||||
trace!(
|
|
||||||
"instruction {:?} has cost of {}",
|
|
||||||
instruction,
|
|
||||||
instruction_cost
|
|
||||||
);
|
|
||||||
cost += instruction_cost;
|
|
||||||
}
|
|
||||||
cost
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -272,7 +298,7 @@ mod tests {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
expected_cost,
|
expected_cost,
|
||||||
testee.find_transaction_cost(&simple_transaction)
|
testee.get_transaction_cost(&simple_transaction)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,7 +324,7 @@ mod tests {
|
||||||
testee
|
testee
|
||||||
.upsert_instruction_cost(&system_program::id(), program_cost)
|
.upsert_instruction_cost(&system_program::id(), program_cost)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(expected_cost, testee.find_transaction_cost(&tx));
|
assert_eq!(expected_cost, testee.get_transaction_cost(&tx));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -326,7 +352,7 @@ mod tests {
|
||||||
debug!("many random transaction {:?}", tx);
|
debug!("many random transaction {:?}", tx);
|
||||||
|
|
||||||
let testee = CostModel::default();
|
let testee = CostModel::default();
|
||||||
let result = testee.find_transaction_cost(&tx);
|
let result = testee.get_transaction_cost(&tx);
|
||||||
|
|
||||||
// expected cost for two random/unknown program is
|
// expected cost for two random/unknown program is
|
||||||
let expected_cost = testee.instruction_execution_cost_table.get_mode() * 2;
|
let expected_cost = testee.instruction_execution_cost_table.get_mode() * 2;
|
||||||
|
@ -392,7 +418,7 @@ mod tests {
|
||||||
.try_into()
|
.try_into()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let expected_account_cost = ACCOUNT_WRITE_COST + ACCOUNT_WRITE_COST + ACCOUNT_READ_COST;
|
let expected_account_cost = WRITE_LOCK_UNITS * 2;
|
||||||
let expected_execution_cost = 8;
|
let expected_execution_cost = 8;
|
||||||
|
|
||||||
let mut cost_model = CostModel::default();
|
let mut cost_model = CostModel::default();
|
||||||
|
@ -400,7 +426,7 @@ mod tests {
|
||||||
.upsert_instruction_cost(&system_program::id(), expected_execution_cost)
|
.upsert_instruction_cost(&system_program::id(), expected_execution_cost)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let tx_cost = cost_model.calculate_cost(&tx, /*demote_program_write_locks=*/ true);
|
let tx_cost = cost_model.calculate_cost(&tx, /*demote_program_write_locks=*/ true);
|
||||||
assert_eq!(expected_account_cost, tx_cost.account_access_cost);
|
assert_eq!(expected_account_cost, tx_cost.write_lock_cost);
|
||||||
assert_eq!(expected_execution_cost, tx_cost.execution_cost);
|
assert_eq!(expected_execution_cost, tx_cost.execution_cost);
|
||||||
assert_eq!(2, tx_cost.writable_accounts.len());
|
assert_eq!(2, tx_cost.writable_accounts.len());
|
||||||
}
|
}
|
||||||
|
@ -447,8 +473,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
let number_threads = 10;
|
let number_threads = 10;
|
||||||
let expected_account_cost =
|
let expected_account_cost = WRITE_LOCK_UNITS * 3;
|
||||||
ACCOUNT_WRITE_COST + ACCOUNT_WRITE_COST * 2 + ACCOUNT_READ_COST * 2;
|
|
||||||
let cost1 = 100;
|
let cost1 = 100;
|
||||||
let cost2 = 200;
|
let cost2 = 200;
|
||||||
// execution cost can be either 2 * Default (before write) or cost1+cost2 (after write)
|
// execution cost can be either 2 * Default (before write) or cost1+cost2 (after write)
|
||||||
|
@ -472,7 +497,7 @@ mod tests {
|
||||||
let tx_cost = cost_model
|
let tx_cost = cost_model
|
||||||
.calculate_cost(&tx, /*demote_program_write_locks=*/ true);
|
.calculate_cost(&tx, /*demote_program_write_locks=*/ true);
|
||||||
assert_eq!(3, tx_cost.writable_accounts.len());
|
assert_eq!(3, tx_cost.writable_accounts.len());
|
||||||
assert_eq!(expected_account_cost, tx_cost.account_access_cost);
|
assert_eq!(expected_account_cost, tx_cost.write_lock_cost);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -484,7 +509,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_cost_model_init_cost_table() {
|
fn test_initialize_cost_table() {
|
||||||
// build cost table
|
// build cost table
|
||||||
let cost_table = vec![
|
let cost_table = vec![
|
||||||
(Pubkey::new_unique(), 10),
|
(Pubkey::new_unique(), 10),
|
||||||
|
@ -500,5 +525,15 @@ mod tests {
|
||||||
for (id, cost) in cost_table.iter() {
|
for (id, cost) in cost_table.iter() {
|
||||||
assert_eq!(*cost, cost_model.find_instruction_cost(id));
|
assert_eq!(*cost, cost_model.find_instruction_cost(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// verify built-in programs
|
||||||
|
assert!(cost_model
|
||||||
|
.instruction_execution_cost_table
|
||||||
|
.get_cost(&system_program::id())
|
||||||
|
.is_some());
|
||||||
|
assert!(cost_model
|
||||||
|
.instruction_execution_cost_table
|
||||||
|
.get_cost(&solana_vote_program::id())
|
||||||
|
.is_some());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,11 +52,7 @@ impl CostTracker {
|
||||||
) -> Result<(), CostModelError> {
|
) -> Result<(), CostModelError> {
|
||||||
let mut cost_model = self.cost_model.write().unwrap();
|
let mut cost_model = self.cost_model.write().unwrap();
|
||||||
let tx_cost = cost_model.calculate_cost(transaction, demote_program_write_locks);
|
let tx_cost = cost_model.calculate_cost(transaction, demote_program_write_locks);
|
||||||
self.would_fit(
|
self.would_fit(&tx_cost.writable_accounts, &tx_cost.sum(), stats)
|
||||||
&tx_cost.writable_accounts,
|
|
||||||
&(tx_cost.account_access_cost + tx_cost.execution_cost),
|
|
||||||
stats,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_transaction_cost(
|
pub fn add_transaction_cost(
|
||||||
|
@ -67,7 +63,7 @@ impl CostTracker {
|
||||||
) {
|
) {
|
||||||
let mut cost_model = self.cost_model.write().unwrap();
|
let mut cost_model = self.cost_model.write().unwrap();
|
||||||
let tx_cost = cost_model.calculate_cost(transaction, demote_program_write_locks);
|
let tx_cost = cost_model.calculate_cost(transaction, demote_program_write_locks);
|
||||||
let cost = tx_cost.account_access_cost + tx_cost.execution_cost;
|
let cost = tx_cost.sum();
|
||||||
for account_key in tx_cost.writable_accounts.iter() {
|
for account_key in tx_cost.writable_accounts.iter() {
|
||||||
*self
|
*self
|
||||||
.cost_by_writable_accounts
|
.cost_by_writable_accounts
|
||||||
|
@ -103,7 +99,7 @@ impl CostTracker {
|
||||||
transaction_cost: &TransactionCost,
|
transaction_cost: &TransactionCost,
|
||||||
stats: &mut CostTrackerStats,
|
stats: &mut CostTrackerStats,
|
||||||
) -> Result<u64, CostModelError> {
|
) -> Result<u64, CostModelError> {
|
||||||
let cost = transaction_cost.account_access_cost + transaction_cost.execution_cost;
|
let cost = transaction_cost.sum();
|
||||||
self.would_fit(&transaction_cost.writable_accounts, &cost, stats)?;
|
self.would_fit(&transaction_cost.writable_accounts, &cost, stats)?;
|
||||||
|
|
||||||
self.add_transaction(&transaction_cost.writable_accounts, &cost);
|
self.add_transaction(&transaction_cost.writable_accounts, &cost);
|
||||||
|
@ -428,8 +424,8 @@ mod tests {
|
||||||
{
|
{
|
||||||
let tx_cost = TransactionCost {
|
let tx_cost = TransactionCost {
|
||||||
writable_accounts: vec![acct1, acct2, acct3],
|
writable_accounts: vec![acct1, acct2, acct3],
|
||||||
account_access_cost: 0,
|
|
||||||
execution_cost: cost,
|
execution_cost: cost,
|
||||||
|
..TransactionCost::default()
|
||||||
};
|
};
|
||||||
assert!(testee
|
assert!(testee
|
||||||
.try_add(&tx_cost, &mut CostTrackerStats::default())
|
.try_add(&tx_cost, &mut CostTrackerStats::default())
|
||||||
|
@ -448,8 +444,8 @@ mod tests {
|
||||||
{
|
{
|
||||||
let tx_cost = TransactionCost {
|
let tx_cost = TransactionCost {
|
||||||
writable_accounts: vec![acct2],
|
writable_accounts: vec![acct2],
|
||||||
account_access_cost: 0,
|
|
||||||
execution_cost: cost,
|
execution_cost: cost,
|
||||||
|
..TransactionCost::default()
|
||||||
};
|
};
|
||||||
assert!(testee
|
assert!(testee
|
||||||
.try_add(&tx_cost, &mut CostTrackerStats::default())
|
.try_add(&tx_cost, &mut CostTrackerStats::default())
|
||||||
|
@ -470,8 +466,8 @@ mod tests {
|
||||||
{
|
{
|
||||||
let tx_cost = TransactionCost {
|
let tx_cost = TransactionCost {
|
||||||
writable_accounts: vec![acct1, acct2],
|
writable_accounts: vec![acct1, acct2],
|
||||||
account_access_cost: 0,
|
|
||||||
execution_cost: cost,
|
execution_cost: cost,
|
||||||
|
..TransactionCost::default()
|
||||||
};
|
};
|
||||||
assert!(testee
|
assert!(testee
|
||||||
.try_add(&tx_cost, &mut CostTrackerStats::default())
|
.try_add(&tx_cost, &mut CostTrackerStats::default())
|
||||||
|
|
|
@ -78,7 +78,7 @@ impl ExecuteCostTable {
|
||||||
self.table.get(key)
|
self.table.get(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn upsert(&mut self, key: &Pubkey, value: u64) {
|
pub fn upsert(&mut self, key: &Pubkey, value: u64) -> Option<u64> {
|
||||||
let need_to_add = self.table.get(key).is_none();
|
let need_to_add = self.table.get(key).is_none();
|
||||||
let current_size = self.get_count();
|
let current_size = self.get_count();
|
||||||
if current_size == self.capacity && need_to_add {
|
if current_size == self.capacity && need_to_add {
|
||||||
|
@ -94,6 +94,8 @@ impl ExecuteCostTable {
|
||||||
.or_insert((0, SystemTime::now()));
|
.or_insert((0, SystemTime::now()));
|
||||||
*count += 1;
|
*count += 1;
|
||||||
*timestamp = SystemTime::now();
|
*timestamp = SystemTime::now();
|
||||||
|
|
||||||
|
Some(*program_cost)
|
||||||
}
|
}
|
||||||
|
|
||||||
// prune the old programs so the table contains `new_size` of records,
|
// prune the old programs so the table contains `new_size` of records,
|
||||||
|
|
|
@ -1,34 +1,56 @@
|
||||||
//! defines block cost related limits
|
//! defines block cost related limits
|
||||||
//!
|
//!
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use solana_sdk::{
|
||||||
|
feature, incinerator, native_loader, pubkey::Pubkey, secp256k1_program, system_program,
|
||||||
|
};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
// see https://github.com/solana-labs/solana/issues/18944
|
/// Static configurations:
|
||||||
// and https://github.com/solana-labs/solana/pull/18994#issuecomment-896128992
|
///
|
||||||
//
|
/// Number of microseconds replaying a block should take, 400 millisecond block times
|
||||||
pub const MAX_BLOCK_TIME_US: u64 = 400_000; // aiming at 400ms/block max time
|
/// is curerntly publicly communicated on solana.com
|
||||||
pub const AVG_INSTRUCTION_TIME_US: u64 = 1_000; // average instruction execution time
|
pub const MAX_BLOCK_REPLAY_TIME_US: u64 = 400_000;
|
||||||
pub const SYSTEM_PARALLELISM: u64 = 10;
|
/// number of concurrent processes,
|
||||||
pub const MAX_INSTRUCTION_COST: u64 = 200_000;
|
pub const MAX_CONCURRENCY: u64 = 10;
|
||||||
pub const MAX_NUMBER_BPF_INSTRUCTIONS_PER_ACCOUNT: u64 = 200;
|
|
||||||
|
|
||||||
// 4_000
|
/// Cluster data, method of collecting at https://github.com/solana-labs/solana/issues/19627
|
||||||
pub const MAX_INSTRUCTIONS_PER_BLOCK: u64 =
|
///
|
||||||
(MAX_BLOCK_TIME_US / AVG_INSTRUCTION_TIME_US) * SYSTEM_PARALLELISM;
|
/// cluster avergaed compute unit to microsec conversion rate
|
||||||
|
pub const COMPUTE_UNIT_TO_US_RATIO: u64 = 40;
|
||||||
|
/// Number of compute units for one signature verification.
|
||||||
|
pub const SIGNATURE_COST: u64 = COMPUTE_UNIT_TO_US_RATIO * 175;
|
||||||
|
/// Number of compute units for one write lock
|
||||||
|
pub const WRITE_LOCK_UNITS: u64 = COMPUTE_UNIT_TO_US_RATIO * 20;
|
||||||
|
/// Number of data bytes per compute units
|
||||||
|
pub const DATA_BYTES_UNITS: u64 = 220 /*bytes per us*/ / COMPUTE_UNIT_TO_US_RATIO;
|
||||||
|
// Number of compute units for each built-in programs
|
||||||
|
lazy_static! {
|
||||||
|
/// Number of compute units for each built-in programs
|
||||||
|
pub static ref BUILT_IN_INSTRUCTION_COSTS: HashMap<Pubkey, u64> = [
|
||||||
|
(feature::id(), COMPUTE_UNIT_TO_US_RATIO * 2),
|
||||||
|
(incinerator::id(), COMPUTE_UNIT_TO_US_RATIO * 2),
|
||||||
|
(native_loader::id(), COMPUTE_UNIT_TO_US_RATIO * 2),
|
||||||
|
(solana_sdk::stake::config::id(), COMPUTE_UNIT_TO_US_RATIO * 2),
|
||||||
|
(solana_sdk::stake::program::id(), COMPUTE_UNIT_TO_US_RATIO * 50),
|
||||||
|
(solana_vote_program::id(), COMPUTE_UNIT_TO_US_RATIO * 200),
|
||||||
|
(secp256k1_program::id(), COMPUTE_UNIT_TO_US_RATIO * 4),
|
||||||
|
(system_program::id(), COMPUTE_UNIT_TO_US_RATIO * 15),
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
|
||||||
// 8_000_000_000
|
/// Statically computed data:
|
||||||
pub const BLOCK_COST_MAX: u64 = MAX_INSTRUCTION_COST * MAX_INSTRUCTIONS_PER_BLOCK * 10;
|
///
|
||||||
|
/// Number of compute units that a block is allowed. A block's compute units are
|
||||||
// 800_000_000
|
/// accumualted by Transactions added to it; A transaction's compute units are
|
||||||
pub const ACCOUNT_COST_MAX: u64 = MAX_INSTRUCTION_COST * MAX_INSTRUCTIONS_PER_BLOCK;
|
/// calculated by cost_model, based on transaction's signarures, write locks,
|
||||||
|
/// data size and built-in and BPF instructinos.
|
||||||
// 2_000
|
pub const MAX_BLOCK_UNITS: u64 =
|
||||||
pub const COMPUTE_UNIT_TO_US_RATIO: u64 =
|
MAX_BLOCK_REPLAY_TIME_US * COMPUTE_UNIT_TO_US_RATIO * MAX_CONCURRENCY;
|
||||||
(MAX_INSTRUCTION_COST / AVG_INSTRUCTION_TIME_US) * SYSTEM_PARALLELISM;
|
/// Number of compute units that a writable account in a block is allowed. The
|
||||||
|
/// limit is to prevent too many transactions write to same account, threrefore
|
||||||
// signature takes average 10us, or 20K CU
|
/// reduce block's paralellism.
|
||||||
pub const SIGNATURE_COST: u64 = COMPUTE_UNIT_TO_US_RATIO * 10;
|
pub const MAX_WRITABLE_ACCOUNT_UNITS: u64 = MAX_BLOCK_REPLAY_TIME_US * COMPUTE_UNIT_TO_US_RATIO;
|
||||||
|
|
||||||
// read account averages 5us, or 10K CU
|
|
||||||
pub const ACCOUNT_READ_COST: u64 = COMPUTE_UNIT_TO_US_RATIO * 5;
|
|
||||||
|
|
||||||
// write account averages 25us, or 50K CU
|
|
||||||
pub const ACCOUNT_WRITE_COST: u64 = COMPUTE_UNIT_TO_US_RATIO * 25;
|
|
||||||
|
|
|
@ -66,7 +66,7 @@ pub struct BlockCostCapacityMeter {
|
||||||
|
|
||||||
impl Default for BlockCostCapacityMeter {
|
impl Default for BlockCostCapacityMeter {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
BlockCostCapacityMeter::new(BLOCK_COST_MAX)
|
BlockCostCapacityMeter::new(MAX_BLOCK_UNITS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,7 +143,8 @@ fn aggregate_total_execution_units(execute_timings: &ExecuteTimings) -> u64 {
|
||||||
if timing.count < 1 {
|
if timing.count < 1 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
execute_cost_units += timing.accumulated_units / timing.count as u64;
|
execute_cost_units =
|
||||||
|
execute_cost_units.saturating_add(timing.accumulated_units / timing.count as u64);
|
||||||
trace!("aggregated execution cost of {:?} {:?}", program_id, timing);
|
trace!("aggregated execution cost of {:?} {:?}", program_id, timing);
|
||||||
}
|
}
|
||||||
execute_cost_units
|
execute_cost_units
|
||||||
|
|
Loading…
Reference in New Issue