CostTracker: Add a getter to expose cost by writable accounts#7920
CostTracker: Add a getter to expose cost by writable accounts#7920tao-stones merged 4 commits intoanza-xyz:masterfrom
Conversation
This change adds a new method to the CostTracker that provides a breakdown of compute unit costs for each writable account during block processing.
Problem
Currently, CostTracker aggregates compute unit costs but doesn't expose a detailed breakdown of costs per account. This makes it difficult to analyze resource consumption patterns and identify which accounts are the biggest resource consumers within a block.
Solution
A new method, get_cost_by_writable_accounts(), has been added to the CostTracker to address this issue. This method returns a mapping of each writable account to its specific compute unit costs, providing a granular view of resource usage.
Testing
✅ The new method was tested to ensure it correctly tracks and returns per-account costs.
✅ Existing CostTracker functionality remains unchanged and unaffected by this addition.
✅ The change was validated to confirm it correctly captures the cost distribution among writable accounts.
Type of Change
[ ] Bug fix (non-breaking change which fixes an issue)
[x] New feature (non-breaking change which adds functionality)
[ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
[ ] Documentation update
Checklist
[x] Code compiles without errors
[x] New tests have been added and pass
[x] No breaking changes
[x] Follows existing code style
cost-model/src/cost_tracker.rs
Outdated
| } | ||
|
|
||
| /// Get a reference to the cost by writable accounts map | ||
| pub fn get_cost_by_writable_accounts(&self) -> &HashMap<Pubkey, u64, ahash::RandomState> { |
There was a problem hiding this comment.
The concern with this pub fn is others may start use it in validator, then accidentally increase CostTracker locking period. In validator code space, CostTracker is shared among worker threads, therefore is highly contended, especially in high tps environment. Would be great to prevent such accident.
There was a problem hiding this comment.
Few options I thought of:
- your project implements this functionality locally, so it does not exist in Agave repo;
- if it has to be in Agave, create a separate module with name screaming "only use in post-analyze to avoid lock contention", implement this for CostTracker with a dedicated trait (for example,
CostTrackerPostAnalysis). So only intended users will import and use it;
There was a problem hiding this comment.
could we hide method behind a feature that is disabled by default?
There was a problem hiding this comment.
sounds pretty hacky, but a possible solution. Is there an easy way to explain why #1 above not possible?
There was a problem hiding this comment.
if logic changes w/ a new feature gate, assume there's an API breakage which is easy to pick up in users of the software. could be logic change, parameter change, or something like SIMD-322
There was a problem hiding this comment.
Sorry, not sure if I'm following. my thought was to do this in your project so upstream isn't polluted:
// In your project cost_tracker_post_analysis.rs
use solana-cost-model::CostTracker;
pub trait CostTrackerPostAnalysis {
fn get_cost_by_writable_accounts(&self);
}
impl CostTrackerPostAnalysis for CostTracker { ... }
// in use side
use cost_tracker_post_analysis::CostTrackerPostAnalysis;
// use CostTrackerPostAnalysis()
future API changes wouldn't impact your project, but if that Hashmap goes away, you will know 😄
There was a problem hiding this comment.
impl CostTrackerPostAnalysis for CostTracker { ... }
This won't work for the CostTracker right as the hash map is private.
I've made some updates so that we have a feature gated trait that implements the getter. So, when the cargo crate is published, can use the feature. For use in agave code, this would require the feature + the trait inclusion, so easy to spot
|
Also, maybe related, I am running this branch to collect contended accounts details - similar info you are looking for from the HashMap. Maybe can be repurposed for your need (eg instead of report to metrics, do something differently for the same data)? |
tao-stones
left a comment
There was a problem hiding this comment.
It looks good to me. @apfitzge do you have opinions?
| use {solana_pubkey::Pubkey, std::collections::HashMap}; | ||
|
|
||
| /// Trait to help with post-analysis of a given block | ||
| pub trait CostTrackerPostAnalysis { |
There was a problem hiding this comment.
if we already have the feature, is there a purpose in having the trait?
There was a problem hiding this comment.
Requiring manually use trait to access that function is an additional safety for dev.
There was a problem hiding this comment.
I think it comes down to maintainability and making sure the get method is not used inadvertently.
The trait is kept behind a feature and the implementation as well.
So, if someone has to use it within agave code, the change would be very noticeable.
This should deter any such usage as it's not part of the vanilla struct and keeps it distinct.
There was a problem hiding this comment.
right but it's 2 things to do the same thing, prevent accidental usage. Just choose one I think
There was a problem hiding this comment.
Can keep only the trait, if the general recommendation is not to use the feature gate.
There was a problem hiding this comment.
i'll also vote of using trait. Ok without feature gate
There was a problem hiding this comment.
right but it's 2 things to do the same thing, prevent accidental usage. Just choose one I think
if we actually cared about safety here, we'd use the type system and transform to a type that specifies the new method only after we've left the "hot path" not pray that dev reads a comment in a different file
|
we've generally not added features, as it complicates compilation and testing. |
I guess I'm still not that convinced this is the right approach. If you're doing post-analysis of the block, why use the cost-tracker which is designed around checking if things can fit into the block. I'm not convinced re-using this is the best option for BAM. |
This reverts commit b927337.
apfitzge
left a comment
There was a problem hiding this comment.
Thanks for responding to feedback quickly!
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #7920 +/- ##
=======================================
Coverage 83.1% 83.1%
=======================================
Files 810 810
Lines 357419 357436 +17
=======================================
+ Hits 297211 297255 +44
+ Misses 60208 60181 -27 🚀 New features to boost your workflow:
|
|
Backports to the beta branch are to be avoided unless absolutely necessary for fixing bugs, security issues, and perf regressions. Changes intended for backport should be structured such that a minimum effective diff can be committed separately from any refactoring, plumbing, cleanup, etc that are not strictly necessary to achieve the goal. Any of the latter should go only into master and ride the normal stabilization schedule. Exceptions include CI/metrics changes, CLI improvements and documentation updates on a case by case basis. |
* Summary
This change adds a new method to the CostTracker that provides a breakdown of compute unit costs for each writable account during block processing.
Problem
Currently, CostTracker aggregates compute unit costs but doesn't expose a detailed breakdown of costs per account. This makes it difficult to analyze resource consumption patterns and identify which accounts are the biggest resource consumers within a block.
Solution
A new method, get_cost_by_writable_accounts(), has been added to the CostTracker to address this issue. This method returns a mapping of each writable account to its specific compute unit costs, providing a granular view of resource usage.
Testing
✅ The new method was tested to ensure it correctly tracks and returns per-account costs.
✅ Existing CostTracker functionality remains unchanged and unaffected by this addition.
✅ The change was validated to confirm it correctly captures the cost distribution among writable accounts.
Type of Change
[ ] Bug fix (non-breaking change which fixes an issue)
[x] New feature (non-breaking change which adds functionality)
[ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
[ ] Documentation update
Checklist
[x] Code compiles without errors
[x] New tests have been added and pass
[x] No breaking changes
[x] Follows existing code style
* Use a trait to expose the heavy method
* gate behind a feature
* Revert "gate behind a feature"
This reverts commit b927337.
(cherry picked from commit f4598b1)
…backport of #7920) (#8174) CostTracker: Add a getter to expose cost by writable accounts (#7920) * Summary This change adds a new method to the CostTracker that provides a breakdown of compute unit costs for each writable account during block processing. Problem Currently, CostTracker aggregates compute unit costs but doesn't expose a detailed breakdown of costs per account. This makes it difficult to analyze resource consumption patterns and identify which accounts are the biggest resource consumers within a block. Solution A new method, get_cost_by_writable_accounts(), has been added to the CostTracker to address this issue. This method returns a mapping of each writable account to its specific compute unit costs, providing a granular view of resource usage. Testing ✅ The new method was tested to ensure it correctly tracks and returns per-account costs. ✅ Existing CostTracker functionality remains unchanged and unaffected by this addition. ✅ The change was validated to confirm it correctly captures the cost distribution among writable accounts. Type of Change [ ] Bug fix (non-breaking change which fixes an issue) [x] New feature (non-breaking change which adds functionality) [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) [ ] Documentation update Checklist [x] Code compiles without errors [x] New tests have been added and pass [x] No breaking changes [x] Follows existing code style * Use a trait to expose the heavy method * gate behind a feature * Revert "gate behind a feature" This reverts commit b927337. (cherry picked from commit f4598b1) Co-authored-by: ebin-mathews <144146969+ebin-mathews@users.noreply.github.com>
Summary
This change adds a new method to the CostTracker that provides a breakdown of compute unit costs for each writable account during block processing.
Problem
Currently, CostTracker aggregates compute unit costs but doesn't expose a detailed breakdown of costs per account. This makes it difficult to analyze resource consumption patterns and identify which accounts are the biggest resource consumers within a block during post-analysis.
Proposed Changes
A new method, get_cost_by_writable_accounts(), has been added to the CostTracker which exposes a reference to the map that holds the detailed breakdown of costs per account.