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

Adjust formulas for calculating memory usage of arithmetic operations on big integers #1590

Merged

Conversation

turbolent
Copy link
Member

@turbolent turbolent commented Apr 18, 2022

Closes https://github.com/dapperlabs/cadence-private-issues/issues/32

Based on https://www.notion.so/dapperlabs/Metering-arithmetic-operations-7acc852f89834e78911f8f42c6bb842d.

Not complete, I left some questions in the document.


  • Targeted PR against master branch
  • Linked to Github issue with discussion and accepted design OR link to spec that describes this work
  • Code follows the standards mentioned here
  • Updated relevant documentation
  • Re-reviewed Files changed in the Github PR explorer
  • Added appropriate labels

@codecov
Copy link

codecov bot commented Apr 18, 2022

Codecov Report

Merging #1590 (be06794) into feature/memory-metering (2b47860) will increase coverage by 0.09%.
The diff coverage is 78.83%.

@@                     Coverage Diff                     @@
##           feature/memory-metering    #1590      +/-   ##
===========================================================
+ Coverage                    76.40%   76.49%   +0.09%     
===========================================================
  Files                          291      291              
  Lines                        58665    58782     +117     
===========================================================
+ Hits                         44822    44967     +145     
+ Misses                       12265    12231      -34     
- Partials                      1578     1584       +6     
Flag Coverage Δ
unittests 76.49% <78.83%> (+0.09%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
runtime/common/metering.go 87.95% <78.72%> (-9.59%) ⬇️
runtime/interpreter/value.go 71.11% <100.00%> (-0.01%) ⬇️
runtime/interpreter/account.go 92.59% <0.00%> (-0.11%) ⬇️
runtime/interpreter/decode.go 46.88% <0.00%> (-0.09%) ⬇️
runtime/sema/authaccount_type.go 100.00% <0.00%> (ø)
runtime/interpreter/accountkey.go 100.00% <0.00%> (ø)
runtime/interpreter/interpreter.go 89.33% <0.00%> (+0.03%) ⬆️
runtime/convertValues.go 81.83% <0.00%> (+0.04%) ⬆️
runtime/convertTypes.go 77.58% <0.00%> (+0.09%) ⬆️
... and 8 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 2b47860...be06794. Read the comment docs.

@github-actions
Copy link

github-actions bot commented Apr 18, 2022

Cadence Benchstat comparison

This branch with compared with the base branch onflow:feature/memory-metering commit 2b47860
The command for i in {1..N}; do go test ./... -run=XXX -bench=. -shuffle=on; done was used.
Bench tests were run a total of 7 times on each branch.

Results

old.txtnew.txt
time/opdelta
RuntimeResourceDictionaryValues-26.46ms ± 4%6.46ms ± 2%~(p=1.000 n=7+7)
Transfer-287.5ns ±12%87.8ns ± 1%~(p=0.731 n=7+6)
ParseArray-213.1ms ± 1%14.3ms ±33%~(p=0.836 n=6+7)
ParseInfix-29.33µs ± 2%9.59µs ± 6%~(p=0.073 n=5+7)
ParseFungibleToken/With_memory_metering-2247µs ± 1%247µs ± 2%~(p=0.945 n=6+7)
ParseDeploy/byte_array-221.3ms ± 4%22.0ms ±12%~(p=1.000 n=6+7)
QualifiedIdentifierCreation/One_level-22.34ns ± 0%2.34ns ± 0%~(p=0.242 n=5+6)
QualifiedIdentifierCreation/Three_levels-2140ns ± 2%140ns ± 0%~(p=0.084 n=6+6)
ContractInterfaceFungibleToken-239.8µs ± 2%39.6µs ± 7%~(p=0.836 n=7+6)
CheckContractInterfaceFungibleTokenConformance-2134µs ± 4%156µs ±36%~(p=0.234 n=6+7)
NewInterpreter/new_interpreter-21.15µs ± 7%1.12µs ± 1%~(p=0.108 n=7+6)
NewInterpreter/new_sub-interpreter-22.27µs ± 1%2.46µs ±36%~(p=0.937 n=6+6)
InterpretRecursionFib-22.73ms ± 3%2.73ms ± 4%~(p=0.805 n=7+7)
ParseDeploy/decode_hex-21.18ms ± 0%1.16ms ± 1%−1.58%(p=0.004 n=5+6)
ParseFungibleToken/Without_memory_metering-2206µs ±11%198µs ± 0%−3.85%(p=0.035 n=7+6)
RuntimeFungibleTokenTransfer-21.47ms ± 3%1.11ms ±30%−24.20%(p=0.008 n=6+7)
 
alloc/opdelta
Transfer-248.0B ± 0%48.0B ± 0%~(all equal)
ParseFungibleToken/Without_memory_metering-2199kB ± 0%199kB ± 0%~(all equal)
ParseFungibleToken/With_memory_metering-2199kB ± 0%199kB ± 0%~(p=0.385 n=7+6)
QualifiedIdentifierCreation/One_level-20.00B 0.00B ~(all equal)
QualifiedIdentifierCreation/Three_levels-264.0B ± 0%64.0B ± 0%~(all equal)
ContractInterfaceFungibleToken-226.6kB ± 0%26.6kB ± 0%~(p=0.385 n=7+6)
CheckContractInterfaceFungibleTokenConformance-266.2kB ± 0%66.2kB ± 0%~(p=0.939 n=5+7)
NewInterpreter/new_interpreter-2848B ± 0%848B ± 0%~(all equal)
NewInterpreter/new_sub-interpreter-21.36kB ± 0%1.36kB ± 0%~(all equal)
InterpretRecursionFib-21.20MB ± 0%1.20MB ± 0%~(all equal)
RuntimeResourceDictionaryValues-22.25MB ± 0%2.25MB ± 0%−0.01%(p=0.001 n=7+7)
RuntimeFungibleTokenTransfer-2275kB ± 0%275kB ± 0%−0.04%(p=0.001 n=7+7)
 
allocs/opdelta
Transfer-21.00 ± 0%1.00 ± 0%~(all equal)
ParseFungibleToken/Without_memory_metering-21.07k ± 0%1.07k ± 0%~(all equal)
ParseFungibleToken/With_memory_metering-21.07k ± 0%1.07k ± 0%~(all equal)
QualifiedIdentifierCreation/One_level-20.00 0.00 ~(all equal)
QualifiedIdentifierCreation/Three_levels-22.00 ± 0%2.00 ± 0%~(all equal)
ContractInterfaceFungibleToken-2458 ± 0%458 ± 0%~(all equal)
CheckContractInterfaceFungibleTokenConformance-21.07k ± 0%1.07k ± 0%~(all equal)
NewInterpreter/new_interpreter-213.0 ± 0%13.0 ± 0%~(all equal)
NewInterpreter/new_sub-interpreter-241.0 ± 0%41.0 ± 0%~(all equal)
InterpretRecursionFib-223.8k ± 0%23.8k ± 0%~(all equal)
RuntimeResourceDictionaryValues-237.6k ± 0%37.6k ± 0%−0.01%(p=0.001 n=7+7)
RuntimeFungibleTokenTransfer-24.59k ± 0%4.59k ± 0%−0.07%(p=0.001 n=7+7)
 

@turbolent turbolent merged commit eab1954 into feature/memory-metering Apr 19, 2022
@turbolent turbolent deleted the bastian/adjust-big-int-memory-metering branch April 19, 2022 21:19
@tarakby
Copy link
Contributor

tarakby commented Apr 26, 2022

FYI, I am back today and answered the questions here.

Copy link
Contributor

@tarakby tarakby left a comment

Choose a reason for hiding this comment

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

I left a late review.

  • The code is overall correct except some small errors.
  • I also noticed some edge cases to tighten the upper bound were omitted (like zero big integers), maybe to simplify the code?
  • If Cadence is accepting big integer shift values, it might make sense to update the API in the future to only accept 32 or 64-bits shift values.

func NewPlusBigIntMemoryUsage(a, b *big.Int) MemoryUsage {
// max(|a|, |b|) + 5
Copy link
Contributor

Choose a reason for hiding this comment

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

I see that the cases |a|==|b|==0 were skipped. I guess that's because it makes the metering more complex?
The downside is that the metering becomes more "loose", but that's okay if you are fine with that.

} else {
recursionCost := int(unsafe.Sizeof(uintptr(0))) +
9*bWordLength +
int(math.Floor(float64(aWordLength)/float64(bWordLength))) + 12
Copy link
Contributor

Choose a reason for hiding this comment

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

On the document, I wrote Floor because that's the math specific operation. In software languages, integer division implicitly returns that floor.
We could replace int(math.Floor(float64(aWordLength)/float64(bWordLength))) by aWordLength/bWordLength.

var resultWordLength int
if a.Cmp(b) < 0 || b.Cmp(bigOne) == 0 {
resultWordLength = aWordLength + 4
} else if b.Cmp(bigOneHundred) < 0 {
Copy link
Contributor

@tarakby tarakby Apr 28, 2022

Choose a reason for hiding this comment

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

This should check |b| < 100 while it the code does b<100.
Same for the check |b|==1, it shouldn't be b==1.

)
}

var invalidLeftShift = errors.New("invalid left shift of non-Int64")

func NewBitwiseLeftShiftBigIntMemoryUsage(a, b *big.Int) MemoryUsage {
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think the shift value should be a big integer, a uint is more than enough.
Then in the metering b/word_size would avoid using a big integer division ( I guess the metering itself should be very light and should not allocate memory)

resultWordLength = aWordLength + 4
} else {
// TODO: meter the allocation of the metering itself
shiftByteLengthBig := new(big.Int).Div(b, bigIntWordSizeAsBig)
Copy link
Contributor

Choose a reason for hiding this comment

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

This the operation I mentioned in the last comment.

// TODO: https://github.com/dapperlabs/cadence-private-issues/issues/32
BigIntByteLength(a) +
BigIntByteLength(b),
resultWordLength * bigIntWordSize,
)
}

func NewBitwiseRightShiftBigIntMemoryUsage(a, b *big.Int) MemoryUsage {
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here for the shift value that should be uint

@tarakby tarakby mentioned this pull request May 7, 2022
5 tasks
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.

3 participants