-
Notifications
You must be signed in to change notification settings - Fork 374
tests(benchmark): add missing scenario and optimization #1768
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: forks/osaka
Are you sure you want to change the base?
tests(benchmark): add missing scenario and optimization #1768
Conversation
f9fdb17 to
01e4d6b
Compare
01e4d6b to
1b44dea
Compare
| ) | ||
|
|
||
|
|
||
| def test_pc_op( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We miss the benchmark for PC opcode
| @pytest.mark.parametrize("mem_alloc", [b"", b"ff", b"ff" * 32]) | ||
| @pytest.mark.parametrize("offset", [0, 31, 1024]) | ||
| def test_keccak( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of finding the optimal length, add a new case that parametrized initial memory layout and access offset. Request by gas repricing effort.
| ], | ||
| ) | ||
| @pytest.mark.parametrize("balance", [0, 1]) | ||
| def test_block_full_of_ether_transfers( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a new scenario for ether transfer benchmark: whether the receiver is an empty account.
|
|
||
| benchmark_test( | ||
| code_generator=JumpLoopGenerator( | ||
| code_generator=ExtCallGenerator( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By updating fro JumpLoopGenerator to ExtCallGenerator, we could get rid of the POP opcode each operation, enhancing the TLOAD density.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might break the cases using CALLVALUE because we are now running this in the context of the subcall, and since we are calling using STATICCALL to call that subcontract, CALLVALUE is always going to be zero.
Perhaps replacing this with CALLDATASIZE, since we are passing along the data from the transaction?
| # Create 256 dummy blocks to fill the blockhash window. | ||
| blocks = [Block()] * 256 | ||
|
|
||
| block_number = Op.AND(Op.GAS, 0xFF) if index is None else index |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding extra BLOCKHASH operation for benchmarking, as it was one of the slowest operations.
Cases:
- Valid block index
- Invalid block index
- Dynamic block index
| empty_account: bool, | ||
| initial_balance: bool, | ||
| initial_storage: bool, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add new test scenario for gas repricing effort:
- Accessing empty / non-empty account
- Accessing account contains zero / non-zero balance
- Accessing account contains zero / non-zero storage
marioevz
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
In general, I liked the refactoring and changes, I've just left comments on multiple files just to make everything more polished.
| code = setup + Op.JUMPDEST + repeated_code * max_iterations + cleanup | ||
| code += Op.JUMP(len(setup)) if len(setup) > 0 else Op.PUSH0 + Op.JUMP | ||
| # Pad the code to the maximum code size. | ||
| code += Op.STOP * (max_code_size - len(code)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should make this optional via a new flag in the BenchmarkCodeGenerator class:
code_padding_opcode: Op | None = None
So only tests that need to pad the code to the max size do this.
| sender=pre.fund_eoa(), | ||
| benchmark_test( | ||
| code_generator=JumpLoopGenerator( | ||
| setup=setup, attack_block=attack_block |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| setup=setup, attack_block=attack_block | |
| setup=setup, attack_block=attack_block, code_padding_opcode=Op.STOP |
Following previous suggestion.
| ) | ||
| @pytest.mark.parametrize( | ||
| "absent_target", | ||
| "empty_account", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The meaning of "empty_account" seems confusing IMO. From what I'm reading below, it seems like it's rather "empty_code"?
| target_addr = pre.fund_eoa( | ||
| storage={0: 0x1337} if initial_storage else {0: 0} | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This unconditionally sets the balance to a non-zero value, even when initial_balance==False.
Also, {0: 0}, given the current fund_eoa logic, is still going to try to touch the storage (arguably a bug but besides the point).
We should instead build a kwargs and then call pre.fund_eoa(**kwargs) to make this cleaner.
| if not initial_balance and not initial_storage: | ||
| target_addr = pre.empty_account() | ||
| elif initial_balance or initial_storage: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should refactor this logic a bit:
| if not initial_balance and not initial_storage: | |
| target_addr = pre.empty_account() | |
| elif initial_balance or initial_storage: | |
| if not initial_balance and not initial_storage and empty_code: | |
| target_addr = pre.empty_account() | |
| else: |
And in the else branch we construct the kwargs to either call pre.fund_eoa or pre.deploy_contract.
| benchmark_test( | ||
| code_generator=ExtCallGenerator( | ||
| setup=Op.MLOAD(Op.SELFBALANCE) + Op.POP, | ||
| setup=Op.POP(Op.MLOAD(Op.SELFBALANCE)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, I'm guessing this helps with the stack push/pop elements calculations.
| attack_block = Op.POP(Op.TLOAD(Op.DUP1)) | ||
| code_key_mut = Op.POP + Op.GAS | ||
| code_val_mut = Op.TSTORE(Op.DUP2, Op.GAS) | ||
| setup = Op.GAS + Op.TSTORE(Op.DUP2, Op.GAS) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While trying to read this code, I realize that key_mut -> fixed_key, val_mut -> fixed_value, and flipping the logic, would make it easier to read IMO.
|
|
||
| benchmark_test( | ||
| code_generator=JumpLoopGenerator( | ||
| code_generator=ExtCallGenerator( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might break the cases using CALLVALUE because we are now running this in the context of the subcall, and since we are calling using STATICCALL to call that subcontract, CALLVALUE is always going to be zero.
Perhaps replacing this with CALLDATASIZE, since we are passing along the data from the transaction?
| ) | ||
| executable_code = mem_preparation + opcode(size=return_size) | ||
| code = executable_code | ||
| if return_non_zero_data: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is lost because we are now by default returning code full of Op.STOP, which is all zeros. This can also be addressed by using the code_padding_opcode I mentioned in a previous comment (doing code_padding_opcode=Op.INVALID instead).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch, i do not notice STOP is 0x00. This is helpful.
| yield receiver | ||
|
|
||
|
|
||
| @pytest.fixture |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could still work as a fixture if we parametrize balance=0 on the rest of the test cases.
c720a28 to
2b6823a
Compare
|
Sorry for adding complexity to this PR, I’ve added one more commit that optimizes the
|
🗒️ Description
Enhance the code generator for
BenchmarkTestwrapper:JumpLoopGenerator, which already supports in the other code generator, this could simplifytest_create.ExtCallGenerator, when calculating the max iterations, it should deduct the stack delta that comes fromsetupsection, or it might lead to stack overflow issue.STOPopcode. This could help simplify some cases liketest_codecopy&test_return_revert.ExtCallGenerator, there are two different account: target contract and loop contract. The loop contract will repeatedly calls into target contract viaSTATICCALL. For some operations that involvesCALLDATAinteractions, we should forward theCALLDATAfrom loop contract to target contract via (1) forward theCALLDATAfrom loop contract and (2) Configure the memory from CALLDATA in target contract.Test Case Enhancement:
ExtCallGenerator🔗 Related Issues or PRs
N/A.
✅ Checklist
toxchecks to avoid unnecessary CI fails, see also Code Standards and Enabling Pre-commit Checks:uvx tox -e statictype(scope):.mkdocs servelocally and verified the auto-generated docs for new tests in the Test Case Reference are correctly formatted.@ported_frommarker.Cute Animal Picture