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: Refactor jump table to merge dynamic gas cost with memory expansion cost calculations #24113

Closed
wants to merge 5 commits into from

Conversation

gumb0
Copy link
Member

@gumb0 gumb0 commented Dec 15, 2021

This was discussed in #24048 (comment)

Basically it merges operation.dynamicGas call with operation.memorySize call into single dynamicGas call returning both gas required and new memory size (or 0 if memory size doesn't change).

It allows to simplify logic in interpreter loop and also to get rid of init-time validation to check that operation.memory != nil iff operation.dynamicGas != nil (Because it's now expected from dynamicGas function author to correctly cover both costs, operation.memory member is removed)

@@ -55,45 +55,62 @@ func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) {
return 0, nil
}

func memoryGasCostAndSize(stack *Stack, mem *Memory, sizeFunc memorySizeFunc) (uint64, uint64, error) {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
func memoryGasCostAndSize(stack *Stack, mem *Memory, sizeFunc memorySizeFunc) (uint64, uint64, error) {
func memoryGasCostAndSize(stack *Stack, mem *Memory, sizeFunc memorySizeFunc) (gas, memorySize uint64, err error) {

You can name results (although it is still confusing for me that now these variables are defined from the beginning of the function).

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks, I forgot about that, but not sure if I like this variant more, and whether it aligns well with the usual geth style.

func memoryGasCostAndSize(stack *Stack, mem *Memory, sizeFunc memorySizeFunc) (gas uint64, memorySize uint64, err error) {
	memSize, overflow := sizeFunc(stack)
	if overflow {
		return 0, 0, ErrGasUintOverflow
	}
	// memory is expanded in words of 32 bytes. Gas is also calculated in words.
	memorySize, overflow = math.SafeMul(toWordSize(memSize), 32)
	if overflow {
		return 0, 0, ErrGasUintOverflow
	}
	gas, err = memoryGasCost(mem, memorySize)
	if err != nil {
		return 0, 0, err
	}
	return
}

@holiman
Copy link
Contributor

holiman commented Dec 16, 2021 via email

@gumb0
Copy link
Member Author

gumb0 commented Dec 16, 2021

Not much changes in my benchmarks:

name                                  old time/op  new time/op  delta
main/blake2b_shifts/2805nulls         12.3ms ±13%  12.7ms ±12%    ~     (p=0.529 n=20+20)
main/blake2b_shifts/5610nulls         26.6ms ± 1%  26.5ms ± 1%  -0.57%  (p=0.000 n=19+18)
main/blake2b_shifts/8415nulls         39.6ms ± 2%  39.2ms ± 2%  -0.87%  (p=0.003 n=19+18)
main/weierstrudel/0                    454µs ± 0%   458µs ± 0%  +0.82%  (p=0.000 n=19+19)
main/weierstrudel/1                    918µs ± 0%   930µs ± 1%  +1.27%  (p=0.000 n=20+20)
main/weierstrudel/3                   1.44ms ± 0%  1.46ms ± 1%  +1.26%  (p=0.000 n=19+18)
main/weierstrudel/9                   2.97ms ± 1%  3.00ms ± 1%  +0.95%  (p=0.000 n=18+19)
main/weierstrudel/14                  4.24ms ± 1%  4.28ms ± 1%  +0.94%  (p=0.000 n=18+19)
main/sha1_divs/empty                   181µs ± 1%   181µs ± 1%  +0.41%  (p=0.001 n=19+20)
main/sha1_divs/1351                   3.54ms ± 1%  3.54ms ± 1%    ~     (p=0.945 n=19+20)
main/sha1_divs/2737                   6.92ms ± 2%  6.87ms ± 1%  -0.82%  (p=0.000 n=19+19)
main/sha1_divs/5311                   13.4ms ± 3%  13.5ms ± 2%  +0.85%  (p=0.004 n=18+19)
main/sha1_shifts/empty                 142µs ± 1%   146µs ± 1%  +2.88%  (p=0.000 n=18+20)
main/sha1_shifts/1351                 2.82ms ± 1%  2.89ms ± 1%  +2.28%  (p=0.000 n=18+19)
main/sha1_shifts/2737                 5.49ms ± 1%  5.58ms ± 1%  +1.66%  (p=0.000 n=18+20)
main/sha1_shifts/5311                 10.8ms ± 1%  11.0ms ± 1%  +1.85%  (p=0.000 n=18+17)
main/blake2b_huff/empty                100µs ± 0%   100µs ± 1%    ~     (p=0.746 n=17+20)
main/blake2b_huff/2805nulls           1.89ms ± 1%  1.89ms ± 1%    ~     (p=0.779 n=20+20)
main/blake2b_huff/5610nulls           3.63ms ± 1%  3.63ms ± 1%    ~     (p=0.443 n=18+20)
main/blake2b_huff/8415nulls           5.31ms ± 1%  5.30ms ± 1%    ~     (p=0.141 n=20+19)
micro/JUMPDEST_n0/empty               2.21ms ± 1%  2.19ms ± 1%  -1.24%  (p=0.000 n=20+18)
micro/jump_around/empty                136µs ± 1%   136µs ± 1%    ~     (p=0.067 n=20+18)
micro/memory_grow_mstore/nogrow        434µs ± 1%   434µs ± 1%    ~     (p=0.365 n=19+20)
micro/memory_grow_mstore/by1           458µs ± 0%   459µs ± 1%    ~     (p=0.322 n=19+20)
micro/memory_grow_mstore/by16          544µs ± 1%   544µs ± 1%    ~     (p=0.808 n=18+18)
micro/memory_grow_mstore/by32          652µs ± 0%   652µs ± 1%    ~     (p=0.900 n=17+19)
micro/signextend/zero                  304µs ± 1%   302µs ± 1%  -0.57%  (p=0.008 n=19+19)
micro/signextend/one                   321µs ± 5%   312µs ± 1%  -2.78%  (p=0.000 n=20+19)
micro/loop_with_many_jumpdests/empty  1.83µs ± 4%  1.85µs ± 2%    ~     (p=0.173 n=18+20)
micro/memory_grow_mload/nogrow         202µs ± 1%   205µs ± 1%  +1.03%  (p=0.000 n=20+19)
micro/memory_grow_mload/by1            218µs ± 1%   221µs ± 1%  +1.38%  (p=0.000 n=19+20)
micro/memory_grow_mload/by16           303µs ± 1%   305µs ± 0%  +0.78%  (p=0.000 n=18+19)
micro/memory_grow_mload/by32           398µs ± 1%   400µs ± 1%  +0.47%  (p=0.000 n=19+20)
[Geo mean]                            1.07ms       1.08ms       +0.47%

func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
memoryGas, err := memoryGasCost(mem, memorySize)
func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory) (uint64, uint64, error) {
memoryGas, memorySize, err := memoryGasCostAndSize(stack, mem, memoryCall)
Copy link
Member Author

Choose a reason for hiding this comment

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

CALLCODE uses the same memory function as CALL

CALLCODE: {
execute: opCallCode,
constantGas: params.CallGasFrontier,
dynamicGas: gasCallCode,
minStack: minStack(7, 1),
maxStack: maxStack(7, 1),
memorySize: memoryCall,

@gumb0 gumb0 marked this pull request as ready for review December 16, 2021 14:16
@holiman
Copy link
Contributor

holiman commented Dec 16, 2021

As it is, I'm a bit torn on this. It is a pretty deep change, which needs to be very thorougly reviewed, but the benchmarks do not really support the change.
However, I think you may have missed an opportunity for optimization: previously, the code used various makeMemoryCopier bla bla, to build a function that we shoved into a map. Since we were going to do the call into it via an indirecton in operation, there was no way it was going to be inlined, so it didn't matter.

Now that you placed into dynamicGas, it might be possible to make the call to the memory calculation become inlined. But it might mean that instead of calling makeMemoryCoper, you would instead define each such function. And that might improve the benchmark. Just a thought/theory.

@gumb0
Copy link
Member Author

gumb0 commented Dec 21, 2021

However, I think you may have missed an opportunity for optimization: previously, the code used various makeMemoryCopier bla bla, to build a function that we shoved into a map. Since we were going to do the call into it via an indirecton in operation, there was no way it was going to be inlined, so it didn't matter.

Now that you placed into dynamicGas, it might be possible to make the call to the memory calculation become inlined. But it might mean that instead of calling makeMemoryCoper, you would instead define each such function. And that might improve the benchmark. Just a thought/theory.

In this refactoring I keep the memory gas calculation general, by having low-level memoryGasCostAndSize function which takes as a paramter a function returning memory size for particular opcode. So there's still indirection (even if particular memoryCopierGas is unrolled) and unless we copypaste this memoryGasCostAndSize into memory expanding opcodes' gas calculations, I doubt it will get to inlining. And it would affect only this small subset of opcodes that expand memory. But I can try.

@chfast
Copy link
Member

chfast commented Dec 21, 2021

I don't see any clear improvement (historically the signextend is sensitive to any changes).

name                                  old time/op  new time/op  delta                                                                                                                                                             
micro/memory_grow_mload/nogrow         212µs ± 1%   211µs ± 2%   -0.87%  (p=0.003 n=19+20)                                                                                                                                        
micro/memory_grow_mload/by1            223µs ± 1%   221µs ± 1%   -0.83%  (p=0.000 n=18+20)                                                                                                                                        
micro/memory_grow_mload/by16           285µs ± 0%   283µs ± 1%   -0.78%  (p=0.000 n=18+20)                                                                                                                                        
micro/memory_grow_mload/by32           358µs ± 1%   357µs ± 1%     ~     (p=0.426 n=20+18)                                                                                                                                        
micro/signextend/zero                  355µs ± 0%   308µs ± 1%  -13.10%  (p=0.000 n=20+19)                                                                                                                                        
micro/signextend/one                   369µs ± 1%   336µs ± 1%   -9.00%  (p=0.000 n=19+20)                                                                                                                                        
micro/loop_with_many_jumpdests/empty  1.69µs ± 2%  1.67µs ± 1%   -0.73%  (p=0.011 n=20+20)                                                                                                                                        
micro/jump_around/empty                135µs ± 1%   134µs ± 1%   -0.79%  (p=0.000 n=20+19)                                                                                                                                        
micro/memory_grow_mstore/nogrow        449µs ± 0%   458µs ± 1%   +2.09%  (p=0.000 n=18+17)                                                                                                                                        
micro/memory_grow_mstore/by1           464µs ± 1%   472µs ± 1%   +1.75%  (p=0.000 n=20+18)                                                                                                                                        
micro/memory_grow_mstore/by16          523µs ± 0%   538µs ± 1%   +2.79%  (p=0.000 n=20+20)                                                                                                                                        
micro/memory_grow_mstore/by32          602µs ± 1%   621µs ± 1%   +3.11%  (p=0.000 n=20+20)                                                                                                                                        
micro/JUMPDEST_n0/empty               2.58ms ± 0%  2.49ms ± 0%   -3.67%  (p=0.000 n=18+19)                                                                                                                                        
main/sha1_divs/empty                   176µs ± 0%   178µs ± 1%   +0.84%  (p=0.000 n=19+20)                                                                                                                                        
main/sha1_divs/1351                   3.55ms ± 1%  3.56ms ± 1%   +0.42%  (p=0.000 n=19+20)
main/sha1_divs/2737                   6.92ms ± 0%  6.93ms ± 1%   +0.22%  (p=0.004 n=17+18)
main/sha1_divs/5311                   13.5ms ± 0%  13.6ms ± 0%     ~     (p=0.086 n=20+20)
main/weierstrudel/0                    446µs ± 0%   445µs ± 0%   -0.25%  (p=0.000 n=20+19)
main/weierstrudel/1                    923µs ± 0%   910µs ± 1%   -1.40%  (p=0.000 n=18+19)
main/weierstrudel/3                   1.45ms ± 0%  1.44ms ± 1%   -1.10%  (p=0.000 n=19+18)
main/weierstrudel/9                   3.03ms ± 1%  3.00ms ± 1%   -0.81%  (p=0.000 n=20+18)
main/weierstrudel/14                  4.33ms ± 0%  4.30ms ± 1%   -0.59%  (p=0.000 n=19+20)
main/sha1_shifts/empty                 132µs ± 1%   131µs ± 1%   -0.84%  (p=0.000 n=20+19)
main/sha1_shifts/1351                 2.71ms ± 1%  2.66ms ± 0%   -1.88%  (p=0.000 n=20+20)
main/sha1_shifts/2737                 5.28ms ± 0%  5.17ms ± 1%   -1.95%  (p=0.000 n=18+20)
main/sha1_shifts/5311                 10.3ms ± 0%  10.1ms ± 1%   -1.73%  (p=0.000 n=18+20)
main/blake2b_huff/empty               95.7µs ± 0%  97.0µs ± 0%   +1.36%  (p=0.000 n=17+17)
main/blake2b_huff/2805nulls           1.85ms ± 0%  1.88ms ± 0%   +1.37%  (p=0.000 n=19+19)
main/blake2b_huff/5610nulls           3.59ms ± 0%  3.64ms ± 0%   +1.31%  (p=0.000 n=17+19)
main/blake2b_huff/8415nulls           5.26ms ± 0%  5.33ms ± 0%   +1.36%  (p=0.000 n=19+19)
main/blake2b_shifts/2805nulls         13.1ms ± 2%  13.1ms ± 1%     ~     (p=0.325 n=19+19)
main/blake2b_shifts/5610nulls         25.9ms ± 2%  26.1ms ± 3%   +0.78%  (p=0.007 n=17+20)
main/blake2b_shifts/8415nulls         38.4ms ± 1%  38.6ms ± 1%   +0.44%  (p=0.003 n=17+18)
[Geo mean]                            1.07ms       1.06ms        -0.73%

@gumb0
Copy link
Member Author

gumb0 commented Dec 22, 2021

I tried inlining memoryGasCostAndSize for several opcodes (CALLDATACOPY, CODECOPY, EXCCODECOPY, RETURNDATACOPY, RETURN, REVERT, MLOAD, MSTORE, MSTORE8, CREATE, LOG*), still not much improvement:

name                                  old time/op  new time/op  delta
main/blake2b_shifts/2805nulls         12.6ms ±14%  12.3ms ±11%  -3.04%  (p=0.038 n=20+19)
main/blake2b_shifts/5610nulls         26.3ms ± 1%  26.7ms ± 1%  +1.40%  (p=0.000 n=17+18)
main/blake2b_shifts/8415nulls         38.9ms ± 1%  39.7ms ± 2%  +2.03%  (p=0.000 n=18+19)
main/weierstrudel/0                    457µs ± 1%   447µs ± 0%  -2.15%  (p=0.000 n=20+20)
main/weierstrudel/1                    915µs ± 1%   907µs ± 1%  -0.85%  (p=0.000 n=18+18)
main/weierstrudel/3                   1.43ms ± 0%  1.42ms ± 0%  -0.75%  (p=0.000 n=19+19)
main/weierstrudel/9                   2.96ms ± 0%  2.96ms ± 1%    ~     (p=0.513 n=19+20)
main/weierstrudel/14                  4.22ms ± 0%  4.22ms ± 1%    ~     (p=1.000 n=18+20)
main/sha1_divs/empty                   182µs ± 1%   180µs ± 1%  -1.15%  (p=0.000 n=20+20)
main/sha1_divs/1351                   3.60ms ± 3%  3.54ms ± 2%  -1.70%  (p=0.001 n=19+20)
main/sha1_divs/2737                   6.98ms ± 5%  6.90ms ± 2%  -1.16%  (p=0.003 n=19+20)
main/sha1_divs/5311                   13.6ms ± 5%  13.4ms ± 2%  -1.31%  (p=0.005 n=19+20)
main/sha1_shifts/empty                 141µs ± 1%   143µs ± 4%  +1.91%  (p=0.000 n=19+20)
main/sha1_shifts/1351                 2.80ms ± 4%  2.85ms ± 5%  +1.83%  (p=0.002 n=20+20)
main/sha1_shifts/2737                 5.44ms ± 7%  5.52ms ± 2%    ~     (p=0.081 n=20+20)
main/sha1_shifts/5311                 10.7ms ± 4%  10.9ms ± 3%  +2.13%  (p=0.001 n=19+20)
main/blake2b_huff/empty               98.6µs ± 0%  97.9µs ± 0%  -0.74%  (p=0.000 n=19+17)
main/blake2b_huff/2805nulls           1.90ms ± 5%  1.88ms ± 5%    ~     (p=0.149 n=20+20)
main/blake2b_huff/5610nulls           3.63ms ± 4%  3.65ms ± 4%    ~     (p=0.627 n=20+19)
main/blake2b_huff/8415nulls           5.35ms ± 7%  5.29ms ± 6%    ~     (p=0.183 n=20+20)
micro/JUMPDEST_n0/empty               2.22ms ± 7%  2.19ms ± 7%    ~     (p=0.063 n=20+20)
micro/jump_around/empty                133µs ± 1%   131µs ± 1%  -1.19%  (p=0.000 n=18+18)
micro/memory_grow_mstore/nogrow        432µs ± 5%   426µs ± 4%    ~     (p=0.060 n=20+20)
micro/memory_grow_mstore/by1           457µs ± 1%   451µs ± 2%  -1.26%  (p=0.000 n=19+20)
micro/memory_grow_mstore/by16          541µs ± 0%   537µs ± 1%  -0.81%  (p=0.000 n=18+17)
micro/memory_grow_mstore/by32          651µs ± 0%   647µs ± 1%  -0.60%  (p=0.000 n=19+20)
micro/signextend/zero                  303µs ± 4%   302µs ± 4%    ~     (p=0.429 n=20+20)
micro/signextend/one                   316µs ± 4%   312µs ± 5%    ~     (p=0.054 n=19+20)
micro/loop_with_many_jumpdests/empty  1.84µs ± 4%  1.82µs ± 3%  -1.20%  (p=0.027 n=19+20)
micro/memory_grow_mload/nogrow         202µs ± 3%   200µs ± 4%    ~     (p=0.201 n=20+20)
micro/memory_grow_mload/by1            216µs ± 1%   215µs ± 1%  -0.39%  (p=0.005 n=20+19)
micro/memory_grow_mload/by16           301µs ± 0%   300µs ± 0%  -0.33%  (p=0.000 n=18+17)
micro/memory_grow_mload/by32           398µs ± 1%   398µs ± 1%    ~     (p=0.916 n=19+18)
[Geo mean]                            1.07ms       1.07ms       -0.44%

@holiman
Copy link
Contributor

holiman commented Sep 23, 2022

I'll close this. Feel free to reopen if you manage to unlock some new level :)

@holiman holiman closed this Sep 23, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants