add cu benchmarking - #67
Conversation
9c036fe to
694f57b
Compare
a7925ff to
ee38f6e
Compare
85c36c4 to
fa68b93
Compare
fa68b93 to
03545e4
Compare
There was a problem hiding this comment.
A few comments but it looks reasonable.
Come comments:
- The PR is fairly small, I think it's ok to already use the new function everywhere. Right now, I had to go and ask Claude to add this function everywhere. (It found interesting things, like: we possibly want to skip recording tests that revert.)
- What do you think about adding two extra fields,
accountsandbytes, that just print the number of accounts/bytes used by a transaction? It's fairly easy to add and it's relevant for benchmarks. - I'm in favor of checking in this file, so we always see the changes. In case, there should be CI for it. Doesn't need to be this PR though.
|
|
||
| fs::write( | ||
| path, | ||
| serde_json::to_string_pretty(&report).expect("CU report should serialize"), |
There was a problem hiding this comment.
I'd kind of prefer yaml or toml here for readability, but ultimately it doesn't matter.
There was a problem hiding this comment.
yaml tooling is a lot less ubiquitous and . for example, we use jq to format the json from jsonl, but the equivalent tool yq isn't commonly installed on workstations.
JSON can be very readable if its formatted pretty, so I would like to leave it as is.
First time I have ever been told to grow the size of a PR, progress 😆
Wanted to keep the PR scoped but yea it shouldn't be a big ask
Agreed! Right now there was nothing to check in. Will add the tests. |
# Description Make tests deterministic by replacing `Pubkey::new_unique()` or `Keypair::new()` with deterministic randomness from a seed. # Motivation Enables #67 because right now the random key generation can cause CU to shift randomly from test run to test run due to bump searching. As seen in #94 # Strategy Replace all usages of `Pubkey::new_unique()` or `Keypair::new()` with new `common` methods which draw from a random source from a seed, reset prior to every test in `setup()`. Utilize a `clippy.toml` rule to prevent usage of the non-deterministic functions in the integration tests only. It is also necessary to replace the `CreateMint` helper provided by `litesvm` because it uses those functions under the hood, and there doesn't seem to be away around. Here is what the clippy rule firing looks like: <img width="879" height="212" alt="image" src="https://github.com/user-attachments/assets/67180860-d310-4cb2-82c3-869b6833316b" /> # How to test Verify the test results. Run the tests multiple times to verify the CU is not changing from run to run in #67 . I did so manually by running `just test` a couple times and comparing the output `target/cu-report.json` against previous runs. If you would like, confirm that the clippy rule is applying as expected in the integration tests, and the clippy rule is *NOT* applied elsewhere (ex. in the program unit tests, which don't really need to be deterministic) --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: Federico Giacon <58218759+fedgiac@users.noreply.github.com>
701ebcc to
e290fc0
Compare
Co-authored-by: Federico Giacon <58218759+fedgiac@users.noreply.github.com>
now its much simpler
735eaaa to
9c7486b
Compare
fedgiac
left a comment
There was a problem hiding this comment.
Very nice, I like the result, and this is only 300 lines of code!
My only observation that could require some work would be to use the metered function everywhere in the tests where it doesn't revert. And I'm even considering using it for reverting transaction and add a flag in the label, like /r. No strong opinion here, I'll leave to you to which extent we should do it.
Otherwise, I only have small things in the comments.
| "accounts_readable": { | ||
| "finalizes_with_no_pushes/settle": 4, | ||
| "happy_path_creates_initialized_buffer_token_account/create_buffers": 4, | ||
| "happy_path_creates_multiple_buffers_in_one_instruction/create_buffers": 6, | ||
| "happy_path_creates_order_pda_with_expected_body/create_order": 2, | ||
| "happy_path_initializes_state_pda_with_expected_data/initialize": 2, | ||
| "happy_path_returns_lamports_and_closes_pda/reclaim_order": 1, | ||
| "max_buffers_in_one_instruction/create_buffers": 33, | ||
| "pulls_from_multiple_orders/settle": 4, | ||
| "pulls_funds_to_destination/settle": 4, | ||
| "pulls_to_multiple_destinations/settle": 4, | ||
| "pushes_a_single_order/settle": 4, | ||
| "pushes_several_orders_from_different_buffers/settle": 4, | ||
| "pushes_several_orders_from_one_buffer/settle": 4, | ||
| "settles_a_single_order/settle": 4, | ||
| "settles_multiple_orders/settle": 4 | ||
| }, | ||
| "accounts_writable": { | ||
| "finalizes_with_no_pushes/settle": 1, | ||
| "happy_path_creates_initialized_buffer_token_account/create_buffers": 2, | ||
| "happy_path_creates_multiple_buffers_in_one_instruction/create_buffers": 4, | ||
| "happy_path_creates_order_pda_with_expected_body/create_order": 2, | ||
| "happy_path_initializes_state_pda_with_expected_data/initialize": 2, | ||
| "happy_path_returns_lamports_and_closes_pda/reclaim_order": 3, | ||
| "max_buffers_in_one_instruction/create_buffers": 31, | ||
| "pulls_from_multiple_orders/settle": 11, | ||
| "pulls_funds_to_destination/settle": 6, | ||
| "pulls_to_multiple_destinations/settle": 7, | ||
| "pushes_a_single_order/settle": 5, | ||
| "pushes_several_orders_from_different_buffers/settle": 9, | ||
| "pushes_several_orders_from_one_buffer/settle": 8, | ||
| "settles_a_single_order/settle": 5, | ||
| "settles_multiple_orders/settle": 13 | ||
| }, |
There was a problem hiding this comment.
Since the benchmarks are intended to be human-readable, we could make an entry more information dense and do something like:
"max_buffers_in_one_instruction/create_buffers": "64 (31 w / 33 r)",
In general I suggest always using strings, so that if there's an isolated diff we know by the unit specified in the unit what this number refers to:
"max_buffers_in_one_instruction/create_buffers": "163418 CU",
[...]
"max_buffers_in_one_instruction/create_buffers": "1 b",
There was a problem hiding this comment.
I kept them as numbers without compressing so that it would be easier for scripts/other tooling that may come in the future to parse the file or changes. When reading the diff I think it should still be clear enough as the section names will be shown in most diffing tools, right?
There was a problem hiding this comment.
As discussed privately, it's fine to keep like this but have the sum instead of read/write separate.
There was a problem hiding this comment.
Co-authored-by: Federico Giacon <58218759+fedgiac@users.noreply.github.com>
adds bincode dependency
See comment here, I tested this out (even for reverting functions!), but I didn't really like it. I think what we have now is pretty ideal, actually. Its like proptest--doesn't need to go everywhere, but we can add benchmarking when we believe something to be important to watch. |
…ng-cu-consumption-benchmarks
turns out that solana limits by total accounts locked, whether the account is writable or readable is irrelevant
Description
Record the CU, account, and transaction size usage of integration tests through a wrapper function. Also hook up a good selection of existing tests to use the benchmarks.
Context
The benchmark results are written by a dedicated
just benchfunction. Thebench-report.jsonfile is checked into the repo so that when the benchmarking results change, they will be displayed along with the applicable changes in a highly readable format.The CI checks that a regenerated
bench-report.jsondoes not drift.Dependency Change
Adds
bincodeas a dev-dependency (programs/settlement/Cargo.toml), so it is only ever built for tests and never linked into the on-chain program. It was already in our dependency tree. The bincode package is one of the most popular package on Crates, and is officially used by solana. However, bincode package has been marked as unmaintained. The near-replacement suggested (wincode) causes conflicts/issues way outside of the scope of what should be done within this PR.How to test
Ensure the
just benchcommand works on your machine and doesn't present any changes in thebench-report.jsonfile. There is a possibility that differences in local tooling could affect the output.