Skip to content
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

core/vm: avoid memory expansion check for trivial ops #24048

Merged
merged 1 commit into from Dec 14, 2021

Conversation

holiman
Copy link
Contributor

@holiman holiman commented Dec 3, 2021

This PR moves the dynamic memory check function to only be checked if the op has a dynamic gas function. This is strictly not a guarantee, just how things are, and it allows us to skip two conditionals on the cheap ops.
One minor semantic change is that cost is set earlier, making errors more correct than previously (if erroring on e.g. shallow stack)

@holiman
Copy link
Contributor Author

holiman commented Dec 3, 2021

Simple loop benchmark against master with some optimizations already in

[user@work runtime]$ benchstat before after
name                    old time/op    new time/op    delta
SimpleLoop/loop-100M-6     426ms ± 9%     371ms ± 8%  -13.11%  (p=0.000 n=9+10)

name                    old alloc/op   new alloc/op   delta
SimpleLoop/loop-100M-6    2.86kB ±11%    2.91kB ±13%     ~     (p=0.810 n=10+10)

name                    old allocs/op  new allocs/op  delta
SimpleLoop/loop-100M-6      25.2 ± 5%      25.4 ± 6%     ~     (p=1.000 n=10+10)

Last release against master

[user@work runtime]$ benchstat last_release before
name                    old time/op    new time/op    delta
SimpleLoop/loop-100M-6     525ms ± 6%     426ms ± 9%  -18.82%  (p=0.000 n=10+9)

name                    old alloc/op   new alloc/op   delta
SimpleLoop/loop-100M-6    3.28kB ±23%    2.86kB ±11%  -12.89%  (p=0.050 n=10+10)

name                    old allocs/op  new allocs/op  delta
SimpleLoop/loop-100M-6      26.5 ± 9%      25.2 ± 5%   -4.91%  (p=0.030 n=10+10)

last release against this PR

[user@work runtime]$ benchstat last_release after
name                    old time/op    new time/op    delta
SimpleLoop/loop-100M-6     525ms ± 6%     371ms ± 8%  -29.46%  (p=0.000 n=10+10)

name                    old alloc/op   new alloc/op   delta
SimpleLoop/loop-100M-6    3.28kB ±23%    2.91kB ±13%     ~     (p=0.087 n=10+10)

name                    old allocs/op  new allocs/op  delta
SimpleLoop/loop-100M-6      26.5 ± 9%      25.4 ± 6%     ~     (p=0.050 n=10+10)

if op == nil {
panic(fmt.Sprintf("op 0x%x is not set", i))
}
// The interpreter has an assumption that if the memorySize function is
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Considering they always go together, it would be better to merge these into single function that outputs new memory size and error.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: they don't "always go together". If dynamic memory, then dynamic gas, but not always the other way around. For example the EXP or SSTORE and SELFDESTRUCT doesn't expand memory but have a dynamic cost.

But yes, maybe something could be changed in the strucutre at a higher level.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, what I meant in this case they should return unchanged memory size or 0.
But in total this reduces helper calls from 2 to 1 and eliminates the need for validate().

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eliminates the need for validate().

Afaik, the validate is only performed during geth bootup, when the global vars are instantiated. Unless I misunderstood something.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that it can be reworked, but I don't want to do it in this PR. I think merging memory funcs and gas funcs is going to be a very large change.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Afaik, the validate is only performed during geth bootup, when the global vars are instantiated. Unless I misunderstood something.

Correct. But my comment was about the design. Not needing validate() is better design.

I agree that it can be reworked, but I don't want to do it in this PR.

Make sense.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am doing this refactoring in #24113

@chfast
Copy link
Contributor

chfast commented Dec 3, 2021

name                                  old time/op  new time/op  delta                                                                                                                                                             
micro/memory_grow_mload/nogrow         219µs ± 0%   215µs ± 4%   -1.96%  (p=0.000 n=18+19)                                                                                                                                        
micro/memory_grow_mload/by1            230µs ± 1%   223µs ± 1%   -2.67%  (p=0.000 n=20+20)                                                                                                                                        
micro/memory_grow_mload/by16           292µs ± 1%   285µs ± 1%   -2.39%  (p=0.000 n=19+19)                                                                                                                                        
micro/memory_grow_mload/by32           367µs ± 1%   359µs ± 1%   -2.41%  (p=0.000 n=20+20)                                                                                                                                        
micro/signextend/zero                  390µs ± 1%   324µs ± 1%  -16.93%  (p=0.000 n=20+20)                                                                                                                                        
micro/signextend/one                   406µs ± 1%   337µs ± 1%  -17.10%  (p=0.000 n=20+20)                                                                                                                                        
micro/loop_with_many_jumpdests/empty  1.68µs ± 2%  1.69µs ± 2%     ~     (p=0.583 n=19+20)                                                                                                                                        
micro/jump_around/empty                145µs ± 1%   139µs ± 1%   -4.00%  (p=0.000 n=17+15)                                                                                                                                        
micro/memory_grow_mstore/nogrow        445µs ± 0%   451µs ± 1%   +1.38%  (p=0.000 n=19+19)                                                                                                                                        
micro/memory_grow_mstore/by1           460µs ± 1%   467µs ± 0%   +1.50%  (p=0.000 n=20+19)                                                                                                                                        
micro/memory_grow_mstore/by16          539µs ± 0%   542µs ± 0%   +0.60%  (p=0.000 n=18+20)                                                                                                                                        
micro/memory_grow_mstore/by32          607µs ± 1%   609µs ± 0%   +0.30%  (p=0.006 n=20+20)                                                                                                                                        
micro/JUMPDEST_n0/empty               2.74ms ± 0%  2.58ms ± 0%   -5.96%  (p=0.000 n=19+18)                                                                                                                                        
main/sha1_divs/empty                   196µs ± 1%   179µs ± 1%   -8.52%  (p=0.000 n=20+20)                                                                                                                                        
main/sha1_divs/1351                   3.95ms ± 0%  3.56ms ± 0%   -9.94%  (p=0.000 n=20+16)
main/sha1_divs/2737                   7.71ms ± 0%  6.95ms ± 0%   -9.81%  (p=0.000 n=19+20)
main/sha1_divs/5311                   15.1ms ± 0%  13.6ms ± 0%   -9.75%  (p=0.000 n=18+19)
main/weierstrudel/0                    471µs ± 0%   448µs ± 0%   -4.72%  (p=0.000 n=18+20)
main/weierstrudel/1                    980µs ± 1%   918µs ± 0%   -6.31%  (p=0.000 n=20+18)
main/weierstrudel/3                   1.54ms ± 1%  1.45ms ± 1%   -5.90%  (p=0.000 n=20+19)
main/weierstrudel/9                   3.18ms ± 0%  3.02ms ± 1%   -5.24%  (p=0.000 n=20+18)
main/weierstrudel/14                  4.55ms ± 1%  4.32ms ± 1%   -5.13%  (p=0.000 n=18+20)
main/sha1_shifts/empty                 151µs ± 0%   132µs ± 1%  -12.63%  (p=0.000 n=20+20)
main/sha1_shifts/1351                 3.09ms ± 0%  2.68ms ± 1%  -13.24%  (p=0.000 n=14+20)
main/sha1_shifts/2737                 6.02ms ± 0%  5.22ms ± 0%  -13.34%  (p=0.000 n=17+20)
main/sha1_shifts/5311                 11.8ms ± 1%  10.2ms ± 1%  -13.34%  (p=0.000 n=19+20)
main/blake2b_huff/empty                100µs ± 0%    98µs ± 0%   -1.48%  (p=0.000 n=18+19)
main/blake2b_huff/2805nulls           1.90ms ± 1%  1.88ms ± 1%   -1.08%  (p=0.000 n=20+19)
main/blake2b_huff/5610nulls           3.70ms ± 0%  3.65ms ± 1%   -1.44%  (p=0.000 n=19+20)
main/blake2b_huff/8415nulls           5.43ms ± 1%  5.34ms ± 0%   -1.65%  (p=0.000 n=20+17)
main/blake2b_shifts/2805nulls         14.2ms ± 2%  12.9ms ± 1%   -9.21%  (p=0.000 n=20+17)
main/blake2b_shifts/5610nulls         28.1ms ± 0%  25.7ms ± 1%   -8.44%  (p=0.000 n=18+20)
main/blake2b_shifts/8415nulls         41.9ms ± 0%  38.2ms ± 1%   -8.73%  (p=0.000 n=16+17)
[Geo mean]                            1.14ms       1.07ms        -6.19%

@holiman holiman marked this pull request as ready for review December 3, 2021 12:25
// Get the operation from the jump table and validate the stack to ensure there are
// enough stack items available to perform the operation.
op = contract.GetOp(pc)
operation := in.cfg.JumpTable[op]
cost = operation.constantGas // For tracing
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yesterday I started a branch to experiment with moving all the gas calculation into another function (next to the implementation): https://github.com/ipsilon/go-ethereum/tree/gas-redesign

The constant gas instructions would have the same simple function charging the constant gas, others would have the dynamic calculation + memory expansion.

I am not sure if this will help everything considered, because it will always mean an indirect call, but at the same time it reduces most of the branching in this loop.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looked briefly at the last commit there -- I think it won't work that easily.

The way the dynamic gas has evolved, we need to first ensure that the caller can cover the static cost, because for some ops, actually calculating the dynamic cost is expensive: checking the existence of things in the trie.
So we can't just calculate everything and then charge.

We can get around it by either having the "chargeGas" function not only return the gas, but actually charge it via the contract.
Alternatively, we can pass the availableGas into the "chargeGas" function.

Copy link
Member

@MariusVanDerWijden MariusVanDerWijden left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@holiman holiman added this to the 1.10.14 milestone Dec 14, 2021
@holiman holiman merged commit 155795b into ethereum:master Dec 14, 2021
JacekGlen pushed a commit to JacekGlen/go-ethereum that referenced this pull request May 26, 2022
yperbasis added a commit to ledgerwatch/erigon that referenced this pull request Dec 20, 2022
Cherry-pick ethereum/go-ethereum#24048

Co-authored-by: Martin Holst Swende <martin@swende.se>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants