amt bench in good state
This commit is contained in:
parent
d463abae2f
commit
eaf0200fe1
309
cmd/lotus-bench/amt_internal.go
Normal file
309
cmd/lotus-bench/amt_internal.go
Normal file
@ -0,0 +1,309 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
cid "github.com/ipfs/go-cid"
|
||||
cbg "github.com/whyrusleeping/cbor-gen"
|
||||
xerrors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type AMTRoot struct {
|
||||
BitWidth uint64
|
||||
Height uint64
|
||||
Count uint64
|
||||
AMTNode AMTNode
|
||||
}
|
||||
|
||||
type AMTNode struct {
|
||||
Bmap []byte
|
||||
Links []cid.Cid
|
||||
Values []*cbg.Deferred
|
||||
}
|
||||
|
||||
// Code generated by github.com/whyrusleeping/cbor-gen. DO NOT EDIT.
|
||||
|
||||
var _ = xerrors.Errorf
|
||||
var _ = cid.Undef
|
||||
var _ = math.E
|
||||
var _ = sort.Sort
|
||||
|
||||
var lengthBufAMTRoot = []byte{132}
|
||||
|
||||
func (t *AMTRoot) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
|
||||
cw := cbg.NewCborWriter(w)
|
||||
|
||||
if _, err := cw.Write(lengthBufAMTRoot); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.BitWidth (uint64) (uint64)
|
||||
|
||||
if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(t.BitWidth)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.Height (uint64) (uint64)
|
||||
|
||||
if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(t.Height)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.Count (uint64) (uint64)
|
||||
|
||||
if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(t.Count)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.AMTNode (internal.AMTNode) (struct)
|
||||
if err := t.AMTNode.MarshalCBOR(cw); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *AMTRoot) UnmarshalCBOR(r io.Reader) (err error) {
|
||||
*t = AMTRoot{}
|
||||
|
||||
cr := cbg.NewCborReader(r)
|
||||
|
||||
maj, extra, err := cr.ReadHeader()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
}()
|
||||
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("cbor input should be of type array")
|
||||
}
|
||||
|
||||
if extra != 4 {
|
||||
return fmt.Errorf("cbor input had wrong number of fields")
|
||||
}
|
||||
|
||||
// t.BitWidth (uint64) (uint64)
|
||||
|
||||
{
|
||||
|
||||
maj, extra, err = cr.ReadHeader()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.BitWidth = uint64(extra)
|
||||
|
||||
}
|
||||
// t.Height (uint64) (uint64)
|
||||
|
||||
{
|
||||
|
||||
maj, extra, err = cr.ReadHeader()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.Height = uint64(extra)
|
||||
|
||||
}
|
||||
// t.Count (uint64) (uint64)
|
||||
|
||||
{
|
||||
|
||||
maj, extra, err = cr.ReadHeader()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if maj != cbg.MajUnsignedInt {
|
||||
return fmt.Errorf("wrong type for uint64 field")
|
||||
}
|
||||
t.Count = uint64(extra)
|
||||
|
||||
}
|
||||
// t.AMTNode (internal.AMTNode) (struct)
|
||||
|
||||
{
|
||||
|
||||
if err := t.AMTNode.UnmarshalCBOR(cr); err != nil {
|
||||
return xerrors.Errorf("unmarshaling t.AMTNode: %w", err)
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var lengthBufAMTNode = []byte{131}
|
||||
|
||||
func (t *AMTNode) MarshalCBOR(w io.Writer) error {
|
||||
if t == nil {
|
||||
_, err := w.Write(cbg.CborNull)
|
||||
return err
|
||||
}
|
||||
|
||||
cw := cbg.NewCborWriter(w)
|
||||
|
||||
if _, err := cw.Write(lengthBufAMTNode); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.Bmap ([]uint8) (slice)
|
||||
if len(t.Bmap) > cbg.ByteArrayMaxLen {
|
||||
return xerrors.Errorf("Byte array in field t.Bmap was too long")
|
||||
}
|
||||
|
||||
if err := cw.WriteMajorTypeHeader(cbg.MajByteString, uint64(len(t.Bmap))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := cw.Write(t.Bmap[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// t.Links ([]cid.Cid) (slice)
|
||||
if len(t.Links) > cbg.MaxLength {
|
||||
return xerrors.Errorf("Slice value in field t.Links was too long")
|
||||
}
|
||||
|
||||
if err := cw.WriteMajorTypeHeader(cbg.MajArray, uint64(len(t.Links))); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range t.Links {
|
||||
if err := cbg.WriteCid(w, v); err != nil {
|
||||
return xerrors.Errorf("failed writing cid field t.Links: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// t.Values ([]*typegen.Deferred) (slice)
|
||||
if len(t.Values) > cbg.MaxLength {
|
||||
return xerrors.Errorf("Slice value in field t.Values was too long")
|
||||
}
|
||||
|
||||
if err := cw.WriteMajorTypeHeader(cbg.MajArray, uint64(len(t.Values))); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range t.Values {
|
||||
if err := v.MarshalCBOR(cw); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *AMTNode) UnmarshalCBOR(r io.Reader) (err error) {
|
||||
*t = AMTNode{}
|
||||
|
||||
cr := cbg.NewCborReader(r)
|
||||
|
||||
maj, extra, err := cr.ReadHeader()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
}()
|
||||
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("cbor input should be of type array")
|
||||
}
|
||||
|
||||
if extra != 3 {
|
||||
return fmt.Errorf("cbor input had wrong number of fields")
|
||||
}
|
||||
|
||||
// t.Bmap ([]uint8) (slice)
|
||||
|
||||
maj, extra, err = cr.ReadHeader()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if extra > cbg.ByteArrayMaxLen {
|
||||
return fmt.Errorf("t.Bmap: byte array too large (%d)", extra)
|
||||
}
|
||||
if maj != cbg.MajByteString {
|
||||
return fmt.Errorf("expected byte array")
|
||||
}
|
||||
|
||||
if extra > 0 {
|
||||
t.Bmap = make([]uint8, extra)
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(cr, t.Bmap[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
// t.Links ([]cid.Cid) (slice)
|
||||
|
||||
maj, extra, err = cr.ReadHeader()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if extra > cbg.MaxLength {
|
||||
return fmt.Errorf("t.Links: array too large (%d)", extra)
|
||||
}
|
||||
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("expected cbor array")
|
||||
}
|
||||
|
||||
if extra > 0 {
|
||||
t.Links = make([]cid.Cid, extra)
|
||||
}
|
||||
|
||||
for i := 0; i < int(extra); i++ {
|
||||
|
||||
c, err := cbg.ReadCid(cr)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("reading cid field t.Links failed: %w", err)
|
||||
}
|
||||
t.Links[i] = c
|
||||
}
|
||||
|
||||
// t.Values ([]*typegen.Deferred) (slice)
|
||||
|
||||
maj, extra, err = cr.ReadHeader()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if extra > cbg.MaxLength {
|
||||
return fmt.Errorf("t.Values: array too large (%d)", extra)
|
||||
}
|
||||
|
||||
if maj != cbg.MajArray {
|
||||
return fmt.Errorf("expected cbor array")
|
||||
}
|
||||
|
||||
if extra > 0 {
|
||||
t.Values = make([]*cbg.Deferred, extra)
|
||||
}
|
||||
|
||||
for i := 0; i < int(extra); i++ {
|
||||
|
||||
var v cbg.Deferred
|
||||
if err := v.UnmarshalCBOR(cr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.Values[i] = &v
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
@ -8,9 +9,14 @@ import (
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/docker/go-units"
|
||||
"github.com/ipfs/boxo/blockservice"
|
||||
"github.com/ipfs/go-cid"
|
||||
offline "github.com/ipfs/go-ipfs-exchange-offline"
|
||||
format "github.com/ipfs/go-ipld-format"
|
||||
logging "github.com/ipfs/go-log/v2"
|
||||
"github.com/minio/blake2b-simd"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
@ -20,7 +26,13 @@ import (
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-paramfetch"
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
"github.com/filecoin-project/go-state-types/builtin/v9/verifreg"
|
||||
prooftypes "github.com/filecoin-project/go-state-types/proof"
|
||||
"github.com/filecoin-project/lotus/blockstore"
|
||||
"github.com/filecoin-project/lotus/chain/actors/builtin/market"
|
||||
adt "github.com/filecoin-project/specs-actors/v6/actors/util/adt"
|
||||
"github.com/ipfs/boxo/ipld/merkledag"
|
||||
cbor "github.com/ipfs/go-ipld-cbor"
|
||||
|
||||
lapi "github.com/filecoin-project/lotus/api"
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
@ -104,6 +116,7 @@ func main() {
|
||||
DisableSliceFlagSeparator: true,
|
||||
Commands: []*cli.Command{
|
||||
proveCmd,
|
||||
amtBenchCmd,
|
||||
sealBenchCmd,
|
||||
simpleCmd,
|
||||
importBenchCmd,
|
||||
@ -117,6 +130,202 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
type amtStatCollector struct {
|
||||
ds format.NodeGetter
|
||||
walk func(format.Node) ([]*format.Link, error)
|
||||
|
||||
statsLk sync.Mutex
|
||||
totalAMTLinks int
|
||||
totalAMTValues int
|
||||
totalAMTLinkNodes int
|
||||
totalAMTValueNodes int
|
||||
totalAMTLinkNodeSize int
|
||||
totalAMTValueNodeSize int
|
||||
}
|
||||
|
||||
func (asc *amtStatCollector) String() string {
|
||||
asc.statsLk.Lock()
|
||||
defer asc.statsLk.Unlock()
|
||||
|
||||
str := "\n------------\n"
|
||||
str += fmt.Sprintf("Link Count: %d\n", asc.totalAMTLinks)
|
||||
str += fmt.Sprintf("Value Count: %d\n", asc.totalAMTValues)
|
||||
str += fmt.Sprintf("%d link nodes %d bytes\n", asc.totalAMTLinkNodes, asc.totalAMTLinkNodeSize)
|
||||
str += fmt.Sprintf("%d value nodes %d bytes\n", asc.totalAMTValueNodes, asc.totalAMTValueNodeSize)
|
||||
str += fmt.Sprintf("Total bytes: %d\n------------\n", asc.totalAMTLinkNodeSize+asc.totalAMTValueNodeSize)
|
||||
return str
|
||||
}
|
||||
|
||||
func (asc *amtStatCollector) record(ctx context.Context, nd format.Node) error {
|
||||
size, err := nd.Size()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var node AMTNode
|
||||
if err := node.UnmarshalCBOR(bytes.NewReader(nd.RawData())); err != nil {
|
||||
// try to deserialize root
|
||||
var root AMTRoot
|
||||
if err := root.UnmarshalCBOR(bytes.NewReader(nd.RawData())); err != nil {
|
||||
return err
|
||||
}
|
||||
node = root.AMTNode
|
||||
}
|
||||
|
||||
asc.statsLk.Lock()
|
||||
defer asc.statsLk.Unlock()
|
||||
|
||||
link := len(node.Links) > 0
|
||||
value := len(node.Values) > 0
|
||||
|
||||
if link {
|
||||
asc.totalAMTLinks += len(node.Links)
|
||||
asc.totalAMTLinkNodes += 1
|
||||
asc.totalAMTLinkNodeSize += int(size)
|
||||
} else if value {
|
||||
asc.totalAMTValues += len(node.Values)
|
||||
asc.totalAMTValueNodes += 1
|
||||
asc.totalAMTValueNodeSize += int(size)
|
||||
} else {
|
||||
return xerrors.Errorf("unexpected AMT node %x: neither link nor value", nd.RawData())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (asc *amtStatCollector) walkLinks(ctx context.Context, c cid.Cid) ([]*format.Link, error) {
|
||||
nd, err := asc.ds.Get(ctx, c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := asc.record(ctx, nd); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return asc.walk(nd)
|
||||
}
|
||||
|
||||
func carWalkFunc(nd format.Node) (out []*format.Link, err error) {
|
||||
for _, link := range nd.Links() {
|
||||
if link.Cid.Prefix().Codec == cid.FilCommitmentSealed || link.Cid.Prefix().Codec == cid.FilCommitmentUnsealed {
|
||||
continue
|
||||
}
|
||||
out = append(out, link)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
var amtBenchCmd = &cli.Command{
|
||||
Name: "amt",
|
||||
Usage: "Benchmark AMT churn",
|
||||
Flags: []cli.Flag{
|
||||
&cli.IntFlag{
|
||||
Name: "rounds",
|
||||
Usage: "rounds of churn to measure",
|
||||
Value: 1,
|
||||
},
|
||||
&cli.IntFlag{
|
||||
Name: "interval",
|
||||
Usage: "AMT idx interval for churning values",
|
||||
Value: 2880,
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
bs := blockstore.NewMemory()
|
||||
ctx := c.Context
|
||||
store := adt.WrapStore(ctx, cbor.NewCborStore(bs))
|
||||
|
||||
// Setup in memory blockstore
|
||||
const bitwidth = 6
|
||||
array, err := adt.MakeEmptyArray(store, bitwidth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Using motivating empirical example: market actor states AMT
|
||||
// Create 40,000,000 states for realistic workload
|
||||
fmt.Printf("Populating AMT\n")
|
||||
for i := 0; i < 40000000; i++ {
|
||||
array.Set(uint64(i), &market.DealState{
|
||||
SectorStartEpoch: abi.ChainEpoch(2000000 + i),
|
||||
LastUpdatedEpoch: abi.ChainEpoch(-1),
|
||||
SlashEpoch: -1,
|
||||
VerifiedClaim: verifreg.AllocationId(i),
|
||||
})
|
||||
}
|
||||
|
||||
r, err := array.Root()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Measure ratio of internal / leaf nodes / sizes
|
||||
dag := merkledag.NewDAGService(blockservice.New(bs, offline.Exchange(bs)))
|
||||
asc := &amtStatCollector{
|
||||
ds: dag,
|
||||
walk: carWalkFunc,
|
||||
}
|
||||
|
||||
fmt.Printf("Measuring AMT\n")
|
||||
seen := cid.NewSet()
|
||||
if err := merkledag.Walk(ctx, asc.walkLinks, r, seen.Visit, merkledag.Concurrent()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("%s\n", asc)
|
||||
|
||||
// Overwrite ids with idx % interval: one epoch of market cron
|
||||
rounds := c.Int("rounds")
|
||||
interval := c.Int("interval")
|
||||
|
||||
fmt.Printf("Overwrite 1 out of %d values for %d rounds\n", interval, rounds)
|
||||
array, err = adt.AsArray(store, r, bitwidth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
roots := make([]cid.Cid, rounds)
|
||||
for j := 0; j < rounds; j++ {
|
||||
if j%10 == 0 {
|
||||
fmt.Printf("round: %d\n", j)
|
||||
}
|
||||
for i := j; i < 40000000; i += interval {
|
||||
if i%interval == j {
|
||||
array.Set(uint64(i), &market.DealState{
|
||||
SectorStartEpoch: abi.ChainEpoch(2000000 + i),
|
||||
LastUpdatedEpoch: abi.ChainEpoch(1),
|
||||
SlashEpoch: -1,
|
||||
VerifiedClaim: verifreg.AllocationId(i),
|
||||
})
|
||||
}
|
||||
}
|
||||
roots[j], err = array.Root()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Measure churn
|
||||
dag = merkledag.NewDAGService(blockservice.New(bs, offline.Exchange(bs)))
|
||||
asc = &amtStatCollector{
|
||||
ds: dag,
|
||||
walk: carWalkFunc,
|
||||
}
|
||||
|
||||
fmt.Printf("Measuring %d rounds of churn\n", rounds)
|
||||
|
||||
for _, r := range roots {
|
||||
if err := merkledag.Walk(ctx, asc.walkLinks, r, seen.Visit, merkledag.Concurrent()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("%s\n", asc)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var sealBenchCmd = &cli.Command{
|
||||
Name: "sealing",
|
||||
Usage: "Benchmark seal and winning post and window post",
|
||||
|
1
go.mod
1
go.mod
@ -89,6 +89,7 @@ require (
|
||||
github.com/ipfs/go-fs-lock v0.0.7
|
||||
github.com/ipfs/go-graphsync v0.14.6
|
||||
github.com/ipfs/go-ipfs-blocksutil v0.0.1
|
||||
github.com/ipfs/go-ipfs-exchange-offline v0.3.0
|
||||
github.com/ipfs/go-ipld-cbor v0.0.6
|
||||
github.com/ipfs/go-ipld-format v0.5.0
|
||||
github.com/ipfs/go-log/v2 v2.5.1
|
||||
|
1
go.sum
1
go.sum
@ -759,6 +759,7 @@ github.com/ipfs/go-ipfs-exchange-interface v0.2.0/go.mod h1:z6+RhJuDQbqKguVyslSO
|
||||
github.com/ipfs/go-ipfs-exchange-offline v0.0.1/go.mod h1:WhHSFCVYX36H/anEKQboAzpUws3x7UeEGkzQc3iNkM0=
|
||||
github.com/ipfs/go-ipfs-exchange-offline v0.1.1/go.mod h1:vTiBRIbzSwDD0OWm+i3xeT0mO7jG2cbJYatp3HPk5XY=
|
||||
github.com/ipfs/go-ipfs-exchange-offline v0.3.0 h1:c/Dg8GDPzixGd0MC8Jh6mjOwU57uYokgWRFidfvEkuA=
|
||||
github.com/ipfs/go-ipfs-exchange-offline v0.3.0/go.mod h1:MOdJ9DChbb5u37M1IcbrRB02e++Z7521fMxqCNRrz9s=
|
||||
github.com/ipfs/go-ipfs-files v0.0.3/go.mod h1:INEFm0LL2LWXBhNJ2PMIIb2w45hpXgPjNoE7yA8Y1d4=
|
||||
github.com/ipfs/go-ipfs-files v0.0.4/go.mod h1:INEFm0LL2LWXBhNJ2PMIIb2w45hpXgPjNoE7yA8Y1d4=
|
||||
github.com/ipfs/go-ipfs-files v0.3.0 h1:fallckyc5PYjuMEitPNrjRfpwl7YFt69heCOUhsbGxQ=
|
||||
|
Loading…
Reference in New Issue
Block a user