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

SSZ v2 #223

Merged
merged 19 commits into from
Mar 24, 2022
Merged

SSZ v2 #223

merged 19 commits into from
Mar 24, 2022

Conversation

dapplion
Copy link
Contributor

@dapplion dapplion commented Jan 14, 2022

Motivation

Our current library has multiple fundamental limitations that affect performance, memory and usability downstream. In Lodestar we have a added a lot of custom code to get around this limitations. This PR up-streams all the good ideas created downstream into a library that's performant by default without requiring extra infrastructure

Features

TreeViewDU

Stands for TreeView Deferred Update, and allows for very fast reads and writes without committing to the tree until latter. Then is optimized batched operations all nodes are recomputed and added to the tree with a fast routine setNodesAtDepth.

This mode is meant to be used in the entire beacon state transition function. This is a fundamental shift from the previous version where tree views and regular values had the same API. However, achieving this requires ES6 Proxy which have performance issues. Now the beacon state transition function will not compile if regular JS values are passed to it, it will only accept the TreeViewDU representation of the state.

const statuses = state.currentEpochParticipation;
for (const [index, delta] of deltas) {
  statuses.set(index, statuses.get(index) + delta)
}
state.commit()

DataView and Uint8Array for faster (de)serialization

Current version spends significant time converting bytes to decimal numbers. DataView is extremely fast at bytes to decimal conversion and back. This PR uses it for value conversions and to populate HashObjects much faster.

However, Uint8Array.slice is by far the fastest way of copying binary data. So this PR passes both a DataView and a Uint8Array to make decimal conversions and data copying as fast as possible.

Typed types

The Typescript types of values, TreeView and TreeViewDU are derived from the type arguments. Previously the SSZ type and its Typescript type had no assurance that they were correct and represented the same data. With this guarantee we can now use novel type representations and iterate quickly without breaking any code downstream.

const containerType =  new ContainerType({a: new UintNumberType(8)});
const container = containerType.defaultValue; // automatically typed as {a: number}

Value hash cache

Previously only TreeViews had the ability to cache hashTreeRoot results, so in Lodestar we deserialized Attestations as tree just for that. This version introduces the option cachePermanentRootStruct for composite types which will cache only the root hashTreeRoot (not other intermediary hashes) in the JavaScript object itself. Now we can deserialize Attestations as value and get cached hashTreeRoot in a memory efficient and performant representation.

new ContainerType(fields, {cachePermanentRootStruct: true})

BitArray

BitList and BitVectors are now represented in a new class BitArray that replaces the previous boolean[] representation. Thanks to some optimizations introduced in Lodestar to intersect Uint8Arrays fast it provides the same performance as a boolean[] but at a much lower memory cost. Now in Lodestar we don't need to do the optimization manually and just call

BitArray.intersectValues(committeeIndices);

Pre-computed type data

Many of the types immutable characteristics like minSize, fixedLen, offset positions, etc are now computed in the constructor. This makes consuming this data instant speeding up serialization + de-serialization. Containers also pre-compute offset data and JSON keys data, which help speed up its methods.

type.minSize;

TODO after merge

  • Make subtreeFillToContents not input data, and more benchmarks
  • Add coverage tracker in SSZ
  • Optimize merkelize chunks for hashTreeRoot
  • Optimize hashing of struct values, 30-40% potential improvements to be gained. Matters when hashing the state for the first time.
  • Optimize ArrayBasicView getAll()
  • Optimize and benchmark setNodeAtDepth. Benchmark vs caching gindexstring

@github-actions
Copy link

github-actions bot commented Jan 15, 2022

Performance Report

✔️ no performance regression detected

🚀🚀 Significant benchmark improvement detected

Benchmark suite Current: de84cb6 Previous: 9e5bd76 Ratio
bitlist bytes to struct (120,90) 1.1060 us/op 27.742 us/op 0.04
bitlist bytes to struct (2048,2048) 1.9710 us/op 479.94 us/op 0.00
BasicListType - deserialize 18.573 ms/op 344.65 ms/op 0.05
ByteListType - serialize 17.744 ms/op 55.051 ms/op 0.32
BasicListType - tree_convertToStruct 39.225 ms/op 2.0700 s/op 0.02
epochParticipation len 250000 rws 7813 3.3677 ms/op 24.492 ms/op 0.14
deserialize Attestation - struct 2.9860 us/op 40.024 us/op 0.07
deserialize SignedAggregateAndProof - tree 5.0500 us/op 18.202 us/op 0.28
deserialize SignedAggregateAndProof - struct 4.5790 us/op 45.240 us/op 0.10
deserialize SignedContributionAndProof - struct 3.6400 us/op 38.634 us/op 0.09
BeaconState vc 300000 - deserialize tree 1.0004 s/op 4.2206 s/op 0.24
BeaconState.validators vc 300000 - deserialize tree 925.23 ms/op 3.4065 s/op 0.27
BeaconState.validators vc 300000 - serialize tree 250.87 ms/op 786.99 ms/op 0.32
BeaconState.balances vc 300000 - deserialize tree 39.451 ms/op 208.20 ms/op 0.19
BeaconState.balances vc 300000 - serialize tree 5.6078 ms/op 27.414 ms/op 0.20
BeaconState.previousEpochParticipation vc 300000 - deserialize tree 773.70 us/op 29.516 ms/op 0.03
BeaconState.currentEpochParticipation vc 300000 - deserialize tree 832.81 us/op 28.975 ms/op 0.03
BeaconState.currentEpochParticipation vc 300000 - serialize tree 500.07 us/op 1.7100 ms/op 0.29
BeaconState.inactivityScores vc 300000 - deserialize tree 35.308 ms/op 237.59 ms/op 0.15
BeaconState.inactivityScores vc 300000 - serialize tree 7.2192 ms/op 28.142 ms/op 0.26
basicListValue.readonlyValuesArray() 7.8179 ms/op 197.84 ms/op 0.04
basicListValue.readonlyValuesArray() + loop all 6.8927 ms/op 197.47 ms/op 0.03
compositeListValue.readonlyValuesArray() 47.319 ms/op 219.26 ms/op 0.22
compositeListValue.readonlyValuesArray() + loop all 49.892 ms/op 213.12 ms/op 0.23
Number64UintType - get balances list 1.7187 ms/op 445.42 ms/op 0.00
Number64UintType - set balances list 16.044 ms/op 896.99 ms/op 0.02
Number64UintType - get and increase 10 then set 52.244 ms/op 1.2680 s/op 0.04
Number64UintType - increase 10 using applyDelta 20.417 ms/op 493.79 ms/op 0.04
Number64UintType - increase 10 using applyDeltaInBatch 20.217 ms/op 495.16 ms/op 0.04
Container {a,b,vec} - as tree x100000 705.23 us/op 100.39 ms/op 0.01
Container {a,vec,b} - as tree x100000 840.55 us/op 99.462 ms/op 0.01
aggregationBits binary -> struct 793.00 ns/op 31.011 us/op 0.03
List(uint8) 100000 binary -> struct 1.9377 ms/op 7.6329 ms/op 0.25
List(uint8) 100000 binary -> tree_backed 191.62 us/op 4.2483 ms/op 0.05
List(uint8) 100000 tree_backed -> binary 133.38 us/op 510.19 us/op 0.26
List(uint64Number) 100000 binary -> struct 1.6148 ms/op 79.288 ms/op 0.02
List(uint64Number) 100000 binary -> tree_backed 5.5354 ms/op 46.994 ms/op 0.12
List(Uint64Bigint) 100000 binary -> struct 5.4324 ms/op 46.916 ms/op 0.12
List(Uint64Bigint) 100000 binary -> tree_backed 5.9852 ms/op 45.367 ms/op 0.13
List(Uint64Bigint) 100000 struct -> binary 3.2883 ms/op 18.305 ms/op 0.18
Vector(Root) 100000 binary -> tree_backed 53.990 ms/op 198.87 ms/op 0.27
Vector(Root) 100000 tree_backed -> binary 14.224 ms/op 67.956 ms/op 0.21
List(Validator) 100000 binary -> struct 161.77 ms/op 758.13 ms/op 0.21
List(Validator) 100000 struct -> binary 43.721 ms/op 172.41 ms/op 0.25
List(Validator) 100000 tree_backed -> binary 122.40 ms/op 406.53 ms/op 0.30
List(Validator-NS) 100000 binary -> struct 178.70 ms/op 725.20 ms/op 0.25
List(Validator-NS) 100000 binary -> tree_backed 250.91 ms/op 956.41 ms/op 0.26
List(Validator-NS) 100000 struct -> binary 43.263 ms/op 175.68 ms/op 0.25
List(Validator-NS) 100000 tree_backed -> binary 45.838 ms/op 174.39 ms/op 0.26
Full benchmark results
Benchmark suite Current: de84cb6 Previous: 9e5bd76 Ratio
digestTwoHashObjects 50023 times 57.094 ms/op 56.889 ms/op 1.00
digest64 50023 times 59.441 ms/op 58.827 ms/op 1.01
digest 50023 times 59.570 ms/op 58.814 ms/op 1.01
input length 32 1.6060 us/op 1.5680 us/op 1.02
input length 64 1.7190 us/op 1.7020 us/op 1.01
input length 128 2.9350 us/op 2.8810 us/op 1.02
input length 256 4.2540 us/op 4.2280 us/op 1.01
input length 512 6.9550 us/op 6.8360 us/op 1.02
input length 1024 13.285 us/op 13.169 us/op 1.01
digest 1000000 times 1.0459 s/op 964.09 ms/op 1.08
hashObjectToByteArray 50023 times 2.2518 ms/op 1.9729 ms/op 1.14
byteArrayToHashObject 50023 times 2.7313 ms/op 2.3219 ms/op 1.18
getGindicesAtDepth 6.8120 us/op
iterateAtDepth 14.308 us/op
getGindexBits 631.00 ns/op
gindexIterator 1.4920 us/op
hash 2 Uint8Array 2250026 times 2.7560 s/op
hashTwoObjects 2250026 times 2.5868 s/op
getNodeH() x7812.5 avg hindex 21.616 us/op
getNodeH() x7812.5 index 0 8.2390 us/op
getNodeH() x7812.5 index 7 8.2550 us/op
getNodeH() x7812.5 index 7 with key array 8.2480 us/op
new LeafNode() x7812.5 149.49 us/op
packedRootsBytesToLeafNodes bytes 4000 offset 0 3.8240 us/op
packedRootsBytesToLeafNodes bytes 4000 offset 1 3.8250 us/op
packedRootsBytesToLeafNodes bytes 4000 offset 2 3.8480 us/op
packedRootsBytesToLeafNodes bytes 4000 offset 3 3.8660 us/op
subtreeFillToContents depth 40 count 250000 55.641 ms/op
setRoot - gindexBitstring 16.339 ms/op
setRoot - gindex 16.328 ms/op
getRoot - gindexBitstring 3.5014 ms/op
getRoot - gindex 5.9503 ms/op
getHashObject then setHashObject 18.190 ms/op
setNodeWithFn 16.484 ms/op
getNodeAtDepth depth 0 x100000 2.0573 ms/op
setNodeAtDepth depth 0 x100000 4.6139 ms/op
getNodesAtDepth depth 0 x100000 1.7295 ms/op
setNodesAtDepth depth 0 x100000 2.2167 ms/op
getNodeAtDepth depth 1 x100000 2.1011 ms/op
setNodeAtDepth depth 1 x100000 10.308 ms/op
getNodesAtDepth depth 1 x100000 1.9195 ms/op
setNodesAtDepth depth 1 x100000 8.0093 ms/op
getNodeAtDepth depth 2 x100000 2.5570 ms/op
setNodeAtDepth depth 2 x100000 16.977 ms/op
getNodesAtDepth depth 2 x100000 10.344 ms/op
setNodesAtDepth depth 2 x100000 26.628 ms/op
tree.getNodesAtDepth - gindexes 10.713 ms/op
tree.getNodesAtDepth - push all nodes 2.3089 ms/op
tree.getNodesAtDepth - navigation 336.22 us/op
tree.setNodesAtDepth - indexes 600.45 us/op
set at depth 8 975.00 ns/op
set at depth 16 1.0150 us/op
set at depth 32 1.8520 us/op
iterateNodesAtDepth 8 256 22.247 us/op
getNodesAtDepth 8 256 5.5530 us/op
iterateNodesAtDepth 16 65536 6.3586 ms/op
getNodesAtDepth 16 65536 2.2128 ms/op
iterateNodesAtDepth 32 250000 23.765 ms/op
getNodesAtDepth 32 250000 6.2500 ms/op
iterateNodesAtDepth 40 250000 23.564 ms/op
getNodesAtDepth 40 250000 7.2114 ms/op
250k validators 3.0478 s/op
bitlist bytes to struct (120,90) 1.1060 us/op 27.742 us/op 0.04
bitlist bytes to tree (120,90) 4.7910 us/op 2.2150 us/op 2.16
bitlist bytes to struct (2048,2048) 1.9710 us/op 479.94 us/op 0.00
bitlist bytes to tree (2048,2048) 7.7280 us/op 7.6020 us/op 1.02
ByteListType - deserialize 16.883 ms/op 10.002 ms/op 1.69
BasicListType - deserialize 18.573 ms/op 344.65 ms/op 0.05
ByteListType - serialize 17.744 ms/op 55.051 ms/op 0.32
BasicListType - serialize 20.305 ms/op 57.725 ms/op 0.35
BasicListType - tree_convertToStruct 39.225 ms/op 2.0700 s/op 0.02
List[uint8, 68719476736] len 300000 ViewDU.getAll() + iterate 5.7774 ms/op
List[uint8, 68719476736] len 300000 ViewDU.get(i) 6.2128 ms/op
Array.push len 300000 empty Array - number 8.2946 ms/op
Array.set len 300000 from new Array - number 2.2864 ms/op
Array.set len 300000 - number 9.7420 ms/op
Uint8Array.set len 300000 285.43 us/op
Uint32Array.set len 300000 498.18 us/op
Container({a: uint8, b: uint8}) getViewDU x300000 41.203 ms/op
ContainerNodeStruct({a: uint8, b: uint8}) getViewDU x300000 14.619 ms/op
List(Container) len 300000 ViewDU.getAllReadonly() + iterate 364.68 ms/op
List(Container) len 300000 ViewDU.getAllReadonlyValues() + iterate 393.64 ms/op
List(Container) len 300000 ViewDU.get(i) 12.124 ms/op
List(Container) len 300000 ViewDU.getReadonly(i) 11.227 ms/op
List(ContainerNodeStruct) len 300000 ViewDU.getAllReadonly() + iterate 64.816 ms/op
List(ContainerNodeStruct) len 300000 ViewDU.getAllReadonlyValues() + iterate 8.5805 ms/op
List(ContainerNodeStruct) len 300000 ViewDU.get(i) 10.277 ms/op
List(ContainerNodeStruct) len 300000 ViewDU.getReadonly(i) 9.9631 ms/op
Array.push len 300000 empty Array - object 7.4819 ms/op
Array.set len 300000 from new Array - object 2.3218 ms/op
Array.set len 300000 - object 7.6733 ms/op
cachePermanentRootStruct no cache 12.552 us/op
cachePermanentRootStruct with cache 460.00 ns/op
epochParticipation len 250000 rws 7813 3.3677 ms/op 24.492 ms/op 0.14
deserialize Attestation - tree 4.3050 us/op 12.260 us/op 0.35
deserialize Attestation - struct 2.9860 us/op 40.024 us/op 0.07
deserialize SignedAggregateAndProof - tree 5.0500 us/op 18.202 us/op 0.28
deserialize SignedAggregateAndProof - struct 4.5790 us/op 45.240 us/op 0.10
deserialize SyncCommitteeMessage - tree 1.6020 us/op
deserialize SyncCommitteeMessage - struct 1.5730 us/op
deserialize SignedContributionAndProof - tree 3.0710 us/op
deserialize SignedContributionAndProof - struct 3.6400 us/op 38.634 us/op 0.09
deserialize SignedBeaconBlock - tree 326.27 us/op
deserialize SignedBeaconBlock - struct 200.80 us/op
BeaconState vc 300000 - deserialize tree 1.0004 s/op 4.2206 s/op 0.24
BeaconState vc 300000 - serialize tree 319.91 ms/op 930.88 ms/op 0.34
BeaconState.historicalRoots vc 300000 - deserialize tree 1.2080 us/op 1.1070 us/op 1.09
BeaconState.historicalRoots vc 300000 - serialize tree 1.3660 us/op 3.9520 us/op 0.35
BeaconState.validators vc 300000 - deserialize tree 925.23 ms/op 3.4065 s/op 0.27
BeaconState.validators vc 300000 - serialize tree 250.87 ms/op 786.99 ms/op 0.32
BeaconState.balances vc 300000 - deserialize tree 39.451 ms/op 208.20 ms/op 0.19
BeaconState.balances vc 300000 - serialize tree 5.6078 ms/op 27.414 ms/op 0.20
BeaconState.previousEpochParticipation vc 300000 - deserialize tree 773.70 us/op 29.516 ms/op 0.03
BeaconState.previousEpochParticipation vc 300000 - serialize tree 508.26 us/op 1.5227 ms/op 0.33
BeaconState.currentEpochParticipation vc 300000 - deserialize tree 832.81 us/op 28.975 ms/op 0.03
BeaconState.currentEpochParticipation vc 300000 - serialize tree 500.07 us/op 1.7100 ms/op 0.29
BeaconState.inactivityScores vc 300000 - deserialize tree 35.308 ms/op 237.59 ms/op 0.15
BeaconState.inactivityScores vc 300000 - serialize tree 7.2192 ms/op 28.142 ms/op 0.26
hashTreeRoot Attestation - struct 53.856 us/op
hashTreeRoot Attestation - tree 25.238 us/op
hashTreeRoot SignedAggregateAndProof - struct 66.949 us/op
hashTreeRoot SignedAggregateAndProof - tree 38.257 us/op
hashTreeRoot SyncCommitteeMessage - struct 15.811 us/op
hashTreeRoot SyncCommitteeMessage - tree 8.6990 us/op
hashTreeRoot SignedContributionAndProof - struct 44.008 us/op
hashTreeRoot SignedContributionAndProof - tree 25.602 us/op
hashTreeRoot SignedBeaconBlock - struct 3.5065 ms/op
hashTreeRoot SignedBeaconBlock - tree 2.4071 ms/op
hashTreeRoot Validator - struct 19.466 us/op
hashTreeRoot Validator - tree 16.375 us/op
BeaconState vc 300000 - hashTreeRoot tree 4.9859 s/op
BeaconState.historicalRoots vc 300000 - hashTreeRoot tree 2.1830 us/op
BeaconState.validators vc 300000 - hashTreeRoot tree 4.7268 s/op
BeaconState.balances vc 300000 - hashTreeRoot tree 127.74 ms/op
BeaconState.previousEpochParticipation vc 300000 - hashTreeRoot tree 12.336 ms/op
BeaconState.currentEpochParticipation vc 300000 - hashTreeRoot tree 12.467 ms/op
BeaconState.inactivityScores vc 300000 - hashTreeRoot tree 117.12 ms/op
hash64 x18 26.269 us/op
hashTwoObjects x18 23.541 us/op
hash64 x1740 2.4987 ms/op
hashTwoObjects x1740 2.2250 ms/op
hash64 x2700000 3.8109 s/op
hashTwoObjects x2700000 3.4523 s/op
get_exitEpoch - ContainerType 471.00 ns/op 612.00 ns/op 0.77
get_exitEpoch - ContainerNodeStructType 406.00 ns/op
set_exitEpoch - ContainerType 456.00 ns/op 641.00 ns/op 0.71
set_exitEpoch - ContainerNodeStructType 422.00 ns/op
get_pubkey - ContainerType 1.6620 us/op 1.8430 us/op 0.90
get_pubkey - ContainerNodeStructType 432.00 ns/op
hashTreeRoot - ContainerType 881.00 ns/op 730.00 ns/op 1.21
hashTreeRoot - ContainerNodeStructType 918.00 ns/op
createProof - ContainerType 7.5280 us/op 6.9480 us/op 1.08
createProof - ContainerNodeStructType 35.992 us/op
serialize - ContainerType 2.9110 us/op 5.8640 us/op 0.50
serialize - ContainerNodeStructType 2.2310 us/op
set_exitEpoch_and_hashTreeRoot - ContainerType 6.3180 us/op 4.8340 us/op 1.31
set_exitEpoch_and_hashTreeRoot - ContainerNodeStructType 16.205 us/op
Array - for of 10.626 us/op 10.585 us/op 1.00
Array - for(;;) 7.2400 us/op 7.1550 us/op 1.01
basicListValue.readonlyValuesArray() 7.8179 ms/op 197.84 ms/op 0.04
basicListValue.readonlyValuesArray() + loop all 6.8927 ms/op 197.47 ms/op 0.03
compositeListValue.readonlyValuesArray() 47.319 ms/op 219.26 ms/op 0.22
compositeListValue.readonlyValuesArray() + loop all 49.892 ms/op 213.12 ms/op 0.23
Number64UintType - get balances list 1.7187 ms/op 445.42 ms/op 0.00
Number64UintType - set balances list 16.044 ms/op 896.99 ms/op 0.02
Number64UintType - get and increase 10 then set 52.244 ms/op 1.2680 s/op 0.04
Number64UintType - increase 10 using applyDelta 20.417 ms/op 493.79 ms/op 0.04
Number64UintType - increase 10 using applyDeltaInBatch 20.217 ms/op 495.16 ms/op 0.04
tree_newTreeFromUint64Deltas 38.113 ms/op 48.712 ms/op 0.78
unsafeUint8ArrayToTree 49.935 ms/op 52.198 ms/op 0.96
bitLength(50) 498.00 ns/op
bitLengthStr(50) 679.00 ns/op
bitLength(8000) 506.00 ns/op
bitLengthStr(8000) 838.00 ns/op
bitLength(250000) 512.00 ns/op
bitLengthStr(250000) 905.00 ns/op
floor - Math.floor (53) 0.67034 ns/op
floor - << 0 (53) 0.50274 ns/op
floor - Math.floor (512) 0.66999 ns/op
floor - << 0 (512) 0.50290 ns/op
fnIf(0) 2.6780 ns/op
fnSwitch(0) 3.6828 ns/op
fnObj(0) 0.67030 ns/op
fnArr(0) 0.66978 ns/op
fnIf(4) 4.3520 ns/op
fnSwitch(4) 5.0228 ns/op
fnObj(4) 0.67019 ns/op
fnArr(4) 0.67007 ns/op
fnIf(9) 6.6961 ns/op
fnSwitch(9) 6.6970 ns/op
fnObj(9) 0.67005 ns/op
fnArr(9) 0.66988 ns/op
Container {a,b,vec} - as struct x100000 50.716 us/op 67.495 us/op 0.75
Container {a,b,vec} - as tree x100000 705.23 us/op 100.39 ms/op 0.01
Container {a,vec,b} - as struct x100000 100.98 us/op 100.95 us/op 1.00
Container {a,vec,b} - as tree x100000 840.55 us/op 99.462 ms/op 0.01
get 2 props x1000000 - rawObject 335.30 us/op
get 2 props x1000000 - proxy 117.72 ms/op
get 2 props x1000000 - customObj 335.73 us/op
Simple object binary -> struct 973.00 ns/op 2.2020 us/op 0.44
Simple object binary -> tree_backed 2.4950 us/op 3.1860 us/op 0.78
Simple object struct -> tree_backed 3.3060 us/op 1.8810 us/op 1.76
Simple object tree_backed -> struct 2.7980 us/op 2.3610 us/op 1.19
Simple object struct -> binary 1.4560 us/op 2.0180 us/op 0.72
Simple object tree_backed -> binary 2.6630 us/op 2.4840 us/op 1.07
aggregationBits binary -> struct 793.00 ns/op 31.011 us/op 0.03
aggregationBits binary -> tree_backed 2.8230 us/op 2.3410 us/op 1.21
aggregationBits struct -> tree_backed 3.7310 us/op 4.9610 us/op 0.75
aggregationBits tree_backed -> struct 1.6340 us/op 5.1430 us/op 0.32
aggregationBits struct -> binary 1.3630 us/op 3.7070 us/op 0.37
aggregationBits tree_backed -> binary 1.5140 us/op 3.8140 us/op 0.40
List(uint8) 100000 binary -> struct 1.9377 ms/op 7.6329 ms/op 0.25
List(uint8) 100000 binary -> tree_backed 191.62 us/op 4.2483 ms/op 0.05
List(uint8) 100000 struct -> tree_backed 2.1738 ms/op 4.0492 ms/op 0.54
List(uint8) 100000 tree_backed -> struct 1.4057 ms/op 4.1002 ms/op 0.34
List(uint8) 100000 struct -> binary 1.9564 ms/op 672.09 us/op 2.91
List(uint8) 100000 tree_backed -> binary 133.38 us/op 510.19 us/op 0.26
List(uint64Number) 100000 binary -> struct 1.6148 ms/op 79.288 ms/op 0.02
List(uint64Number) 100000 binary -> tree_backed 5.5354 ms/op 46.994 ms/op 0.12
List(uint64Number) 100000 struct -> tree_backed 8.9925 ms/op 18.907 ms/op 0.48
List(uint64Number) 100000 tree_backed -> struct 2.7039 ms/op 18.859 ms/op 0.14
List(uint64Number) 100000 struct -> binary 2.3181 ms/op 2.7965 ms/op 0.83
List(uint64Number) 100000 tree_backed -> binary 1.5627 ms/op 4.6719 ms/op 0.33
List(Uint64Bigint) 100000 binary -> struct 5.4324 ms/op 46.916 ms/op 0.12
List(Uint64Bigint) 100000 binary -> tree_backed 5.9852 ms/op 45.367 ms/op 0.13
List(Uint64Bigint) 100000 struct -> tree_backed 8.8684 ms/op 35.182 ms/op 0.25
List(Uint64Bigint) 100000 tree_backed -> struct 7.2704 ms/op 35.848 ms/op 0.20
List(Uint64Bigint) 100000 struct -> binary 3.2883 ms/op 18.305 ms/op 0.18
List(Uint64Bigint) 100000 tree_backed -> binary 1.6218 ms/op 4.2373 ms/op 0.38
Vector(Root) 100000 binary -> struct 46.628 ms/op 96.033 ms/op 0.49
Vector(Root) 100000 binary -> tree_backed 53.990 ms/op 198.87 ms/op 0.27
Vector(Root) 100000 struct -> tree_backed 66.370 ms/op 178.92 ms/op 0.37
Vector(Root) 100000 tree_backed -> struct 72.321 ms/op 184.79 ms/op 0.39
Vector(Root) 100000 struct -> binary 2.9302 ms/op 3.7210 ms/op 0.79
Vector(Root) 100000 tree_backed -> binary 14.224 ms/op 67.956 ms/op 0.21
List(Validator) 100000 binary -> struct 161.77 ms/op 758.13 ms/op 0.21
List(Validator) 100000 binary -> tree_backed 476.90 ms/op 1.2064 s/op 0.40
List(Validator) 100000 struct -> tree_backed 532.47 ms/op 932.94 ms/op 0.57
List(Validator) 100000 tree_backed -> struct 282.70 ms/op 953.30 ms/op 0.30
List(Validator) 100000 struct -> binary 43.721 ms/op 172.41 ms/op 0.25
List(Validator) 100000 tree_backed -> binary 122.40 ms/op 406.53 ms/op 0.30
List(Validator-NS) 100000 binary -> struct 178.70 ms/op 725.20 ms/op 0.25
List(Validator-NS) 100000 binary -> tree_backed 250.91 ms/op 956.41 ms/op 0.26
List(Validator-NS) 100000 struct -> tree_backed 321.94 ms/op 62.101 ms/op 5.18
List(Validator-NS) 100000 tree_backed -> struct 260.67 ms/op 60.112 ms/op 4.34
List(Validator-NS) 100000 struct -> binary 43.263 ms/op 175.68 ms/op 0.25
List(Validator-NS) 100000 tree_backed -> binary 45.838 ms/op 174.39 ms/op 0.26
get epochStatuses - MutableVector 116.77 us/op
get epochStatuses - ViewDU 282.85 us/op
set epochStatuses - ListTreeView 2.4737 ms/op
set epochStatuses - ListTreeView - set() 612.61 us/op
set epochStatuses - ListTreeView - commit() 822.36 us/op
bitstring 812.44 ns/op
bit mask 23.194 ns/op
struct - increase slot to 1000000 2.1850 ms/op 2.6831 ms/op 0.81
UintNumberType - increase slot to 1000000 41.324 ms/op
UintBigintType - increase slot to 1000000 657.97 ms/op
UintBigint8 x 100000 tree_deserialize 6.7927 ms/op
UintBigint8 x 100000 tree_serialize 1.1216 ms/op
UintBigint16 x 100000 tree_deserialize 6.3678 ms/op
UintBigint16 x 100000 tree_serialize 1.7710 ms/op
UintBigint32 x 100000 tree_deserialize 7.7172 ms/op
UintBigint32 x 100000 tree_serialize 1.8666 ms/op
UintBigint64 x 100000 tree_deserialize 9.6010 ms/op
UintBigint64 x 100000 tree_serialize 2.4927 ms/op
UintBigint8 x 100000 value_deserialize 804.36 us/op
UintBigint8 x 100000 value_serialize 1.2342 ms/op
UintBigint16 x 100000 value_deserialize 843.19 us/op
UintBigint16 x 100000 value_serialize 1.3009 ms/op
UintBigint32 x 100000 value_deserialize 737.90 us/op
UintBigint32 x 100000 value_serialize 1.3921 ms/op
UintBigint64 x 100000 value_deserialize 844.32 us/op
UintBigint64 x 100000 value_serialize 1.6254 ms/op
UintBigint8 x 100000 deserialize 8.2253 ms/op
UintBigint8 x 100000 serialize 2.1226 ms/op
UintBigint16 x 100000 deserialize 8.2142 ms/op
UintBigint16 x 100000 serialize 2.2335 ms/op
UintBigint32 x 100000 deserialize 9.1566 ms/op
UintBigint32 x 100000 serialize 4.5640 ms/op
UintBigint64 x 100000 deserialize 6.1036 ms/op
UintBigint64 x 100000 serialize 2.2397 ms/op
UintBigint128 x 100000 deserialize 8.4725 ms/op
UintBigint128 x 100000 serialize 24.217 ms/op
UintBigint256 x 100000 deserialize 16.098 ms/op
UintBigint256 x 100000 serialize 86.946 ms/op
Slice from Uint8Array x25000 1.6476 ms/op
Slice from ArrayBuffer x25000 29.871 ms/op
Slice from ArrayBuffer x25000 + new Uint8Array 30.715 ms/op
Copy Uint8Array 100000 iterate 958.71 us/op
Copy Uint8Array 100000 slice 137.85 us/op
Copy Uint8Array 100000 slice arrayBuffer 141.54 us/op
Uint64 deserialize 100000 - iterate Uint8Array 2.2253 ms/op
Uint64 deserialize 100000 - by Uint32A 2.1806 ms/op
Uint64 deserialize 100000 - by DataView.getUint32 x2 2.1298 ms/op
Uint64 deserialize 100000 - by DataView.getBigUint64 6.8588 ms/op
Uint64 deserialize 100000 - by byte 69.344 ms/op

by benchmarkbot/action

packages/persistent-merkle-tree/src/index.ts Show resolved Hide resolved
Comment on lines +403 to +512
return startIndex === 0 ? [rootNode.left] : [rootNode.right];
} else {
return [rootNode.left, rootNode.right];
Copy link
Member

Choose a reason for hiding this comment

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

These are a little optimistic, no? should have more validation of count and startIndex

some test cases that should error:
getNodesAtDepth(node, 1, 5, 1) - startIndex > maximum index at depth
getNodesAtDepth(node, 1, 0, 4) - count > maximum count at depth
getNodesAtDepth(node, 1, 1, 2) - startIndex + count > maximum index at depth

previously caught with this condition:

 if (BigInt(1) << BigInt(depth) < startIndex + count) {
      throw new Error(ERR_COUNT_GT_DEPTH);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah this function is optimistic, as well as setNodesAtDepth where you must pass data sorted, and perfectly arranged, or the result is wrong in hard to notice ways. This methods are meant to achieve the max performance possible. getNodesAtDepth is a pretty hot path when deserializing a full state. Do you really think it should protect against the test cases you listed above?

packages/persistent-merkle-tree/src/tree.ts Outdated Show resolved Hide resolved
packages/persistent-merkle-tree/src/tree.ts Outdated Show resolved Hide resolved
packages/ssz/src/README.md Outdated Show resolved Hide resolved
packages/ssz/src/type/composite.ts Outdated Show resolved Hide resolved
packages/ssz/src/type/composite.ts Outdated Show resolved Hide resolved
packages/ssz/src/type/container.ts Outdated Show resolved Hide resolved
packages/ssz/src/util/merkleize.ts Outdated Show resolved Hide resolved
packages/ssz/src/util/zeros.ts Outdated Show resolved Hide resolved
packages/ssz/src/type/byteVector.ts Show resolved Hide resolved
packages/ssz/src/type/union.ts Outdated Show resolved Hide resolved
@dapplion dapplion force-pushed the dapplion/v2 branch 2 times, most recently from 94f8749 to 1bf9b1c Compare February 12, 2022 10:43
Replace lodestarTypes

Update tests

Add Union and None types

Update spec tests

Remove old src and lodestar dependencies

Improve JSON parsing for tests

Load YAML with lodestar utils schema

Commit ArrayComposite before getAll

Re-write toJson fromJson test

Fix uint json conversions

Print tree in valid test if requested

Fix hashTreeRoot for ByteList

Add RENDER_ROOTS option

Fix typos

Use numerical sort in array commit

Fix value_serializedSizeArrayComposite

Pass ssz_generic tests

Allow to select tests to run in ssz_static

Fix container bytes offset

Rename fixedLen to fixedSize

Sort deserailization methods

Print json stringified in test

Review logic

Clean up tests

Define JSON casing at constructor time only

Add casing maps for merge types

Update casing in unit tests

Re-organize utils

Fix merge casing

FIx offset calculation

Pass all spec test pre-merge

Bump merge test

Extend timeout for mainnet tests

Pass all unit tests

Return defaultValue in simpleserialize random

Remove @chainsafe/lodestar-spec-test-util dependency

Copy yaml schema from lodestar-utils

Fix benchmark type issues

Skip createProof benchmark

Skip old benchmark without runner

Remove postinstall script

Re-add UintBigint optimization

Fix set_exitEpoch_and_hashTreeRoot benchmark

Add List of Number benchmark

Use DataViews for faster deserialization

Use DataViews for tree serialization too

Update workflows build after install

Update packedNode tests

Review Tree API

Validate length in ByteArrays

FIx benchmarks in persistent-merkle-tree

Simplify LeafNode constructor

Refactor subtreeFillToContents

Run struct <-> tree_backed benchmarks

Update test types

Fix Uint64 DataView benchmarks

Add benchmarks for full state serialization

Use consistent initialization in DataView

Optimize toView for ByteArray

Add clone method for safer ContainerTreeViewDU

Add documentation to all public methods

Update SSZ README

Simplify testTypes

Update persistent-merkle-tree README

Add unit test push x5

Fix heigh typo

Add note Supports index up to Number.MAX_SAFE_INTEGER.

Address PR comments

Add more benchmarks (#227)

Remove merkleizeSingleBuff

Add more documentation

Add typeName to all containers

Add eth2 JSON casing

Rename merge to bellatrix

Fix missing renames of merge to bellatrix

Update ssz readme

Export byteArrayEquals

Add more BitArray functionality

Various fixes

Test type UX of allForks types

Abstract logic into BitArray and ByteArray

Add .equals functionality

Don't run allForks code

Rename receiptRoot

Update ssz docstrings

Multiple fixes

Remove non-existent variable

Add proof api

Fix int conversion in node.getUintBigint

Add Proofs functionality

Run Proof tests on ssz_static minimal

Add length node for proofs of lists

Require rootNode only when necessary in Array proofs

Fix rebase issues

Move Node navigation to functions

Add treeZeroAfterIndex fn

Add sliceTo method in CompositeList ViewDU

Use Uint64NumInf only where necessary

Add Eth1Block type

Use defaultView and defaultViewDU methods

Support BranchNodeStruct in proofs

Run proof tests

Post process proof nodes

Fix proof generation for all types

Fix ContainerNodeStructType proof unit test

Stop tracking complimentary benchmarks

Fix typo in Union maxSize

Add test for maxSize and minSize for all data structures

Export helper functions

Fix length param in Vector

Prevent setting values in ViewDU beyond length

Add more test cases

Improve ListBasicTreeViewDU.push logic

Fix Composite ViewDU commit nodes logic

Fix cache logic in ViewDU

Use type guards and simply set composite

Fix allForks test

Fix recursive call in BitArray

Ensure data consistency in ArrayCompositeViewDU

Better error in BitArray.set

FIx typo in populateAllNodes

Add more BitArray unit tests

TreeViewDU.commit should not return node

Better error messages in fromHexString

Auto-commit on ViewDU

Fix perf types

Fix proof generation for Validator type

Benchmark more eth2 ssz objects

Track raw cost of hashing vc list

Add more coverage WIP

More coverage WIP

Prevent pushing over length

More coverage

Ensure consistent mutability behaviour

Ensure correct mutability in ContainerNodeStruct

Move isViewMutable to CompositeType

Prevent keeping references for immutable views

Fix ContainerNodeStruct valid tests

Fix types in Uint constructor

Increase bitArray coverage

Guard against new BitList checks

Fix readVariableOffsetsArrayComposite
@dapplion
Copy link
Contributor Author

I've squashed all commits into one for better visibility of this PR's comments and reviews. All commits will be squashed on merge so it's not important to preserve git history of this branch at this point

@philknows
Copy link
Member

Is this still getting merged in @dapplion as discussed on the Mar 18 planning call?

@dapplion
Copy link
Contributor Author

Is this still getting merged in @dapplion as discussed on the Mar 18 planning call?

Yes @wemeetagain should help merge and release this

@wemeetagain wemeetagain merged commit b0e9999 into master Mar 24, 2022
@wemeetagain wemeetagain deleted the dapplion/v2 branch March 24, 2022 14:42
@wemeetagain wemeetagain restored the dapplion/v2 branch March 24, 2022 15:06
wemeetagain pushed a commit that referenced this pull request Mar 24, 2022
BREAKING CHANGE: complete refactor, see packages/ssz/README.md for details

Initial squashed v2 commit

Replace lodestarTypes

Update tests

Add Union and None types

Update spec tests

Remove old src and lodestar dependencies

Improve JSON parsing for tests

Load YAML with lodestar utils schema

Commit ArrayComposite before getAll

Re-write toJson fromJson test

Fix uint json conversions

Print tree in valid test if requested

Fix hashTreeRoot for ByteList

Add RENDER_ROOTS option

Fix typos

Use numerical sort in array commit

Fix value_serializedSizeArrayComposite

Pass ssz_generic tests

Allow to select tests to run in ssz_static

Fix container bytes offset

Rename fixedLen to fixedSize

Sort deserailization methods

Print json stringified in test

Review logic

Clean up tests

Define JSON casing at constructor time only

Add casing maps for merge types

Update casing in unit tests

Re-organize utils

Fix merge casing

FIx offset calculation

Pass all spec test pre-merge

Bump merge test

Extend timeout for mainnet tests

Pass all unit tests

Return defaultValue in simpleserialize random

Remove @chainsafe/lodestar-spec-test-util dependency

Copy yaml schema from lodestar-utils

Fix benchmark type issues

Skip createProof benchmark

Skip old benchmark without runner

Remove postinstall script

Re-add UintBigint optimization

Fix set_exitEpoch_and_hashTreeRoot benchmark

Add List of Number benchmark

Use DataViews for faster deserialization

Use DataViews for tree serialization too

Update workflows build after install

Update packedNode tests

Review Tree API

Validate length in ByteArrays

FIx benchmarks in persistent-merkle-tree

Simplify LeafNode constructor

Refactor subtreeFillToContents

Run struct <-> tree_backed benchmarks

Update test types

Fix Uint64 DataView benchmarks

Add benchmarks for full state serialization

Use consistent initialization in DataView

Optimize toView for ByteArray

Add clone method for safer ContainerTreeViewDU

Add documentation to all public methods

Update SSZ README

Simplify testTypes

Update persistent-merkle-tree README

Add unit test push x5

Fix heigh typo

Add note Supports index up to Number.MAX_SAFE_INTEGER.

Address PR comments

Add more benchmarks (#227)

Remove merkleizeSingleBuff

Add more documentation

Add typeName to all containers

Add eth2 JSON casing

Rename merge to bellatrix

Fix missing renames of merge to bellatrix

Update ssz readme

Export byteArrayEquals

Add more BitArray functionality

Various fixes

Test type UX of allForks types

Abstract logic into BitArray and ByteArray

Add .equals functionality

Don't run allForks code

Rename receiptRoot

Update ssz docstrings

Multiple fixes

Remove non-existent variable

Add proof api

Fix int conversion in node.getUintBigint

Add Proofs functionality

Run Proof tests on ssz_static minimal

Add length node for proofs of lists

Require rootNode only when necessary in Array proofs

Fix rebase issues

Move Node navigation to functions

Add treeZeroAfterIndex fn

Add sliceTo method in CompositeList ViewDU

Use Uint64NumInf only where necessary

Add Eth1Block type

Use defaultView and defaultViewDU methods

Support BranchNodeStruct in proofs

Run proof tests

Post process proof nodes

Fix proof generation for all types

Fix ContainerNodeStructType proof unit test

Stop tracking complimentary benchmarks

Fix typo in Union maxSize

Add test for maxSize and minSize for all data structures

Export helper functions

Fix length param in Vector

Prevent setting values in ViewDU beyond length

Add more test cases

Improve ListBasicTreeViewDU.push logic

Fix Composite ViewDU commit nodes logic

Fix cache logic in ViewDU

Use type guards and simply set composite

Fix allForks test

Fix recursive call in BitArray

Ensure data consistency in ArrayCompositeViewDU

Better error in BitArray.set

FIx typo in populateAllNodes

Add more BitArray unit tests

TreeViewDU.commit should not return node

Better error messages in fromHexString

Auto-commit on ViewDU

Fix perf types

Fix proof generation for Validator type

Benchmark more eth2 ssz objects

Track raw cost of hashing vc list

Add more coverage WIP

More coverage WIP

Prevent pushing over length

More coverage

Ensure consistent mutability behaviour

Ensure correct mutability in ContainerNodeStruct

Move isViewMutable to CompositeType

Prevent keeping references for immutable views

Fix ContainerNodeStruct valid tests

Fix types in Uint constructor

Increase bitArray coverage

Guard against new BitList checks

Fix readVariableOffsetsArrayComposite

Fix rebase issues

WIP - hashTreeRoot perf

Pre-allocate array if length is known

Use Uint32Array in offsets array

Expose faster ArrayLike vectors for view.getAll()

Allow to customize Array typename

Add more array perf tests

Remove ArrayLikeWritable

Increase coverage

Modularize TreeView and TreeViewDU

More coverage

Remove rebindLeft rebindRight methods

Add more coverage

Organize tests

Set name to Container tree view instances

export CompositeTypeAny

Refactor default* getters to methods
@dapplion dapplion mentioned this pull request Apr 13, 2022
@dapplion dapplion mentioned this pull request Jul 11, 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.

None yet

5 participants