Skip to content

Commit

Permalink
feat: compact multiproof (#292)
Browse files Browse the repository at this point in the history
* feat: add dynamic multiproofs

* Fix logic and rename to compact proof

* Add perf tests

* Fix benchmark

* Update packages/persistent-merkle-tree/src/proof/compactMulti.ts

Co-authored-by: Lion - dapplion <35266934+dapplion@users.noreply.github.com>

* Fix linter errors

* PR review

Co-authored-by: Lion - dapplion <35266934+dapplion@users.noreply.github.com>
  • Loading branch information
wemeetagain and dapplion authored Jan 6, 2023
1 parent 7bd63c6 commit 5f1ea99
Show file tree
Hide file tree
Showing 6 changed files with 309 additions and 12 deletions.
151 changes: 151 additions & 0 deletions packages/persistent-merkle-tree/src/proof/compactMulti.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import {convertGindexToBitstring, Gindex, GindexBitstring} from "../gindex";
import {BranchNode, LeafNode, Node} from "../node";
import {computeProofBitstrings} from "./util";

export function computeDescriptor(indices: Gindex[]): Uint8Array {
// include all helper indices
const proofBitstrings = new Set<GindexBitstring>();
const pathBitstrings = new Set<GindexBitstring>();
for (const leafIndex of indices) {
const leafBitstring = convertGindexToBitstring(leafIndex);
proofBitstrings.add(leafBitstring);
const {branch, path} = computeProofBitstrings(leafBitstring);
path.delete(leafBitstring);
for (const pathIndex of path) {
pathBitstrings.add(pathIndex);
}
for (const branchIndex of branch) {
proofBitstrings.add(branchIndex);
}
}
for (const pathIndex of pathBitstrings) {
proofBitstrings.delete(pathIndex);
}

// sort gindex bitstrings in-order
const allBitstringsSorted = Array.from(proofBitstrings).sort((a, b) => a.localeCompare(b));

// convert gindex bitstrings into descriptor bitstring
let descriptorBitstring = "";
for (const gindexBitstring of allBitstringsSorted) {
for (let i = 0; i < gindexBitstring.length; i++) {
if (gindexBitstring[gindexBitstring.length - 1 - i] === "1") {
descriptorBitstring += "1".padStart(i + 1, "0");
break;
}
}
}

// append zero bits to byte-alignt
if (descriptorBitstring.length % 8 != 0) {
descriptorBitstring = descriptorBitstring.padEnd(
8 - (descriptorBitstring.length % 8) + descriptorBitstring.length,
"0"
);
}

// convert descriptor bitstring to bytes
const descriptor = new Uint8Array(descriptorBitstring.length / 8);
for (let i = 0; i < descriptor.length; i++) {
descriptor[i] = Number("0b" + descriptorBitstring.substring(i * 8, (i + 1) * 8));
}
return descriptor;
}

function getBit(bitlist: Uint8Array, bitIndex: number): boolean {
const bit = bitIndex % 8;
const byteIdx = Math.floor(bitIndex / 8);
const byte = bitlist[byteIdx];
switch (bit) {
case 0:
return (byte & 0b1000_0000) !== 0;
case 1:
return (byte & 0b0100_0000) !== 0;
case 2:
return (byte & 0b0010_0000) !== 0;
case 3:
return (byte & 0b0001_0000) !== 0;
case 4:
return (byte & 0b0000_1000) !== 0;
case 5:
return (byte & 0b0000_0100) !== 0;
case 6:
return (byte & 0b0000_0010) !== 0;
case 7:
return (byte & 0b0000_0001) !== 0;
default:
throw new Error("unreachable");
}
}

export function descriptorToBitlist(descriptor: Uint8Array): boolean[] {
const bools: boolean[] = [];
const maxBitLength = descriptor.length * 8;
let count0 = 0;
let count1 = 0;
for (let i = 0; i < maxBitLength; i++) {
const bit = getBit(descriptor, i);
bools.push(bit);
if (bit) {
count1++;
} else {
count0++;
}
if (count1 > count0) {
i++;
if (i + 7 < maxBitLength) {
throw new Error("Invalid descriptor: too many bytes");
}
for (; i < maxBitLength; i++) {
const bit = getBit(descriptor, i);
if (bit) {
throw new Error("Invalid descriptor: too many 1 bits");
}
}
return bools;
}
}
throw new Error("Invalid descriptor: not enough 1 bits");
}

export function nodeToCompactMultiProof(node: Node, bitlist: boolean[], bitIndex: number): Uint8Array[] {
if (bitlist[bitIndex]) {
return [node.root];
} else {
const left = nodeToCompactMultiProof(node.left, bitlist, bitIndex + 1);
const right = nodeToCompactMultiProof(node.right, bitlist, bitIndex + left.length * 2);
return [...left, ...right];
}
}

/**
* Create a Node given a validated bitlist, leaves, and a pointer into the bitlist and leaves
*
* Recursive definition
*/
export function compactMultiProofToNode(
bitlist: boolean[],
leaves: Uint8Array[],
pointer: {bitIndex: number; leafIndex: number}
): Node {
if (bitlist[pointer.bitIndex++]) {
return LeafNode.fromRoot(leaves[pointer.leafIndex++]);
} else {
return new BranchNode(
compactMultiProofToNode(bitlist, leaves, pointer),
compactMultiProofToNode(bitlist, leaves, pointer)
);
}
}

export function createCompactMultiProof(rootNode: Node, descriptor: Uint8Array): Uint8Array[] {
return nodeToCompactMultiProof(rootNode, descriptorToBitlist(descriptor), 0);
}

export function createNodeFromCompactMultiProof(leaves: Uint8Array[], descriptor: Uint8Array): Node {
const bools = descriptorToBitlist(descriptor);
if (bools.length !== leaves.length * 2 - 1) {
throw new Error("Invalid multiproof: invalid number of leaves");
}
return compactMultiProofToNode(bools, leaves, {bitIndex: 0, leafIndex: 0});
}
30 changes: 28 additions & 2 deletions packages/persistent-merkle-tree/src/proof/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {Gindex} from "../gindex";
import {Node} from "../node";
import {createMultiProof, createNodeFromMultiProof} from "./multi";
import {createNodeFromCompactMultiProof, createCompactMultiProof} from "./compactMulti";
import {createNodeFromSingleProof, createSingleProof} from "./single";
import {
computeTreeOffsetProofSerializedLength,
Expand All @@ -10,10 +11,13 @@ import {
serializeTreeOffsetProof,
} from "./treeOffset";

export {computeDescriptor, descriptorToBitlist} from "./compactMulti";

export enum ProofType {
single = "single",
treeOffset = "treeOffset",
multi = "multi",
compactMulti = "compactMulti",
}

/**
Expand All @@ -23,6 +27,7 @@ export const ProofTypeSerialized = [
ProofType.single, // 0
ProofType.treeOffset, // 1
ProofType.multi, // 2
ProofType.compactMulti, // 3
];

/**
Expand Down Expand Up @@ -58,7 +63,13 @@ export interface MultiProof {
gindices: Gindex[];
}

export type Proof = SingleProof | TreeOffsetProof | MultiProof;
export interface CompactMultiProof {
type: ProofType.compactMulti;
leaves: Uint8Array[];
descriptor: Uint8Array;
}

export type Proof = SingleProof | TreeOffsetProof | MultiProof | CompactMultiProof;

export interface SingleProofInput {
type: ProofType.single;
Expand All @@ -74,7 +85,12 @@ export interface MultiProofInput {
gindices: Gindex[];
}

export type ProofInput = SingleProofInput | TreeOffsetProofInput | MultiProofInput;
export interface CompactMultiProofInput {
type: ProofType.compactMulti;
descriptor: Uint8Array;
}

export type ProofInput = SingleProofInput | TreeOffsetProofInput | MultiProofInput | CompactMultiProofInput;

export function createProof(rootNode: Node, input: ProofInput): Proof {
switch (input.type) {
Expand Down Expand Up @@ -104,6 +120,14 @@ export function createProof(rootNode: Node, input: ProofInput): Proof {
gindices,
};
}
case ProofType.compactMulti: {
const leaves = createCompactMultiProof(rootNode, input.descriptor);
return {
type: ProofType.compactMulti,
leaves,
descriptor: input.descriptor,
};
}
default:
throw new Error("Invalid proof type");
}
Expand All @@ -117,6 +141,8 @@ export function createNodeFromProof(proof: Proof): Node {
return createNodeFromTreeOffsetProof(proof.offsets, proof.leaves);
case ProofType.multi:
return createNodeFromMultiProof(proof.leaves, proof.witnesses, proof.gindices);
case ProofType.compactMulti:
return createNodeFromCompactMultiProof(proof.leaves, proof.descriptor);
default:
throw new Error("Invalid proof type");
}
Expand Down
40 changes: 40 additions & 0 deletions packages/persistent-merkle-tree/test/perf/proof.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import {itBench} from "@dapplion/benchmark";
import {computeDescriptor, createProof, ProofType} from "../../src/proof";
import {createTree} from "../utils/tree";

describe("Proofs", () => {
const depth = 15;
const tree = createTree(depth);
const maxNumLeaves = 10;
const allLeafIndices = Array.from(
{length: maxNumLeaves},
(_, i) => BigInt(2) ** BigInt(depth) + BigInt(i) ** BigInt(2)
);
for (let numLeaves = 1; numLeaves < 5; numLeaves++) {
const leafIndices = allLeafIndices.slice(0, numLeaves);

itBench({
id: `multiproof - depth ${depth}, ${numLeaves} requested leaves`,
fn: () => {
createProof(tree, {type: ProofType.multi, gindices: leafIndices});
},
});

itBench({
id: `tree offset multiproof - depth ${depth}, ${numLeaves} requested leaves`,
fn: () => {
createProof(tree, {type: ProofType.treeOffset, gindices: leafIndices});
},
});

itBench({
id: `compact multiproof - depth ${depth}, ${numLeaves} requested leaves`,
beforeEach: () => {
return computeDescriptor(leafIndices);
},
fn: (descriptor) => {
createProof(tree, {type: ProofType.compactMulti, descriptor});
},
});
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import {expect} from "chai";
import {
createNodeFromCompactMultiProof,
createCompactMultiProof,
descriptorToBitlist,
computeDescriptor,
} from "../../../src/proof/compactMulti";
import {createTree} from "../../utils/tree";

describe("CompactMultiProof", () => {
const descriptorTestCases = [
{
input: Uint8Array.from([0b1000_0000]),
output: [1].map(Boolean),
},
{
input: Uint8Array.from([0b0010_0101, 0b1110_0000]),
output: [0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1].map(Boolean),
},
{
input: Uint8Array.from([0b0101_0101, 0b1000_0000]),
output: [0, 1, 0, 1, 0, 1, 0, 1, 1].map(Boolean),
},
{
input: Uint8Array.from([0b0101_0110]),
output: [0, 1, 0, 1, 0, 1, 1].map(Boolean),
},
];
describe("descriptorToBitlist", () => {
it("should convert valid descriptor to a bitlist", () => {
for (const {input, output} of descriptorTestCases) {
expect(descriptorToBitlist(input)).to.deep.equal(output);
}
});
it("should throw on invalid descriptors", () => {
const errorCases = [
Uint8Array.from([0b1000_0000, 0]),
Uint8Array.from([0b0000_0001, 0]),
Uint8Array.from([0b0101_0111]),
Uint8Array.from([0b0101_0110, 0]),
];
for (const input of errorCases) {
expect(() => descriptorToBitlist(input)).to.throw();
}
});
});
describe("computeDescriptor", () => {
it("should convert gindices to a descriptor", () => {
const index = 42n;
const expected = Uint8Array.from([0x25, 0xe0]);
expect(computeDescriptor([index])).to.deep.equal(expected);
});
});

const tree = createTree(5);
it("should roundtrip node -> proof -> node", () => {
for (const {input} of descriptorTestCases) {
const proof = createCompactMultiProof(tree, input);
const newNode = createNodeFromCompactMultiProof(proof, input);
expect(newNode.root).to.deep.equal(tree.root);
}
});
});
29 changes: 19 additions & 10 deletions packages/persistent-merkle-tree/test/unit/proof/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import {expect} from "chai";
import {describe, it} from "mocha";
import {createNodeFromProof, createProof, deserializeProof, ProofType, serializeProof} from "../../../src/proof";
import {Node, LeafNode, BranchNode} from "../../../src/node";

// Create a tree with leaves of different values
function createTree(depth: number, index = 0): Node {
if (!depth) {
return LeafNode.fromRoot(Buffer.alloc(32, index));
}
return new BranchNode(createTree(depth - 1, 2 ** depth + index), createTree(depth - 1, 2 ** depth + index + 1));
}
import {
computeDescriptor,
createNodeFromProof,
createProof,
deserializeProof,
ProofType,
serializeProof,
} from "../../../src/proof";
import {createTree} from "../../utils/tree";

describe("proof equivalence", () => {
it("should compute the same root from different proof types - single leaf", () => {
Expand All @@ -19,9 +18,14 @@ describe("proof equivalence", () => {
const singleProof = createProof(node, {type: ProofType.single, gindex});
const treeOffsetProof = createProof(node, {type: ProofType.treeOffset, gindices: [gindex]});
const multiProof = createProof(node, {type: ProofType.multi, gindices: [gindex]});
const compactMultiProof = createProof(node, {
type: ProofType.compactMulti,
descriptor: computeDescriptor([gindex]),
});
expect(node.root).to.deep.equal(createNodeFromProof(singleProof).root);
expect(node.root).to.deep.equal(createNodeFromProof(treeOffsetProof).root);
expect(node.root).to.deep.equal(createNodeFromProof(multiProof).root);
expect(node.root).to.deep.equal(createNodeFromProof(compactMultiProof).root);
}
});
it("should compute the same root from different proof types - multiple leaves", function () {
Expand All @@ -43,9 +47,14 @@ describe("proof equivalence", () => {

const treeOffsetProof = createProof(node, {type: ProofType.treeOffset, gindices});
const multiProof = createProof(node, {type: ProofType.multi, gindices});
const compactMultiProof = createProof(node, {
type: ProofType.compactMulti,
descriptor: computeDescriptor(gindices),
});

expect(node.root).to.deep.equal(createNodeFromProof(treeOffsetProof).root);
expect(node.root).to.deep.equal(createNodeFromProof(multiProof).root);
expect(node.root).to.deep.equal(createNodeFromProof(compactMultiProof).root);
}
}
}
Expand Down
Loading

1 comment on commit 5f1ea99

@github-actions
Copy link

Choose a reason for hiding this comment

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

⚠️ Performance Alert ⚠️

Possible performance regression was detected for some benchmarks.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold.

Benchmark suite Current: 5f1ea99 Previous: 7bd63c6 Ratio
List[uint8, 68719476736] len 300000 ViewDU.get(i) 3.9834 ms/op 1.2932 ms/op 3.08
Full benchmark results
Benchmark suite Current: 5f1ea99 Previous: 7bd63c6 Ratio
digestTwoHashObjects 50023 times 68.926 ms/op 69.074 ms/op 1.00
digest64 50023 times 71.192 ms/op 71.150 ms/op 1.00
digest 50023 times 71.251 ms/op 71.065 ms/op 1.00
input length 32 1.5880 us/op 1.6120 us/op 0.99
input length 64 1.7970 us/op 1.8150 us/op 0.99
input length 128 3.1550 us/op 3.1510 us/op 1.00
input length 256 4.7760 us/op 4.7380 us/op 1.01
input length 512 7.9710 us/op 7.9120 us/op 1.01
input length 1024 15.719 us/op 15.796 us/op 1.00
digest 1000000 times 1.1408 s/op 1.1547 s/op 0.99
hashObjectToByteArray 50023 times 1.9437 ms/op 1.9088 ms/op 1.02
byteArrayToHashObject 50023 times 2.3511 ms/op 2.1104 ms/op 1.11
getGindicesAtDepth 5.6470 us/op 5.5720 us/op 1.01
iterateAtDepth 11.798 us/op 11.735 us/op 1.01
getGindexBits 558.00 ns/op 586.00 ns/op 0.95
gindexIterator 1.3920 us/op 1.4000 us/op 0.99
hash 2 Uint8Array 2250026 times 3.2862 s/op 3.2900 s/op 1.00
hashTwoObjects 2250026 times 3.1285 s/op 3.1122 s/op 1.01
getNodeH() x7812.5 avg hindex 20.900 us/op 21.026 us/op 0.99
getNodeH() x7812.5 index 0 6.7940 us/op 6.7090 us/op 1.01
getNodeH() x7812.5 index 7 6.7210 us/op 6.7010 us/op 1.00
getNodeH() x7812.5 index 7 with key array 6.7830 us/op 6.7760 us/op 1.00
new LeafNode() x7812.5 297.44 us/op 307.72 us/op 0.97
multiproof - depth 15, 1 requested leaves 13.635 us/op
tree offset multiproof - depth 15, 1 requested leaves 31.393 us/op
compact multiproof - depth 15, 1 requested leaves 7.1320 us/op
multiproof - depth 15, 2 requested leaves 17.772 us/op
tree offset multiproof - depth 15, 2 requested leaves 32.998 us/op
compact multiproof - depth 15, 2 requested leaves 3.4870 us/op
multiproof - depth 15, 3 requested leaves 24.521 us/op
tree offset multiproof - depth 15, 3 requested leaves 42.456 us/op
compact multiproof - depth 15, 3 requested leaves 6.7040 us/op
multiproof - depth 15, 4 requested leaves 32.930 us/op
tree offset multiproof - depth 15, 4 requested leaves 52.131 us/op
compact multiproof - depth 15, 4 requested leaves 7.4140 us/op
packedRootsBytesToLeafNodes bytes 4000 offset 0 2.9900 us/op 3.1520 us/op 0.95
packedRootsBytesToLeafNodes bytes 4000 offset 1 2.9220 us/op 2.9100 us/op 1.00
packedRootsBytesToLeafNodes bytes 4000 offset 2 2.8730 us/op 2.9530 us/op 0.97
packedRootsBytesToLeafNodes bytes 4000 offset 3 2.7860 us/op 2.6330 us/op 1.06
subtreeFillToContents depth 40 count 250000 65.324 ms/op 62.276 ms/op 1.05
setRoot - gindexBitstring 12.410 ms/op 12.565 ms/op 0.99
setRoot - gindex 11.981 ms/op 12.847 ms/op 0.93
getRoot - gindexBitstring 2.3561 ms/op 2.4654 ms/op 0.96
getRoot - gindex 3.6886 ms/op 3.7293 ms/op 0.99
getHashObject then setHashObject 14.813 ms/op 14.987 ms/op 0.99
setNodeWithFn 13.361 ms/op 13.189 ms/op 1.01
getNodeAtDepth depth 0 x100000 1.6191 ms/op 1.6247 ms/op 1.00
setNodeAtDepth depth 0 x100000 3.2865 ms/op 3.4590 ms/op 0.95
getNodesAtDepth depth 0 x100000 1.4166 ms/op 1.4130 ms/op 1.00
setNodesAtDepth depth 0 x100000 1.9728 ms/op 1.9599 ms/op 1.01
getNodeAtDepth depth 1 x100000 1.7387 ms/op 1.7376 ms/op 1.00
setNodeAtDepth depth 1 x100000 7.1807 ms/op 7.3019 ms/op 0.98
getNodesAtDepth depth 1 x100000 1.5970 ms/op 1.5977 ms/op 1.00
setNodesAtDepth depth 1 x100000 5.9403 ms/op 6.2146 ms/op 0.96
getNodeAtDepth depth 2 x100000 2.1897 ms/op 2.2098 ms/op 0.99
setNodeAtDepth depth 2 x100000 12.579 ms/op 12.406 ms/op 1.01
getNodesAtDepth depth 2 x100000 26.491 ms/op 27.301 ms/op 0.97
setNodesAtDepth depth 2 x100000 17.596 ms/op 18.053 ms/op 0.97
tree.getNodesAtDepth - gindexes 7.1555 ms/op 8.2377 ms/op 0.87
tree.getNodesAtDepth - push all nodes 2.1826 ms/op 2.2642 ms/op 0.96
tree.getNodesAtDepth - navigation 201.37 us/op 202.15 us/op 1.00
tree.setNodesAtDepth - indexes 492.44 us/op 444.61 us/op 1.11
set at depth 8 647.00 ns/op 673.00 ns/op 0.96
set at depth 16 912.00 ns/op 979.00 ns/op 0.93
set at depth 32 1.3850 us/op 1.4490 us/op 0.96
iterateNodesAtDepth 8 256 18.299 us/op 18.470 us/op 0.99
getNodesAtDepth 8 256 4.5490 us/op 4.5360 us/op 1.00
iterateNodesAtDepth 16 65536 5.3518 ms/op 5.3756 ms/op 1.00
getNodesAtDepth 16 65536 2.0468 ms/op 2.1019 ms/op 0.97
iterateNodesAtDepth 32 250000 19.913 ms/op 20.035 ms/op 0.99
getNodesAtDepth 32 250000 5.4413 ms/op 5.7023 ms/op 0.95
iterateNodesAtDepth 40 250000 19.897 ms/op 19.866 ms/op 1.00
getNodesAtDepth 40 250000 5.5312 ms/op 5.6458 ms/op 0.98
250k validators 3.3778 s/op 3.4551 s/op 0.98
bitlist bytes to struct (120,90) 1.0970 us/op 1.1580 us/op 0.95
bitlist bytes to tree (120,90) 4.9470 us/op 4.9620 us/op 1.00
bitlist bytes to struct (2048,2048) 1.9190 us/op 2.1240 us/op 0.90
bitlist bytes to tree (2048,2048) 6.3330 us/op 7.8360 us/op 0.81
ByteListType - deserialize 15.689 ms/op 17.206 ms/op 0.91
BasicListType - deserialize 14.710 ms/op 22.693 ms/op 0.65
ByteListType - serialize 15.225 ms/op 17.169 ms/op 0.89
BasicListType - serialize 18.639 ms/op 18.821 ms/op 0.99
BasicListType - tree_convertToStruct 37.171 ms/op 37.955 ms/op 0.98
List[uint8, 68719476736] len 300000 ViewDU.getAll() + iterate 4.6037 ms/op 4.7145 ms/op 0.98
List[uint8, 68719476736] len 300000 ViewDU.get(i) 3.9834 ms/op 1.2932 ms/op 3.08
Array.push len 300000 empty Array - number 7.1388 ms/op 7.8594 ms/op 0.91
Array.set len 300000 from new Array - number 2.1997 ms/op 2.3574 ms/op 0.93
Array.set len 300000 - number 7.7998 ms/op 7.7305 ms/op 1.01
Uint8Array.set len 300000 242.10 us/op 242.00 us/op 1.00
Uint32Array.set len 300000 333.63 us/op 325.38 us/op 1.03
Container({a: uint8, b: uint8}) getViewDU x300000 29.738 ms/op 30.252 ms/op 0.98
ContainerNodeStruct({a: uint8, b: uint8}) getViewDU x300000 12.392 ms/op 12.241 ms/op 1.01
List(Container) len 300000 ViewDU.getAllReadonly() + iterate 320.67 ms/op 316.80 ms/op 1.01
List(Container) len 300000 ViewDU.getAllReadonlyValues() + iterate 373.44 ms/op 415.43 ms/op 0.90
List(Container) len 300000 ViewDU.get(i) 9.0503 ms/op 9.0658 ms/op 1.00
List(Container) len 300000 ViewDU.getReadonly(i) 8.6231 ms/op 8.9265 ms/op 0.97
List(ContainerNodeStruct) len 300000 ViewDU.getAllReadonly() + iterate 39.809 ms/op 48.584 ms/op 0.82
List(ContainerNodeStruct) len 300000 ViewDU.getAllReadonlyValues() + iterate 5.7620 ms/op 7.3017 ms/op 0.79
List(ContainerNodeStruct) len 300000 ViewDU.get(i) 8.5602 ms/op 8.9311 ms/op 0.96
List(ContainerNodeStruct) len 300000 ViewDU.getReadonly(i) 8.2837 ms/op 8.6242 ms/op 0.96
Array.push len 300000 empty Array - object 7.2824 ms/op 7.6345 ms/op 0.95
Array.set len 300000 from new Array - object 2.5544 ms/op 2.3167 ms/op 1.10
Array.set len 300000 - object 7.1626 ms/op 8.2656 ms/op 0.87
cachePermanentRootStruct no cache 13.254 us/op 13.317 us/op 1.00
cachePermanentRootStruct with cache 301.00 ns/op 287.00 ns/op 1.05
epochParticipation len 250000 rws 7813 3.0459 ms/op 3.0950 ms/op 0.98
deserialize Attestation - tree 4.3520 us/op 4.2690 us/op 1.02
deserialize Attestation - struct 2.8900 us/op 2.9480 us/op 0.98
deserialize SignedAggregateAndProof - tree 5.6240 us/op 5.3580 us/op 1.05
deserialize SignedAggregateAndProof - struct 4.6530 us/op 4.4670 us/op 1.04
deserialize SyncCommitteeMessage - tree 1.6290 us/op 1.6850 us/op 0.97
deserialize SyncCommitteeMessage - struct 1.7980 us/op 1.8380 us/op 0.98
deserialize SignedContributionAndProof - tree 2.9890 us/op 2.8300 us/op 1.06
deserialize SignedContributionAndProof - struct 3.8370 us/op 3.8740 us/op 0.99
deserialize SignedBeaconBlock - tree 325.28 us/op 316.69 us/op 1.03
deserialize SignedBeaconBlock - struct 189.70 us/op 193.84 us/op 0.98
BeaconState vc 300000 - deserialize tree 782.09 ms/op 876.60 ms/op 0.89
BeaconState vc 300000 - serialize tree 158.61 ms/op 255.97 ms/op 0.62
BeaconState.historicalRoots vc 300000 - deserialize tree 1.1430 us/op 1.1660 us/op 0.98
BeaconState.historicalRoots vc 300000 - serialize tree 1.3060 us/op 1.2720 us/op 1.03
BeaconState.validators vc 300000 - deserialize tree 702.19 ms/op 824.76 ms/op 0.85
BeaconState.validators vc 300000 - serialize tree 203.92 ms/op 207.09 ms/op 0.98
BeaconState.balances vc 300000 - deserialize tree 29.567 ms/op 31.095 ms/op 0.95
BeaconState.balances vc 300000 - serialize tree 3.6826 ms/op 4.6221 ms/op 0.80
BeaconState.previousEpochParticipation vc 300000 - deserialize tree 461.73 us/op 615.93 us/op 0.75
BeaconState.previousEpochParticipation vc 300000 - serialize tree 334.64 us/op 354.14 us/op 0.94
BeaconState.currentEpochParticipation vc 300000 - deserialize tree 450.19 us/op 611.27 us/op 0.74
BeaconState.currentEpochParticipation vc 300000 - serialize tree 333.83 us/op 357.03 us/op 0.94
BeaconState.inactivityScores vc 300000 - deserialize tree 29.428 ms/op 38.045 ms/op 0.77
BeaconState.inactivityScores vc 300000 - serialize tree 4.4150 ms/op 3.7138 ms/op 1.19
hashTreeRoot Attestation - struct 47.733 us/op 41.017 us/op 1.16
hashTreeRoot Attestation - tree 28.516 us/op 30.597 us/op 0.93
hashTreeRoot SignedAggregateAndProof - struct 62.843 us/op 65.480 us/op 0.96
hashTreeRoot SignedAggregateAndProof - tree 47.561 us/op 43.005 us/op 1.11
hashTreeRoot SyncCommitteeMessage - struct 15.742 us/op 13.996 us/op 1.12
hashTreeRoot SyncCommitteeMessage - tree 10.927 us/op 9.5730 us/op 1.14
hashTreeRoot SignedContributionAndProof - struct 43.414 us/op 39.415 us/op 1.10
hashTreeRoot SignedContributionAndProof - tree 29.031 us/op 30.939 us/op 0.94
hashTreeRoot SignedBeaconBlock - struct 3.7269 ms/op 3.4845 ms/op 1.07
hashTreeRoot SignedBeaconBlock - tree 2.5653 ms/op 2.5691 ms/op 1.00
hashTreeRoot Validator - struct 19.562 us/op 19.018 us/op 1.03
hashTreeRoot Validator - tree 16.825 us/op 16.580 us/op 1.01
BeaconState vc 300000 - hashTreeRoot tree 5.3396 s/op 5.3749 s/op 0.99
BeaconState.historicalRoots vc 300000 - hashTreeRoot tree 2.1870 us/op 2.2450 us/op 0.97
BeaconState.validators vc 300000 - hashTreeRoot tree 5.1167 s/op 5.1376 s/op 1.00
BeaconState.balances vc 300000 - hashTreeRoot tree 132.44 ms/op 132.45 ms/op 1.00
BeaconState.previousEpochParticipation vc 300000 - hashTreeRoot tree 13.870 ms/op 14.168 ms/op 0.98
BeaconState.currentEpochParticipation vc 300000 - hashTreeRoot tree 13.868 ms/op 13.983 ms/op 0.99
BeaconState.inactivityScores vc 300000 - hashTreeRoot tree 141.80 ms/op 136.06 ms/op 1.04
hash64 x18 27.779 us/op 28.039 us/op 0.99
hashTwoObjects x18 27.340 us/op 27.458 us/op 1.00
hash64 x1740 2.6250 ms/op 2.6545 ms/op 0.99
hashTwoObjects x1740 2.5989 ms/op 2.6057 ms/op 1.00
hash64 x2700000 4.0664 s/op 4.1050 s/op 0.99
hashTwoObjects x2700000 4.0293 s/op 4.0384 s/op 1.00
get_exitEpoch - ContainerType 456.00 ns/op 481.00 ns/op 0.95
get_exitEpoch - ContainerNodeStructType 387.00 ns/op 406.00 ns/op 0.95
set_exitEpoch - ContainerType 342.00 ns/op 462.00 ns/op 0.74
set_exitEpoch - ContainerNodeStructType 285.00 ns/op 316.00 ns/op 0.90
get_pubkey - ContainerType 1.6020 us/op 1.7160 us/op 0.93
get_pubkey - ContainerNodeStructType 287.00 ns/op 299.00 ns/op 0.96
hashTreeRoot - ContainerType 464.00 ns/op 504.00 ns/op 0.92
hashTreeRoot - ContainerNodeStructType 521.00 ns/op 537.00 ns/op 0.97
createProof - ContainerType 6.7250 us/op 6.9250 us/op 0.97
createProof - ContainerNodeStructType 33.687 us/op 34.575 us/op 0.97
serialize - ContainerType 2.8530 us/op 3.0130 us/op 0.95
serialize - ContainerNodeStructType 2.3520 us/op 2.4060 us/op 0.98
set_exitEpoch_and_hashTreeRoot - ContainerType 6.1520 us/op 6.4150 us/op 0.96
set_exitEpoch_and_hashTreeRoot - ContainerNodeStructType 17.244 us/op 17.614 us/op 0.98
Array - for of 7.1450 us/op 7.1670 us/op 1.00
Array - for(;;) 6.8960 us/op 6.8200 us/op 1.01
basicListValue.readonlyValuesArray() 4.2795 ms/op 4.7385 ms/op 0.90
basicListValue.readonlyValuesArray() + loop all 4.3306 ms/op 4.7442 ms/op 0.91
compositeListValue.readonlyValuesArray() 31.616 ms/op 33.239 ms/op 0.95
compositeListValue.readonlyValuesArray() + loop all 27.290 ms/op 27.814 ms/op 0.98
Number64UintType - get balances list 5.8493 ms/op 6.2219 ms/op 0.94
Number64UintType - set balances list 11.754 ms/op 12.422 ms/op 0.95
Number64UintType - get and increase 10 then set 51.915 ms/op 49.913 ms/op 1.04
Number64UintType - increase 10 using applyDelta 18.756 ms/op 20.981 ms/op 0.89
Number64UintType - increase 10 using applyDeltaInBatch 18.654 ms/op 20.931 ms/op 0.89
tree_newTreeFromUint64Deltas 20.496 ms/op 21.638 ms/op 0.95
unsafeUint8ArrayToTree 43.822 ms/op 41.822 ms/op 1.05
bitLength(50) 300.00 ns/op 314.00 ns/op 0.96
bitLengthStr(50) 307.00 ns/op 321.00 ns/op 0.96
bitLength(8000) 286.00 ns/op 298.00 ns/op 0.96
bitLengthStr(8000) 370.00 ns/op 385.00 ns/op 0.96
bitLength(250000) 279.00 ns/op 306.00 ns/op 0.91
bitLengthStr(250000) 419.00 ns/op 444.00 ns/op 0.94
floor - Math.floor (53) 0.60256 ns/op 0.60260 ns/op 1.00
floor - << 0 (53) 0.60253 ns/op 0.60273 ns/op 1.00
floor - Math.floor (512) 0.60249 ns/op 0.60268 ns/op 1.00
floor - << 0 (512) 0.60260 ns/op 0.60267 ns/op 1.00
fnIf(0) 2.0077 ns/op 2.0087 ns/op 1.00
fnSwitch(0) 3.3414 ns/op 3.2491 ns/op 1.03
fnObj(0) 0.60258 ns/op 0.60325 ns/op 1.00
fnArr(0) 0.60258 ns/op 0.60262 ns/op 1.00
fnIf(4) 3.1718 ns/op 3.0051 ns/op 1.06
fnSwitch(4) 3.3329 ns/op 3.2138 ns/op 1.04
fnObj(4) 0.60254 ns/op 0.60274 ns/op 1.00
fnArr(4) 0.60377 ns/op 0.60263 ns/op 1.00
fnIf(9) 5.2246 ns/op 5.2252 ns/op 1.00
fnSwitch(9) 3.3556 ns/op 3.2531 ns/op 1.03
fnObj(9) 0.60260 ns/op 0.60354 ns/op 1.00
fnArr(9) 0.60270 ns/op 0.60265 ns/op 1.00
Container {a,b,vec} - as struct x100000 60.504 us/op 60.511 us/op 1.00
Container {a,b,vec} - as tree x100000 508.95 us/op 510.27 us/op 1.00
Container {a,vec,b} - as struct x100000 120.95 us/op 120.72 us/op 1.00
Container {a,vec,b} - as tree x100000 552.62 us/op 565.54 us/op 0.98
get 2 props x1000000 - rawObject 401.80 us/op 401.86 us/op 1.00
get 2 props x1000000 - proxy 91.701 ms/op 89.947 ms/op 1.02
get 2 props x1000000 - customObj 402.00 us/op 402.15 us/op 1.00
Simple object binary -> struct 763.00 ns/op 773.00 ns/op 0.99
Simple object binary -> tree_backed 2.3190 us/op 2.4560 us/op 0.94
Simple object struct -> tree_backed 3.1660 us/op 3.2880 us/op 0.96
Simple object tree_backed -> struct 2.7180 us/op 2.8380 us/op 0.96
Simple object struct -> binary 1.4140 us/op 1.5080 us/op 0.94
Simple object tree_backed -> binary 2.4040 us/op 2.5770 us/op 0.93
aggregationBits binary -> struct 657.00 ns/op 683.00 ns/op 0.96
aggregationBits binary -> tree_backed 3.1150 us/op 3.3040 us/op 0.94
aggregationBits struct -> tree_backed 3.7130 us/op 3.8150 us/op 0.97
aggregationBits tree_backed -> struct 1.5540 us/op 1.6390 us/op 0.95
aggregationBits struct -> binary 1.1460 us/op 1.2060 us/op 0.95
aggregationBits tree_backed -> binary 1.4390 us/op 1.5060 us/op 0.96
List(uint8) 100000 binary -> struct 1.7124 ms/op 1.6676 ms/op 1.03
List(uint8) 100000 binary -> tree_backed 127.20 us/op 131.70 us/op 0.97
List(uint8) 100000 struct -> tree_backed 1.6493 ms/op 1.7031 ms/op 0.97
List(uint8) 100000 tree_backed -> struct 1.1550 ms/op 1.2034 ms/op 0.96
List(uint8) 100000 struct -> binary 1.5046 ms/op 1.5649 ms/op 0.96
List(uint8) 100000 tree_backed -> binary 94.594 us/op 95.662 us/op 0.99
List(uint64Number) 100000 binary -> struct 1.5526 ms/op 1.5148 ms/op 1.02
List(uint64Number) 100000 binary -> tree_backed 4.8359 ms/op 4.4424 ms/op 1.09
List(uint64Number) 100000 struct -> tree_backed 6.7426 ms/op 6.4905 ms/op 1.04
List(uint64Number) 100000 tree_backed -> struct 2.4393 ms/op 2.5904 ms/op 0.94
List(uint64Number) 100000 struct -> binary 1.8748 ms/op 2.0078 ms/op 0.93
List(uint64Number) 100000 tree_backed -> binary 937.58 us/op 1.0487 ms/op 0.89
List(Uint64Bigint) 100000 binary -> struct 4.3397 ms/op 4.5703 ms/op 0.95
List(Uint64Bigint) 100000 binary -> tree_backed 4.3450 ms/op 4.9434 ms/op 0.88
List(Uint64Bigint) 100000 struct -> tree_backed 7.6024 ms/op 7.3984 ms/op 1.03
List(Uint64Bigint) 100000 tree_backed -> struct 5.5127 ms/op 5.6370 ms/op 0.98
List(Uint64Bigint) 100000 struct -> binary 2.6297 ms/op 2.6139 ms/op 1.01
List(Uint64Bigint) 100000 tree_backed -> binary 1.1337 ms/op 1.2006 ms/op 0.94
Vector(Root) 100000 binary -> struct 45.613 ms/op 44.133 ms/op 1.03
Vector(Root) 100000 binary -> tree_backed 43.696 ms/op 48.750 ms/op 0.90
Vector(Root) 100000 struct -> tree_backed 52.425 ms/op 56.805 ms/op 0.92
Vector(Root) 100000 tree_backed -> struct 58.641 ms/op 65.460 ms/op 0.90
Vector(Root) 100000 struct -> binary 2.4929 ms/op 2.5433 ms/op 0.98
Vector(Root) 100000 tree_backed -> binary 11.281 ms/op 11.583 ms/op 0.97
List(Validator) 100000 binary -> struct 164.47 ms/op 160.94 ms/op 1.02
List(Validator) 100000 binary -> tree_backed 430.59 ms/op 438.53 ms/op 0.98
List(Validator) 100000 struct -> tree_backed 452.96 ms/op 492.35 ms/op 0.92
List(Validator) 100000 tree_backed -> struct 246.08 ms/op 271.11 ms/op 0.91
List(Validator) 100000 struct -> binary 40.739 ms/op 41.248 ms/op 0.99
List(Validator) 100000 tree_backed -> binary 107.68 ms/op 109.77 ms/op 0.98
List(Validator-NS) 100000 binary -> struct 165.88 ms/op 167.89 ms/op 0.99
List(Validator-NS) 100000 binary -> tree_backed 229.77 ms/op 230.64 ms/op 1.00
List(Validator-NS) 100000 struct -> tree_backed 281.16 ms/op 286.13 ms/op 0.98
List(Validator-NS) 100000 tree_backed -> struct 233.69 ms/op 239.18 ms/op 0.98
List(Validator-NS) 100000 struct -> binary 41.178 ms/op 40.715 ms/op 1.01
List(Validator-NS) 100000 tree_backed -> binary 48.152 ms/op 47.343 ms/op 1.02
get epochStatuses - MutableVector 100.68 us/op 99.730 us/op 1.01
get epochStatuses - ViewDU 287.59 us/op 288.01 us/op 1.00
set epochStatuses - ListTreeView 1.8701 ms/op 1.9333 ms/op 0.97
set epochStatuses - ListTreeView - set() 597.71 us/op 605.03 us/op 0.99
set epochStatuses - ListTreeView - commit() 539.74 us/op 542.74 us/op 0.99
bitstring 933.83 ns/op 922.68 ns/op 1.01
bit mask 14.494 ns/op 14.458 ns/op 1.00
struct - increase slot to 1000000 1.9459 ms/op 1.7877 ms/op 1.09
UintNumberType - increase slot to 1000000 36.775 ms/op 36.816 ms/op 1.00
UintBigintType - increase slot to 1000000 540.37 ms/op 547.63 ms/op 0.99
UintBigint8 x 100000 tree_deserialize 5.3332 ms/op 5.1193 ms/op 1.04
UintBigint8 x 100000 tree_serialize 1.5337 ms/op 1.4463 ms/op 1.06
UintBigint16 x 100000 tree_deserialize 4.7582 ms/op 4.8179 ms/op 0.99
UintBigint16 x 100000 tree_serialize 1.5507 ms/op 1.5020 ms/op 1.03
UintBigint32 x 100000 tree_deserialize 5.4907 ms/op 6.4716 ms/op 0.85
UintBigint32 x 100000 tree_serialize 1.4398 ms/op 1.5637 ms/op 0.92
UintBigint64 x 100000 tree_deserialize 6.0934 ms/op 6.5699 ms/op 0.93
UintBigint64 x 100000 tree_serialize 1.8949 ms/op 1.9545 ms/op 0.97
UintBigint8 x 100000 value_deserialize 537.96 us/op 538.29 us/op 1.00
UintBigint8 x 100000 value_serialize 720.81 us/op 823.12 us/op 0.88
UintBigint16 x 100000 value_deserialize 592.19 us/op 582.74 us/op 1.02
UintBigint16 x 100000 value_serialize 818.83 us/op 872.52 us/op 0.94
UintBigint32 x 100000 value_deserialize 538.91 us/op 544.79 us/op 0.99
UintBigint32 x 100000 value_serialize 795.28 us/op 865.98 us/op 0.92
UintBigint64 x 100000 value_deserialize 608.69 us/op 609.99 us/op 1.00
UintBigint64 x 100000 value_serialize 1.0209 ms/op 1.1024 ms/op 0.93
UintBigint8 x 100000 deserialize 6.0334 ms/op 5.9140 ms/op 1.02
UintBigint8 x 100000 serialize 1.8133 ms/op 1.9353 ms/op 0.94
UintBigint16 x 100000 deserialize 5.8444 ms/op 5.8846 ms/op 0.99
UintBigint16 x 100000 serialize 1.8994 ms/op 1.9275 ms/op 0.99
UintBigint32 x 100000 deserialize 7.2448 ms/op 7.1222 ms/op 1.02
UintBigint32 x 100000 serialize 3.6207 ms/op 3.6568 ms/op 0.99
UintBigint64 x 100000 deserialize 4.2883 ms/op 4.4256 ms/op 0.97
UintBigint64 x 100000 serialize 1.9600 ms/op 1.9111 ms/op 1.03
UintBigint128 x 100000 deserialize 7.0250 ms/op 6.9662 ms/op 1.01
UintBigint128 x 100000 serialize 21.225 ms/op 21.337 ms/op 0.99
UintBigint256 x 100000 deserialize 13.046 ms/op 13.067 ms/op 1.00
UintBigint256 x 100000 serialize 63.310 ms/op 65.065 ms/op 0.97
Slice from Uint8Array x25000 1.3893 ms/op 1.3110 ms/op 1.06
Slice from ArrayBuffer x25000 24.603 ms/op 26.133 ms/op 0.94
Slice from ArrayBuffer x25000 + new Uint8Array 25.398 ms/op 28.796 ms/op 0.88
Copy Uint8Array 100000 iterate 1.0523 ms/op 1.0513 ms/op 1.00
Copy Uint8Array 100000 slice 92.032 us/op 91.894 us/op 1.00
Copy Uint8Array 100000 Uint8Array.prototype.slice.call 93.800 us/op 93.057 us/op 1.01
Copy Buffer 100000 Uint8Array.prototype.slice.call 93.419 us/op 94.350 us/op 0.99
Copy Uint8Array 100000 slice + set 194.17 us/op 425.45 us/op 0.46
Copy Uint8Array 100000 subarray + set 99.133 us/op 215.86 us/op 0.46
Copy Uint8Array 100000 slice arrayBuffer 91.359 us/op 228.26 us/op 0.40
Uint64 deserialize 100000 - iterate Uint8Array 2.1548 ms/op 2.1309 ms/op 1.01
Uint64 deserialize 100000 - by Uint32A 1.9559 ms/op 2.0043 ms/op 0.98
Uint64 deserialize 100000 - by DataView.getUint32 x2 2.0230 ms/op 2.0196 ms/op 1.00
Uint64 deserialize 100000 - by DataView.getBigUint64 5.7015 ms/op 5.8481 ms/op 0.97
Uint64 deserialize 100000 - by byte 72.386 ms/op 73.540 ms/op 0.98

Please sign in to comment.