Repository: stellar/rs-soroban-env
Pull request #982 (2023-08-03 20:47:43)

Scale the linear cost model coefficient; improve model fitting
### What

Resolves https://github.com/stellar/rs-soroban-env/issues/824

Add a scaling factor `COST_MODEL_LIN_TERM_SCALE_BITS` by which the linear cost model coefficients is scaled. 

Minor improvements in the measurement and fitting code, such as choosing a large enough step size (wrote a big comment for it) and using a proper "ceiling" on the coefficients.

Some minor clean ups and renaming.

### Why

All the cost coefficients are u64. Some of the linear coefficients are small, e.g. for `HostMemCmp`, `HostMemCpy`,  and previously we rounded them off and make them a const model. 
A large portion of the code base is doing memory operation, thus a small round off may lead to significant diff in actual metering accuracy. 
Multiplying a constant scaling factor by the linear coefficient is an easy way (compared to using say fixed-point integers) to solving this problem. 

Here is a rough impact of this change in metering benchmark results:

```diff
running 2 tests
test test::metering_benchmark::run_add_i32 ... 
-model cpu consumed: 1147842, actual cpu insns 1460911 
+model cpu consumed: 1568349, actual cpu insns 1462032
-model mem consumed: 1274594, actual mem bytes 1213688
+model mem consumed: 1287091, actual mem bytes 1213688
ok
test test::metering_benchmark::run_complex ... 
-model cpu consumed: 1630980, actual cpu insns 1827787 
+model cpu consumed: 2061258, actual cpu insns 1816229
-model mem consumed: 1308664, actual mem bytes 1245216
+model mem consumed: 1321108, actual mem bytes 1245216
ok
```
Previously we were pretty heavily under-underestimating the cpu cost, and now we are slightly overestimating (which is what our model is supposed to do). 

### Known limitations

[TODO or N/A]

Here is the model fitting [output](https://github.com/stellar/rs-soroban-env/files/12254978/output20.txt)



jayz22: > 

That's a good suggestion! I will make the follow up change in https://github.com/stellar/rs-soroban-env/pull/984

Patch:
diff --git a/soroban-env-host/benches/common/cost_types/compute_keccak256_hash.rs b/soroban-env-host/benches/common/cost_types/compute_keccak256_hash.rs
index e76f3d21..4409af88 100644
--- a/soroban-env-host/benches/common/cost_types/compute_keccak256_hash.rs
+++ b/soroban-env-host/benches/common/cost_types/compute_keccak256_hash.rs
@@ -10,8 +10,6 @@ pub(crate) struct ComputeKeccak256HashMeasure;
 impl HostCostMeasurement for ComputeKeccak256HashMeasure {
     type Runner = ComputeKeccak256HashRun;
 
-    const STEP_SIZE: u64 = 100;
-
     fn new_random_case(_host: &Host, _rng: &mut StdRng, input: u64) -> Vec<u8> {
         let size = 1 + input * Self::STEP_SIZE;
         (0..size).map(|n| n as u8).collect()
diff --git a/soroban-env-host/benches/common/cost_types/compute_sha256_hash.rs b/soroban-env-host/benches/common/cost_types/compute_sha256_hash.rs
index c64cdc73..f8d8cd49 100644
--- a/soroban-env-host/benches/common/cost_types/compute_sha256_hash.rs
+++ b/soroban-env-host/benches/common/cost_types/compute_sha256_hash.rs
@@ -10,8 +10,6 @@ pub(crate) struct ComputeSha256HashMeasure;
 impl HostCostMeasurement for ComputeSha256HashMeasure {
     type Runner = ComputeSha256HashRun;
 
-    const STEP_SIZE: u64 = 100;
-
     fn new_random_case(_host: &Host, _rng: &mut StdRng, input: u64) -> Vec<u8> {
         let size = 1 + input * Self::STEP_SIZE;
         (0..size).map(|n| n as u8).collect()
diff --git a/soroban-env-host/benches/common/cost_types/host_mem_cmp.rs b/soroban-env-host/benches/common/cost_types/host_mem_cmp.rs
index 3f5094a0..4e83541a 100644
--- a/soroban-env-host/benches/common/cost_types/host_mem_cmp.rs
+++ b/soroban-env-host/benches/common/cost_types/host_mem_cmp.rs
@@ -12,6 +12,8 @@ pub struct HostMemCmpMeasure;
 impl HostCostMeasurement for HostMemCmpMeasure {
     type Runner = HostMemCmpRun;
 
+    const STEP_SIZE: u64 = 4096;
+
     fn new_random_case(
         _host: &Host,
         rng: &mut StdRng,
diff --git a/soroban-env-host/benches/common/cost_types/host_mem_cpy.rs b/soroban-env-host/benches/common/cost_types/host_mem_cpy.rs
index 67adc7f8..06e624cf 100644
--- a/soroban-env-host/benches/common/cost_types/host_mem_cpy.rs
+++ b/soroban-env-host/benches/common/cost_types/host_mem_cpy.rs
@@ -9,6 +9,8 @@ pub(crate) struct HostMemCpyMeasure;
 impl HostCostMeasurement for HostMemCpyMeasure {
     type Runner = HostMemCpyRun;
 
+    const STEP_SIZE: u64 = 4096;
+
     fn new_random_case(_host: &Host, rng: &mut StdRng, input: u64) -> (Vec<u8>, Vec<u8>) {
         let len = 1 + input * Self::STEP_SIZE;
         let mut a = vec![0; len as usize];
diff --git a/soroban-env-host/benches/common/cost_types/map_ops.rs b/soroban-env-host/benches/common/cost_types/map_ops.rs
index 8e0e2021..4878d1f4 100644
--- a/soroban-env-host/benches/common/cost_types/map_ops.rs
+++ b/soroban-env-host/benches/common/cost_types/map_ops.rs
@@ -12,8 +12,6 @@ pub(crate) struct MapEntryMeasure;
 impl HostCostMeasurement for MapEntryMeasure {
     type Runner = MapEntryRun;
 
-    const STEP_SIZE: u64 = 100;
-
     fn new_random_case(host: &Host, rng: &mut StdRng, input: u64) -> MapEntrySample {
         let input = 1 + input * Self::STEP_SIZE;
         let mut keys: Vec<_> = util::to_rawval_u32(0..(input as u32)).collect();
diff --git a/soroban-env-host/benches/common/cost_types/recover_ecdsa_secp256k1_key.rs b/soroban-env-host/benches/common/cost_types/recover_ecdsa_secp256k1_key.rs
index 3d390be6..4a7d5d9a 100644
--- a/soroban-env-host/benches/common/cost_types/recover_ecdsa_secp256k1_key.rs
+++ b/soroban-env-host/benches/common/cost_types/recover_ecdsa_secp256k1_key.rs
@@ -16,8 +16,6 @@ pub(crate) struct RecoverEcdsaSecp256k1KeyMeasure;
 impl HostCostMeasurement for RecoverEcdsaSecp256k1KeyMeasure {
     type Runner = RecoverEcdsaSecp256k1KeyRun;
 
-    const STEP_SIZE: u64 = 1000;
-
     fn new_random_case(
         _host: &Host,
         _rng: &mut StdRng,
diff --git a/soroban-env-host/benches/common/cost_types/val_ser.rs b/soroban-env-host/benches/common/cost_types/val_ser.rs
index 4f374bdc..1b7ba5c5 100644
--- a/soroban-env-host/benches/common/cost_types/val_ser.rs
+++ b/soroban-env-host/benches/common/cost_types/val_ser.rs
@@ -8,8 +8,6 @@ pub(crate) struct ValSerMeasure;
 impl HostCostMeasurement for ValSerMeasure {
     type Runner = ValSerRun;
 
-    const STEP_SIZE: u64 = 1000;
-
     fn new_random_case(_host: &Host, rng: &mut StdRng, input: u64) -> (ScVal, Vec<u8>) {
         let len = 1 + input * Self::STEP_SIZE;
         let mut buf = vec![0; len as usize];
diff --git a/soroban-env-host/benches/common/cost_types/vec_ops.rs b/soroban-env-host/benches/common/cost_types/vec_ops.rs
index 0d2fc47d..a4f71531 100644
--- a/soroban-env-host/benches/common/cost_types/vec_ops.rs
+++ b/soroban-env-host/benches/common/cost_types/vec_ops.rs
@@ -12,8 +12,6 @@ pub(crate) struct VecEntryMeasure;
 impl HostCostMeasurement for VecEntryMeasure {
     type Runner = VecEntryRun;
 
-    const STEP_SIZE: u64 = 100;
-
     // Random case is worst case.
     fn new_random_case(_host: &Host, rng: &mut StdRng, input: u64) -> VecEntrySample {
         let input = 1 + input * Self::STEP_SIZE;
diff --git a/soroban-env-host/benches/common/cost_types/verify_ed25519_sig.rs b/soroban-env-host/benches/common/cost_types/verify_ed25519_sig.rs
index 61e74b70..49cbffe5 100644
--- a/soroban-env-host/benches/common/cost_types/verify_ed25519_sig.rs
+++ b/soroban-env-host/benches/common/cost_types/verify_ed25519_sig.rs
@@ -14,8 +14,6 @@ pub(crate) struct VerifyEd25519SigMeasure;
 impl HostCostMeasurement for VerifyEd25519SigMeasure {
     type Runner = VerifyEd25519SigRun;
 
-    const STEP_SIZE: u64 = 1000;
-
     fn new_random_case(_host: &Host, rng: &mut StdRng, input: u64) -> VerifyEd25519SigSample {
         let size = 1 + input * Self::STEP_SIZE;
         let keypair: Keypair = Keypair::generate(rng);
diff --git a/soroban-env-host/benches/common/cost_types/visit_object.rs b/soroban-env-host/benches/common/cost_types/visit_object.rs
index 1e31f15b..d17718af 100644
--- a/soroban-env-host/benches/common/cost_types/visit_object.rs
+++ b/soroban-env-host/benches/common/cost_types/visit_object.rs
@@ -6,8 +6,6 @@ pub(crate) struct VisitObjectMeasure;
 impl HostCostMeasurement for VisitObjectMeasure {
     type Runner = VisitObjectRun;
 
-    const STEP_SIZE: u64 = 100;
-
     fn new_random_case(host: &Host, _rng: &mut rand::prelude::StdRng, input: u64) -> Vec<Object> {
         // During setup we inject a bunch of copies of the object to make
         // the host object array large.
diff --git a/soroban-env-host/benches/common/measure.rs b/soroban-env-host/benches/common/measure.rs
index 2651968d..347bf626 100644
--- a/soroban-env-host/benches/common/measure.rs
+++ b/soroban-env-host/benches/common/measure.rs
@@ -1,6 +1,10 @@
 use rand::{rngs::StdRng, Rng, SeedableRng};
 use soroban_bench_utils::{tracking_allocator::AllocationGroupToken, HostTracker};
-use soroban_env_host::{budget::AsBudget, cost_runner::CostRunner, Host};
+use soroban_env_host::{
+    budget::{AsBudget, COST_MODEL_LIN_TERM_SCALE_BITS},
+    cost_runner::CostRunner,
+    Host,
+};
 use std::ops::Range;
 use tabwriter::{Alignment, TabWriter};
 
@@ -147,8 +151,14 @@ impl Measurements {
         let (x, y): (Vec<_>, Vec<_>) = self
             .averaged_net_measurements
             .iter()
-            .skip(1) // for some reason first data point is often flakey, skip it
-            .map(|m| (m.inputs.unwrap_or(0), m.cpu_insns))
+            .map(|m| {
+                (
+                    // we've made sure the raw inputs have been conflated before via HCM::STEP_SIZE,
+                    // here we can safely scale it back
+                    m.inputs.unwrap_or(0) >> COST_MODEL_LIN_TERM_SCALE_BITS,
+                    m.cpu_insns,
+                )
+            })
             .unzip();
 
         fit_model(x, y)
@@ -164,8 +174,14 @@ impl Measurements {
         let (x, y): (Vec<_>, Vec<_>) = self
             .averaged_net_measurements
             .iter()
-            .skip(1) // for some reason first data point is often flakey, skip it
-            .map(|m| (m.inputs.unwrap_or(0), m.mem_bytes))
+            .map(|m| {
+                (
+                    // we've made sure the raw inputs have been conflated before via HCM::STEP_SIZE,
+                    // here we can safely scale it back
+                    m.inputs.unwrap_or(0) >> COST_MODEL_LIN_TERM_SCALE_BITS,
+                    m.mem_bytes,
+                )
+            })
             .unzip();
 
         fit_model(x, y)
@@ -206,10 +222,15 @@ pub trait HostCostMeasurement: Sized {
     /// The type of host runner we're using. Uniquely identifies a `CostType`.
     type Runner: CostRunner;
 
-    /// Step size of the measurement to scale the input by. For a short-running measurement,
-    /// setting this constant accumulates multiple runs into a single sample point, thus helps
-    /// distingushing the trend from measurement noise.
-    const STEP_SIZE: u64 = 1000;
+    /// The `input: u64` will be multiplied by the `STEP_SIZE` for two reasons:
+    /// 1. for fast-running linear components, setting the step size larger can
+    /// ensure each sample runs for longer (compared to measurement fluctuation),
+    /// thus helps extrapolating the linear coefficient.
+    /// 2. when fitting the linear model, the linear coefficient will be scaled
+    /// up by `factor = 2^COST_MODEL_LIN_TERM_SCALE_BITS`, by scaling down the
+    /// actual input size. Thus `STEP_SIZE` must be `>= factor` to account for
+    /// the input downscaling.
+    const STEP_SIZE: u64 = 1024;
 
     /// Initialize a new instance of a HostMeasurement at a given input _hint_, for
     /// the run; the HostMeasurement can choose a precise input for a given hint
diff --git a/soroban-env-host/benches/common/modelfit.rs b/soroban-env-host/benches/common/modelfit.rs
index 0c9f77c0..e772c3c3 100644
--- a/soroban-env-host/benches/common/modelfit.rs
+++ b/soroban-env-host/benches/common/modelfit.rs
@@ -1,6 +1,6 @@
-use std::collections::HashSet;
-
 use log::info;
+use std::collections::HashSet;
+use std::str::FromStr;
 
 use linregress::{FormulaRegressionBuilder, RegressionDataBuilder};
 
@@ -33,9 +33,14 @@ impl FPCostModel {
     }
     // Extract the parameters from FPs to integers
     pub fn params_as_u64(&self) -> (u64, u64) {
+        let extract_param = |f: f64| -> u64 {
+            // clamp the float to 1 digit (to filter noise) then take the ceil
+            let f = f64::from_str(format!("{:.1}", f).as_str()).unwrap();
+            f.ceil() as u64
+        };
         (
-            self.const_param.max(0.0).round() as u64,
-            self.lin_param.max(0.0).round() as u64,
+            extract_param(self.const_param),
+            extract_param(self.lin_param),
         )
     }
 }
diff --git a/soroban-env-host/benches/worst_case_linear_models.rs b/soroban-env-host/benches/worst_case_linear_models.rs
index 4bea80eb..70b4b29c 100644
--- a/soroban-env-host/benches/worst_case_linear_models.rs
+++ b/soroban-env-host/benches/worst_case_linear_models.rs
@@ -11,7 +11,7 @@ use tabwriter::{Alignment, TabWriter};
 struct WorstCaseLinearModels;
 impl Benchmark for WorstCaseLinearModels {
     fn bench<HCM: HostCostMeasurement>() -> std::io::Result<(FPCostModel, FPCostModel)> {
-        let mut measurements = measure_worst_case_costs::<HCM>(0..20)?;
+        let mut measurements = measure_worst_case_costs::<HCM>(1..20)?;
 
         measurements.preprocess();
         measurements.report_table();
diff --git a/soroban-env-host/src/auth.rs b/soroban-env-host/src/auth.rs
index 16b4cfd7..217cb6a7 100644
--- a/soroban-env-host/src/auth.rs
+++ b/soroban-env-host/src/auth.rs
@@ -13,7 +13,7 @@ use soroban_env_common::{AddressObject, Compare, Symbol, TryFromVal, TryIntoVal,
 
 use crate::budget::{AsBudget, Budget};
 use crate::host::error::TryBorrowOrErr;
-use crate::host::metered_clone::{MeteredClone, MeteredCollect, MeteredContainer};
+use crate::host::metered_clone::{MeteredClone, MeteredContainer, MeteredIterator};
 use crate::host::Frame;
 use crate::host_object::HostVec;
 use crate::native_contract::account_contract::{
@@ -569,7 +569,7 @@ impl AuthorizationManager {
         host: &Host,
         auth_entries: Vec<SorobanAuthorizationEntry>,
     ) -> Result<Self, HostError> {
-        Vec::<AccountAuthorizationTracker>::charge_bulk_init(
+        Vec::<AccountAuthorizationTracker>::charge_bulk_init_cpy(
             auth_entries.len() as u64,
             host.as_budget(),
         )?;
@@ -653,7 +653,7 @@ impl AuthorizationManager {
         let auth_entries =
             host.visit_obj(auth_entries, |e: &HostVec| e.to_vec(host.budget_ref()))?;
         let mut trackers = self.try_borrow_invoker_contract_trackers_mut(host)?;
-        Vec::<Val>::charge_bulk_init(auth_entries.len() as u64, host.as_budget())?;
+        Vec::<Val>::charge_bulk_init_cpy(auth_entries.len() as u64, host.as_budget())?;
         trackers.reserve(auth_entries.len());
         for e in auth_entries {
             trackers.push(InvokerContractAuthorizationTracker::new(host, e)?)
@@ -852,7 +852,7 @@ impl AuthorizationManager {
             AuthorizationMode::Enforcing => {
                 let len = self.try_borrow_account_trackers(host)?.len();
                 let mut snapshots = Vec::with_capacity(len);
-                Vec::<Option<AccountAuthorizationTrackerSnapshot>>::charge_bulk_init(
+                Vec::<Option<AccountAuthorizationTrackerSnapshot>>::charge_bulk_init_cpy(
                     len as u64,
                     host.as_budget(),
                 )?;
@@ -989,7 +989,7 @@ impl AuthorizationManager {
         host: &Host,
         args: CreateContractArgs,
     ) -> Result<(), HostError> {
-        Vec::<CreateContractArgs>::charge_bulk_init(1, host.as_budget())?;
+        Vec::<CreateContractArgs>::charge_bulk_init_cpy(1, host.as_budget())?;
         self.try_borrow_call_stack_mut(host)?
             .push(AuthStackFrame::CreateContractHostFn(args));
         self.push_tracker_frame(host)
@@ -1016,7 +1016,7 @@ impl AuthorizationManager {
         };
         // Currently only contracts might appear in the call stack.
         let contract_address = host.add_host_object(ScAddress::Contract(contract_id))?;
-        Vec::<ContractInvocation>::charge_bulk_init(1, host.as_budget())?;
+        Vec::<ContractInvocation>::charge_bulk_init_cpy(1, host.as_budget())?;
         self.try_borrow_call_stack_mut(host)?
             .push(AuthStackFrame::Contract(ContractInvocation {
                 contract_address,
@@ -1205,7 +1205,7 @@ impl InvocationTracker {
 
     // metering: covered
     fn push_frame(&mut self, budget: &Budget) -> Result<(), HostError> {
-        Vec::<usize>::charge_bulk_init(1, budget)?;
+        Vec::<usize>::charge_bulk_init_cpy(1, budget)?;
         self.invocation_id_in_call_stack.push(None);
         Ok(())
     }
diff --git a/soroban-env-host/src/budget.rs b/soroban-env-host/src/budget.rs
index 3105450c..330507f0 100644
--- a/soroban-env-host/src/budget.rs
+++ b/soroban-env-host/src/budget.rs
@@ -15,6 +15,16 @@ use crate::{
 
 use wasmi::{errors, FuelCosts, ResourceLimiter};
 
+// These should match the default network config settings in core
+pub const DEFAULT_CPU_INSN_LIMIT: u64 = 100_000_000;
+pub const DEFAULT_MEM_BYTES_LIMIT: u64 = 100 * 1024 * 1024; // 100MB
+
+/// The number of bits to scale the linear term by. The linear coefficient has
+/// been scaled by this factor during parameter fitting to retain more significant
+/// digits. Thus to get the cost from the raw input, we need to scale the result
+/// back by the same factor.
+pub const COST_MODEL_LIN_TERM_SCALE_BITS: u32 = 7;
+
 /// We provide a "cost model" object that evaluates a linear expression:
 ///
 ///    f(x) = a + b * Option<x>
@@ -53,8 +63,9 @@ impl HostCostModel for ContractCostParamEntry {
         match input {
             Some(input) => {
                 let mut res = const_term;
-                if self.linear_term != 0 {
-                    res = res.saturating_add(lin_term.saturating_mul(input));
+                if lin_term != 0 {
+                    let lin_cost = lin_term.saturating_mul(input) >> COST_MODEL_LIN_TERM_SCALE_BITS;
+                    res = res.saturating_add(lin_cost);
                 }
                 Ok(res)
             }
@@ -305,31 +316,34 @@ impl BudgetImpl {
             // Define what inputs actually mean. For any constant-cost types -- whether it is a
             // true constant unit cost type, or empirically assigned (via measurement) constant
             // type -- we leave the input as `None`, otherwise, we initialize the input to 0.
+            let mut init_input = |i: usize| {
+                self.tracker[i].1 = Some(0);
+            };
             let i = ct as usize;
             match ct {
                 ContractCostType::WasmInsnExec => (),
                 ContractCostType::WasmMemAlloc => (),
-                ContractCostType::HostMemAlloc => self.tracker[i].1 = Some(0), // number of bytes in host memory to allocate
-                ContractCostType::HostMemCpy => self.tracker[i].1 = Some(0), // number of bytes in host to copy
-                ContractCostType::HostMemCmp => self.tracker[i].1 = Some(0), // number of bytes in host to compare
+                ContractCostType::HostMemAlloc => init_input(i), // number of bytes in host memory to allocate
+                ContractCostType::HostMemCpy => init_input(i),   // number of bytes in host to copy
+                ContractCostType::HostMemCmp => init_input(i), // number of bytes in host to compare
                 ContractCostType::InvokeHostFunction => (),
                 ContractCostType::VisitObject => (),
                 ContractCostType::ValXdrConv => (),
-                ContractCostType::ValSer => self.tracker[i].1 = Some(0), // number of bytes in the result buffer
-                ContractCostType::ValDeser => self.tracker[i].1 = Some(0), // number of bytes in the buffer
-                ContractCostType::ComputeSha256Hash => self.tracker[i].1 = Some(0), // number of bytes in the buffer
+                ContractCostType::ValSer => init_input(i), // number of bytes in the result buffer
+                ContractCostType::ValDeser => init_input(i), // number of bytes in the buffer
+                ContractCostType::ComputeSha256Hash => init_input(i), // number of bytes in the buffer
                 ContractCostType::ComputeEd25519PubKey => (),
                 ContractCostType::MapEntry => (),
                 ContractCostType::VecEntry => (),
                 ContractCostType::GuardFrame => (),
-                ContractCostType::VerifyEd25519Sig => self.tracker[i].1 = Some(0), // length of the signed message
-                ContractCostType::VmMemRead => self.tracker[i].1 = Some(0), // number of bytes in the linear memory to read
-                ContractCostType::VmMemWrite => self.tracker[i].1 = Some(0), // number of bytes in the linear memory to write
-                ContractCostType::VmInstantiation => self.tracker[i].1 = Some(0), // length of the wasm bytes,
-                ContractCostType::VmCachedInstantiation => self.tracker[i].1 = Some(0), // length of the wasm bytes,
+                ContractCostType::VerifyEd25519Sig => init_input(i), // length of the signed message
+                ContractCostType::VmMemRead => init_input(i), // number of bytes in the linear memory to read
+                ContractCostType::VmMemWrite => init_input(i), // number of bytes in the linear memory to write
+                ContractCostType::VmInstantiation => init_input(i), // length of the wasm bytes,
+                ContractCostType::VmCachedInstantiation => init_input(i), // length of the wasm bytes,
                 ContractCostType::InvokeVmFunction => (),
                 ContractCostType::ChargeBudget => (),
-                ContractCostType::ComputeKeccak256Hash => self.tracker[i].1 = Some(0), // number of bytes in the buffer
+                ContractCostType::ComputeKeccak256Hash => init_input(i), // number of bytes in the buffer
                 ContractCostType::ComputeEcdsaSecp256k1Key => (),
                 ContractCostType::ComputeEcdsaSecp256k1Sig => (),
                 ContractCostType::RecoverEcdsaSecp256k1Key => (),
@@ -341,6 +355,37 @@ impl BudgetImpl {
             }
         }
     }
+
+    pub fn charge(
+        &mut self,
+        ty: ContractCostType,
+        iterations: u64,
+        input: Option<u64>,
+    ) -> Result<(), HostError> {
+        if !self.enabled {
+            return Ok(());
+        }
+
+        // update tracker for reporting
+        let (t_iters, t_inputs) = &mut self.tracker[ty as usize];
+        *t_iters = t_iters.saturating_add(iterations);
+        match (t_inputs, input) {
+            (None, None) => (),
+            (Some(t), Some(i)) => *t = t.saturating_add(i.saturating_mul(iterations)),
+            // internal logic error, a wrong cost type has been passed in
+            _ => return Err((ScErrorType::Context, ScErrorCode::InternalError).into()),
+        };
+        // we already know `ChargeBudget` is constant cost, so here we just add 1 iteration.
+        let t_bi = &mut self.tracker[ContractCostType::ChargeBudget as usize].0;
+        *t_bi = t_bi.saturating_add(1);
+
+        // do the actual budget charging
+        // we already know `ChargeBudget` only affects the cpu budget
+        self.cpu_insns
+            .charge(ContractCostType::ChargeBudget, 1, None)?;
+        self.cpu_insns.charge(ty, iterations, input)?;
+        self.mem_bytes.charge(ty, iterations, input)
+    }
 }
 
 impl Debug for BudgetImpl {
@@ -543,43 +588,9 @@ impl Budget {
         iterations: u64,
         input: Option<u64>,
     ) -> Result<(), HostError> {
-        if !self.0.try_borrow_or_err()?.enabled {
-            return Ok(());
-        }
-
-        // NB: charging a cost-amount to the budgeting machinery itself seems to
-        // cost a similar amount as a single WASM instruction; so it's quite
-        // important to buffer WASM step counts before flushing to budgeting,
-        // and we add a constant charge here for "the cost of budget-counting"
-        // itself.
-
-        // update tracker for reporting
-        self.get_tracker_mut(ty, |(t_iters, t_inputs)| {
-            *t_iters = t_iters.saturating_add(iterations);
-            match (t_inputs, input) {
-                (None, None) => Ok(()),
-                (Some(t), Some(i)) => {
-                    *t = t.saturating_add(i.saturating_mul(iterations));
-                    Ok(())
-                }
-                // TODO: improve error code "internal error"
-                _ => Err((ScErrorType::Context, ScErrorCode::InternalError).into()),
-            }
-        })?;
-        self.get_tracker_mut(ContractCostType::ChargeBudget, |(t_iters, _)| {
-            // we already know `ChargeBudget` has undefined input, so here we just add 1 iteration.
-            *t_iters = t_iters.saturating_add(1);
-            Ok(())
-        })?;
-
-        // do the actual budget charging
-        self.mut_budget(|mut b| {
-            // we already know `ChargeBudget` only affects the cpu budget
-            b.cpu_insns
-                .charge(ContractCostType::ChargeBudget, 1, None)?;
-            b.cpu_insns.charge(ty, iterations, input)?;
-            b.mem_bytes.charge(ty, iterations, input)
-        })
+        self.0
+            .try_borrow_mut_or_err()?
+            .charge(ty, iterations, input)
     }
 
     /// Charges the budget under the specified [`CostType`]. The actual amount
@@ -588,7 +599,7 @@ impl Budget {
     /// Otherwise it is a linear model.  The caller needs to ensure the input
     /// passed is consistent with the inherent model underneath.
     pub fn charge(&self, ty: ContractCostType, input: Option<u64>) -> Result<(), HostError> {
-        self.bulk_charge(ty, 1, input)
+        self.0.try_borrow_mut_or_err()?.charge(ty, 1, input)
     }
 
     pub fn with_free_budget<F, T>(&self, f: F) -> Result<T, HostError>
@@ -615,6 +626,7 @@ impl Budget {
         Ok(self.0.try_borrow_or_err()?.tracker[ty as usize])
     }
 
+    #[cfg(any(test, feature = "testutils"))]
     pub(crate) fn get_tracker_mut<F>(&self, ty: ContractCostType, f: F) -> Result<(), HostError>
     where
         F: FnOnce(&mut (u64, Option<u64>)) -> Result<(), HostError>,
@@ -758,7 +770,7 @@ impl Default for BudgetImpl {
                 // instructions may cost additional amount of fuel based on
                 // wasmi's config setting.
                 ContractCostType::WasmInsnExec => {
-                    cpu.const_term = 7;
+                    cpu.const_term = 6;
                     cpu.linear_term = 0;
                 }
                 // Host cpu insns per wasm "memory fuel". This has to be zero since
@@ -771,43 +783,43 @@ impl Default for BudgetImpl {
                     cpu.linear_term = 0;
                 }
                 ContractCostType::HostMemAlloc => {
-                    cpu.const_term = 2350;
-                    cpu.linear_term = 0;
+                    cpu.const_term = 1131;
+                    cpu.linear_term = 1;
                 }
                 ContractCostType::HostMemCpy => {
-                    cpu.const_term = 23;
-                    cpu.linear_term = 0;
+                    cpu.const_term = 28;
+                    cpu.linear_term = 24;
                 }
                 ContractCostType::HostMemCmp => {
-                    cpu.const_term = 43;
-                    cpu.linear_term = 1;
+                    cpu.const_term = 24;
+                    cpu.linear_term = 64;
                 }
                 ContractCostType::InvokeHostFunction => {
-                    cpu.const_term = 928;
+                    cpu.const_term = 698;
                     cpu.linear_term = 0;
                 }
                 ContractCostType::VisitObject => {
-                    cpu.const_term = 19;
+                    cpu.const_term = 27;
                     cpu.linear_term = 0;
                 }
                 ContractCostType::ValXdrConv => {
-                    cpu.const_term = 134;
+                    cpu.const_term = 170;
                     cpu.linear_term = 0;
                 }
                 ContractCostType::ValSer => {
-                    cpu.const_term = 587;
-                    cpu.linear_term = 1;
+                    cpu.const_term = 607;
+                    cpu.linear_term = 68;
                 }
                 ContractCostType::ValDeser => {
-                    cpu.const_term = 870;
-                    cpu.linear_term = 0;
+                    cpu.const_term = 1233;
+                    cpu.linear_term = 33;
                 }
                 ContractCostType::ComputeSha256Hash => {
-                    cpu.const_term = 1725;
-                    cpu.linear_term = 33;
+                    cpu.const_term = 2391;
+                    cpu.linear_term = 4150;
                 }
                 ContractCostType::ComputeEd25519PubKey => {
-                    cpu.const_term = 25551;
+                    cpu.const_term = 25609;
                     cpu.linear_term = 0;
                 }
                 ContractCostType::MapEntry => {
@@ -818,72 +830,73 @@ impl Default for BudgetImpl {
                     cpu.const_term = 5;
                     cpu.linear_term = 0;
                 }
+                // To be removed
                 ContractCostType::GuardFrame => {
                     cpu.const_term = 4050;
                     cpu.linear_term = 0;
                 }
                 ContractCostType::VerifyEd25519Sig => {
-                    cpu.const_term = 369634;
-                    cpu.linear_term = 21;
+                    cpu.const_term = 376859;
+                    cpu.linear_term = 2744;
                 }
                 ContractCostType::VmMemRead => {
-                    cpu.const_term = 0;
-                    cpu.linear_term = 0;
+                    cpu.const_term = 138;
+                    cpu.linear_term = 24;
                 }
                 ContractCostType::VmMemWrite => {
-                    cpu.const_term = 124;
-                    cpu.linear_term = 0;
+                    cpu.const_term = 140;
+                    cpu.linear_term = 24;
                 }
                 ContractCostType::VmInstantiation => {
-                    cpu.const_term = 600447;
-                    cpu.linear_term = 484;
+                    cpu.const_term = 992415;
+                    cpu.linear_term = 68905;
                 }
                 ContractCostType::VmCachedInstantiation => {
-                    cpu.const_term = 600447;
-                    cpu.linear_term = 484;
+                    cpu.const_term = 992415;
+                    cpu.linear_term = 68905;
                 }
                 ContractCostType::InvokeVmFunction => {
-                    cpu.const_term = 5926;
+                    cpu.const_term = 1200;
                     cpu.linear_term = 0;
                 }
                 ContractCostType::ChargeBudget => {
-                    cpu.const_term = 130;
+                    cpu.const_term = 104;
                     cpu.linear_term = 0;
                 }
                 ContractCostType::ComputeKeccak256Hash => {
-                    cpu.const_term = 3322;
-                    cpu.linear_term = 46;
+                    cpu.const_term = 2886;
+                    cpu.linear_term = 3561;
                 }
                 ContractCostType::ComputeEcdsaSecp256k1Key => {
-                    cpu.const_term = 56525;
+                    cpu.const_term = 38418;
                     cpu.linear_term = 0;
                 }
                 ContractCostType::ComputeEcdsaSecp256k1Sig => {
-                    cpu.const_term = 250;
+                    cpu.const_term = 243;
                     cpu.linear_term = 0;
                 }
                 ContractCostType::RecoverEcdsaSecp256k1Key => {
-                    cpu.const_term = 2319640;
+                    cpu.const_term = 1666400;
                     cpu.linear_term = 0;
                 }
                 ContractCostType::Int256AddSub => {
-                    cpu.const_term = 735;
+                    cpu.const_term = 1959;
                     cpu.linear_term = 0;
                 }
                 ContractCostType::Int256Mul => {
-                    cpu.const_term = 1224;
+                    cpu.const_term = 2473;
                     cpu.linear_term = 0;
                 }
                 ContractCostType::Int256Div => {
-                    cpu.const_term = 1347;
+                    cpu.const_term = 2614;
                     cpu.linear_term = 0;
                 }
                 ContractCostType::Int256Pow => {
-                    cpu.const_term = 5350;
+                    cpu.const_term = 5215;
                     cpu.linear_term = 0;
                 }
                 ContractCostType::Int256Shift => {
-                    cpu.const_term = 538;
+                    cpu.const_term = 384;
                     cpu.linear_term = 0;
                 }
             }
@@ -904,8 +917,8 @@ impl Default for BudgetImpl {
                     mem.linear_term = 0;
                 }
                 ContractCostType::HostMemAlloc => {
-                    mem.const_term = 8;
-                    mem.linear_term = 1;
+                    mem.const_term = 16;
+                    mem.linear_term = 128;
                 }
                 ContractCostType::HostMemCpy => {
                     mem.const_term = 0;
@@ -916,7 +929,7 @@ impl Default for BudgetImpl {
                     mem.linear_term = 0;
                 }
                 ContractCostType::InvokeHostFunction => {
-                    mem.const_term = 0;
+                    mem.const_term = 1;
                     mem.linear_term = 0;
                 }
                 ContractCostType::VisitObject => {
@@ -928,12 +941,12 @@ impl Default for BudgetImpl {
                     mem.linear_term = 0;
                 }
                 ContractCostType::ValSer => {
-                    mem.const_term = 9;
-                    mem.linear_term = 3;
+                    mem.const_term = 18;
+                    mem.linear_term = 384;
                 }
                 ContractCostType::ValDeser => {
-                    mem.const_term = 4;
-                    mem.linear_term = 1;
+                    mem.const_term = 16;
+                    mem.linear_term = 128;
                 }
                 ContractCostType::ComputeSha256Hash => {
                     mem.const_term = 40;
@@ -968,15 +981,15 @@ impl Default for BudgetImpl {
                     mem.linear_term = 0;
                 }
                 ContractCostType::VmInstantiation => {
-                    mem.const_term = 117871;
-                    mem.linear_term = 40;
+                    mem.const_term = 131031;
+                    mem.linear_term = 5080;
                 }
                 ContractCostType::VmCachedInstantiation => {
-                    mem.const_term = 117871;
-                    mem.linear_term = 40;
+                    mem.const_term = 131031;
+                    mem.linear_term = 5080;
                 }
                 ContractCostType::InvokeVmFunction => {
-                    mem.const_term = 486;
+                    mem.const_term = 14;
                     mem.linear_term = 0;
                 }
                 ContractCostType::ChargeBudget => {
@@ -996,7 +1009,7 @@ impl Default for BudgetImpl {
                     mem.linear_term = 0;
                 }
                 ContractCostType::RecoverEcdsaSecp256k1Key => {
-                    mem.const_term = 181;
+                    mem.const_term = 201;
                     mem.linear_term = 0;
                 }
                 ContractCostType::Int256AddSub => {
@@ -1024,19 +1037,9 @@ impl Default for BudgetImpl {
             b.init_tracker();
         }
 
-        // For the time being we don't have "on chain" cost models
-        // so we just set some up here that we calibrated manually
-        // in the adjacent benchmarks.
-        //
-        // We don't run for a time unit thought, we run for an estimated
-        // (calibrated) number of CPU instructions.
-        //
-        // Assuming 2ghz chips at 2 instructions per cycle, we can guess about
-        // 4bn instructions / sec. So about 4000 instructions per usec, or 400k
-        // instructions in a 100usec time budget, or about 5479 wasm instructions
-        // using the calibration above (73 CPU insns per wasm insn). Very roughly!
-        b.cpu_insns.reset(40_000_000); // 100x the estimation above which corresponds to 10ms
-        b.mem_bytes.reset(0x320_0000); // 50MB of memory
+        // define the limits
+        b.cpu_insns.reset(DEFAULT_CPU_INSN_LIMIT);
+        b.mem_bytes.reset(DEFAULT_MEM_BYTES_LIMIT);
         b
     }
 }
diff --git a/soroban-env-host/src/events/internal.rs b/soroban-env-host/src/events/internal.rs
index 65da27b2..9cb82153 100644
--- a/soroban-env-host/src/events/internal.rs
+++ b/soroban-env-host/src/events/internal.rs
@@ -136,7 +136,7 @@ impl InternalEventsBuffer {
         // the cost needs to be amortized and buffer size-independent.
 
         if let InternalEvent::Contract(_) = e {
-            Vec::<(InternalEvent, EventError)>::charge_bulk_init(1, budget)?;
+            Vec::<(InternalEvent, EventError)>::charge_bulk_init_cpy(1, budget)?;
         }
         self.vec.push((e, EventError::FromSuccessfulCall));
         Ok(())
@@ -179,7 +179,7 @@ impl InternalEventsBuffer {
                     // the resulting buffer length may be different on different instances
                     // (due to diagnostic events) and we need a deterministic cost across all
                     // instances, the cost needs to be amortized and buffer size-independent.
-                    Vec::<HostEvent>::charge_bulk_init(1, host.as_budget())?;
+                    Vec::<HostEvent>::charge_bulk_init_cpy(1, host.as_budget())?;
                     Ok(HostEvent {
                         event: c.to_xdr(host)?,
                         failed_call: e.1 == EventError::FromFailedCall,
diff --git a/soroban-env-host/src/host.rs b/soroban-env-host/src/host.rs
index 2773c4da..c40fc473 100644
--- a/soroban-env-host/src/host.rs
+++ b/soroban-env-host/src/host.rs
@@ -1044,7 +1044,7 @@ impl EnvBase for Host {
     }
 
     fn map_new_from_slices(&self, keys: &[&str], vals: &[Val]) -> Result<MapObject, HostError> {
-        Vec::<Symbol>::charge_bulk_init(keys.len() as u64, self.as_budget())?;
+        Vec::<Symbol>::charge_bulk_init_cpy(keys.len() as u64, self.as_budget())?;
         // If only fallible iterators worked better in Rust, we would not need this Vec<...>.
         let mut key_syms: Vec<Symbol> = Vec::with_capacity(keys.len());
         for k in keys.iter() {
@@ -1683,7 +1683,7 @@ impl VmCallerEnv for Host {
             pos: keys_pos,
             len,
         } = self.decode_vmslice(keys_pos, len)?;
-        Vec::<Symbol>::charge_bulk_init(len as u64, self.as_budget())?;
+        Vec::<Symbol>::charge_bulk_init_cpy(len as u64, self.as_budget())?;
         let mut key_syms: Vec<Symbol> = Vec::with_capacity(len as usize);
         self.metered_vm_scan_slices_in_linear_memory(
             vmcaller,
@@ -1701,7 +1701,7 @@ impl VmCallerEnv for Host {
 
         // Step 2: extract all val Vals.
         let vals_pos: u32 = vals_pos.into();
-        Vec::<Val>::charge_bulk_init(len as u64, self.as_budget())?;
+        Vec::<Val>::charge_bulk_init_cpy(len as u64, self.as_budget())?;
         let mut vals: Vec<Val> = vec![Val::VOID.into(); len as usize];
         self.metered_vm_read_vals_from_linear_memory::<8, Val>(
             vmcaller,
@@ -1992,7 +1992,7 @@ impl VmCallerEnv for Host {
         len: U32Val,
     ) -> Result<VecObject, HostError> {
         let VmSlice { vm, pos, len } = self.decode_vmslice(vals_pos, len)?;
-        Vec::<Val>::charge_bulk_init(len as u64, self.as_budget())?;
+        Vec::<Val>::charge_bulk_init_cpy(len as u64, self.as_budget())?;
         let mut vals: Vec<Val> = vec![Val::VOID.to_val(); len as usize];
         self.metered_vm_read_vals_from_linear_memory::<8, Val>(
             vmcaller,
diff --git a/soroban-env-host/src/host/conversion.rs b/soroban-env-host/src/host/conversion.rs
index baf0f99c..2e83834e 100644
--- a/soroban-env-host/src/host/conversion.rs
+++ b/soroban-env-host/src/host/conversion.rs
@@ -1,6 +1,6 @@
 use std::rc::Rc;
 
-use super::metered_clone::{self, MeteredClone, MeteredCollect, MeteredContainer};
+use super::metered_clone::{self, MeteredClone, MeteredContainer, MeteredIterator};
 use crate::budget::AsBudget;
 use crate::err;
 use crate::host_object::{HostMap, HostObject, HostVec};
@@ -301,7 +301,7 @@ impl Host {
     }
 
     pub(crate) fn metered_slice_to_vec(&self, s: &[u8]) -> Result<Vec<u8>, HostError> {
-        Vec::<u8>::charge_bulk_init(s.len() as u64, self.as_budget())?;
+        Vec::<u8>::charge_bulk_init_cpy(s.len() as u64, self.as_budget())?;
         Ok(s.to_vec())
     }
 
diff --git a/soroban-env-host/src/host/frame.rs b/soroban-env-host/src/host/frame.rs
index de2bea6e..eddadb97 100644
--- a/soroban-env-host/src/host/frame.rs
+++ b/soroban-env-host/src/host/frame.rs
@@ -133,7 +133,7 @@ impl Host {
             prng: None,
             storage: None,
         };
-        Vec::<Context>::charge_bulk_init(1, self.as_budget())?;
+        Vec::<Context>::charge_bulk_init_cpy(1, self.as_budget())?;
         self.try_borrow_context_mut()?.push(ctx);
         Ok(RollbackPoint {
             storage: self.try_borrow_storage()?.map.clone(),
diff --git a/soroban-env-host/src/host/metered_clone.rs b/soroban-env-host/src/host/metered_clone.rs
index 5e631c2b..b54d4bf0 100644
--- a/soroban-env-host/src/host/metered_clone.rs
+++ b/soroban-env-host/src/host/metered_clone.rs
@@ -76,14 +76,15 @@ pub(crate) fn charge_heap_alloc<T: DeclaredSizeForMetering>(
 }
 
 /// Represents a collection type which can be created from an iterator, and provides
-/// a method for bulk charging its initalization cost.
+/// a method for bulk charging its creation cost.
 pub trait MeteredContainer: FromIterator<Self::Item> + DeclaredSizeForMetering
 where
     Self: Sized,
 {
     type Item: DeclaredSizeForMetering;
-
-    fn charge_bulk_init(len: u64, budget: &Budget) -> Result<(), HostError> {
+    /// Charges the cost for bulk initializing the container and copying `len` elements
+    /// into it.
+    fn charge_bulk_init_cpy(len: u64, budget: &Budget) -> Result<(), HostError> {
         charge_shallow_copy::<Self>(1, budget)?;
         charge_heap_alloc::<Self::Item>(len, budget)?;
         charge_shallow_copy::<Self::Item>(len, budget)
@@ -106,7 +107,7 @@ impl<T: DeclaredSizeForMetering, E: DeclaredSizeForMetering> MeteredContainer
 /// Returns:
 ///   Err - if either the budget was exceeded or the iterator lacks the size hint
 ///   Ok - otherwise
-pub trait MeteredCollect: Iterator
+pub trait MeteredIterator: Iterator
 where
     Self::Item: DeclaredSizeForMetering,
     Self: Sized,
@@ -116,7 +117,7 @@ where
         budget: &Budget,
     ) -> Result<B, HostError> {
         if let (_, Some(ub)) = self.size_hint() {
-            B::charge_bulk_init(ub as u64, budget)?;
+            B::charge_bulk_init_cpy(ub as u64, budget)?;
             Ok(self.collect())
         } else {
             Err((ScErrorType::Object, ScErrorCode::InternalError).into())
@@ -124,7 +125,7 @@ where
     }
 }
 
-impl<T: DeclaredSizeForMetering, I: Iterator, F> MeteredCollect for std::iter::Map<I, F> where
+impl<T: DeclaredSizeForMetering, I: Iterator, F> MeteredIterator for std::iter::Map<I, F> where
     F: FnMut(I::Item) -> T
 {
 }
diff --git a/soroban-env-host/src/native_contract/invoker_contract_auth.rs b/soroban-env-host/src/native_contract/invoker_contract_auth.rs
index 434b7353..4a7d5fdf 100644
--- a/soroban-env-host/src/native_contract/invoker_contract_auth.rs
+++ b/soroban-env-host/src/native_contract/invoker_contract_auth.rs
@@ -54,7 +54,7 @@ impl InvokerContractAuthEntry {
                 host.visit_obj(
                     contract_invocation.sub_invocations.as_object(),
                     |v: &HostVec| {
-                        Vec::<AuthorizedInvocation>::charge_bulk_init(
+                        Vec::<AuthorizedInvocation>::charge_bulk_init_cpy(
                             v.len() as u64,
                             host.as_budget(),
                         )?;


Test patch:
diff --git a/soroban-env-host/src/e2e_invoke.rs b/soroban-env-host/src/e2e_invoke.rs
index deec9e12..75fa917b 100644
--- a/soroban-env-host/src/e2e_invoke.rs
+++ b/soroban-env-host/src/e2e_invoke.rs
@@ -450,7 +450,7 @@ impl Host {
         &self,
         encoded_contract_auth_entries: I,
     ) -> Result<Vec<SorobanAuthorizationEntry>, HostError> {
-        Vec::<SorobanAuthorizationEntry>::charge_bulk_init(
+        Vec::<SorobanAuthorizationEntry>::charge_bulk_init_cpy(
             encoded_contract_auth_entries.len() as u64,
             self.as_budget(),
         )?;
diff --git a/soroban-env-host/src/test/budget_metering.rs b/soroban-env-host/src/test/budget_metering.rs
index 66bf19bb..81d94a33 100644
--- a/soroban-env-host/src/test/budget_metering.rs
+++ b/soroban-env-host/src/test/budget_metering.rs
@@ -1,6 +1,6 @@
 use crate::{
     budget::{AsBudget, Budget},
-    host::metered_clone::{MeteredClone, MeteredCollect},
+    host::metered_clone::{MeteredClone, MeteredIterator},
     host::metered_xdr::metered_write_xdr,
     xdr::{ContractCostType, ScMap, ScMapEntry, ScVal},
     Env, Host, HostError, Symbol, Val,
@@ -282,41 +282,41 @@ fn total_amount_charged_from_random_inputs() -> Result<(), HostError> {
     let actual = format!("{:?}", host.as_budget());
     expect![[r#"
         =====================================================================================================================================================================
-        Cpu limit: 40000000; used: 11026555
-        Mem limit: 52428800; used: 343551
+        Cpu limit: 100000000; used: 10914026
+        Mem limit: 104857600; used: 347820
         =====================================================================================================================================================================
         CostType                 iterations     input          cpu_insns      mem_bytes      const_term_cpu      lin_term_cpu        const_term_mem      lin_term_mem        
-        WasmInsnExec             246            None           1722           0              7                   0                   0                   0                   
+        WasmInsnExec             246            None           1476           0              6                   0                   0                   0                   
         WasmMemAlloc             184            None           0              184            0                   0                   1                   0                   
-        HostMemAlloc             1              Some(152)      2350           160            2350                0                   8                   1                   
-        HostMemCpy               1              Some(65)       23             0              23                  0                   0                   0                   
-        HostMemCmp               1              Some(74)       117            0              43                  1                   0                   0                   
-        InvokeHostFunction       176            None           163328         0              928                 0                   0                   0                   
-        VisitObject              97             None           1843           0              19                  0                   0                   0                   
-        ValXdrConv               148            None           19832          0              134                 0                   0                   0                   
-        ValSer                   1              Some(49)       636            156            587                 1                   9                   3                   
-        ValDeser                 1              Some(103)      870            107            870                 0                   4                   1                   
-        ComputeSha256Hash        1              Some(193)      8094           40             1725                33                  40                  0                   
-        ComputeEd25519PubKey     226            None           5774526        0              25551               0                   0                   0                   
+        HostMemAlloc             1              Some(152)      1132           168            1131                1                   16                  128                 
+        HostMemCpy               1              Some(65)       40             0              28                  24                  0                   0                   
+        HostMemCmp               1              Some(74)       61             0              24                  64                  0                   0                   
+        InvokeHostFunction       176            None           122848         176            698                 0                   1                   0                   
+        VisitObject              97             None           2619           0              27                  0                   0                   0                   
+        ValXdrConv               148            None           25160          0              170                 0                   0                   0                   
+        ValSer                   1              Some(49)       633            165            607                 68                  18                  384                 
+        ValDeser                 1              Some(103)      1259           119            1233                33                  16                  128                 
+        ComputeSha256Hash        1              Some(193)      8648           40             2391                4150                40                  0                   
+        ComputeEd25519PubKey     226            None           5787634        0              25609               0                   0                   0                   
         MapEntry                 250            None           13250          0              53                  0                   0                   0                   
         VecEntry                 186            None           930            0              5                   0                   0                   0                   
         GuardFrame               152            None           615600         71744          4050                0                   472                 0                   
-        VerifyEd25519Sig         1              Some(227)      374401         0              369634              21                  0                   0                   
-        VmMemRead                1              Some(69)       0              0              0                   0                   0                   0                   
-        VmMemWrite               1              Some(160)      124            0              124                 0                   0                   0                   
-        VmInstantiation          1              Some(147)      671595         123751         600447              484                 117871              40                  
-        VmCachedInstantiation    1              Some(147)      671595         123751         600447              484                 117871              40                  
-        InvokeVmFunction         47             None           278522         22842          5926                0                   486                 0                   
-        ChargeBudget             294            None           38220          0              130                 0                   0                   0                   
-        ComputeKeccak256Hash     1              Some(1)        3368           40             3322                46                  40                  0                   
-        ComputeEcdsaSecp256k1Key 1              None           56525          0              56525               0                   0                   0                   
-        ComputeEcdsaSecp256k1Sig 1              None           250            0              250                 0                   0                   0                   
-        RecoverEcdsaSecp256k1Key 1              None           2319640        181            2319640             0                   181                 0                   
-        Int256AddSub             1              None           735            119            735                 0                   119                 0                   
-        Int256Mul                1              None           1224           119            1224                0                   119                 0                   
-        Int256Div                1              None           1347           119            1347                0                   119                 0                   
-        Int256Pow                1              None           5350           119            5350                0                   119                 0                   
-        Int256Shift              1              None           538            119            538                 0                   119                 0                   
+        VerifyEd25519Sig         1              Some(227)      381725         0              376859              2744                0                   0                   
+        VmMemRead                1              Some(69)       150            0              138                 24                  0                   0                   
+        VmMemWrite               1              Some(160)      170            0              140                 24                  0                   0                   
+        VmInstantiation          1              Some(147)      1071548        136865         992415              68905               131031              5080                
+        VmCachedInstantiation    1              Some(147)      1071548        136865         992415              68905               131031              5080                
+        InvokeVmFunction         47             None           56400          658            1200                0                   14                  0                   
+        ChargeBudget             294            None           30576          0              104                 0                   0                   0                   
+        ComputeKeccak256Hash     1              Some(1)        2913           40             2886                3561                40                  0                   
+        ComputeEcdsaSecp256k1Key 1              None           38418          0              38418               0                   0                   0                   
+        ComputeEcdsaSecp256k1Sig 1              None           243            0              243                 0                   0                   0                   
+        RecoverEcdsaSecp256k1Key 1              None           1666400        201            1666400             0                   201                 0                   
+        Int256AddSub             1              None           1959           119            1959                0                   119                 0                   
+        Int256Mul                1              None           2473           119            2473                0                   119                 0                   
+        Int256Div                1              None           2614           119            2614                0                   119                 0                   
+        Int256Pow                1              None           5215           119            5215                0                   119                 0                   
+        Int256Shift              1              None           384            119            384                 0                   119                 0                   
         =====================================================================================================================================================================
 
     "#]]
diff --git a/soroban-env-host/src/test/util.rs b/soroban-env-host/src/test/util.rs
index 135e6d3a..8301dca9 100644
--- a/soroban-env-host/src/test/util.rs
+++ b/soroban-env-host/src/test/util.rs
@@ -10,7 +10,7 @@ use soroban_env_common::{
 };
 
 use crate::{
-    budget::{AsBudget, Budget},
+    budget::{AsBudget, Budget, COST_MODEL_LIN_TERM_SCALE_BITS},
     host::error::TryBorrowOrErr,
     storage::{test_storage::MockSnapshotSource, Storage},
     xdr, Host, HostError,
@@ -97,7 +97,7 @@ impl Host {
                 .try_borrow_mut_or_err()?
                 .cpu_insns
                 .get_cost_model_mut(ty)
-                .linear_term = lin_cpu as i64;
+                .linear_term = (lin_cpu << COST_MODEL_LIN_TERM_SCALE_BITS) as i64;
 
             budget
                 .0
@@ -110,7 +110,7 @@ impl Host {
                 .try_borrow_mut_or_err()?
                 .mem_bytes
                 .get_cost_model_mut(ty)
-                .linear_term = lin_mem as i64;
+                .linear_term = (lin_mem << COST_MODEL_LIN_TERM_SCALE_BITS) as i64;
             Ok(())
         })
         .unwrap();