diff --git a/chain/vm/gas.go b/chain/vm/gas.go index f1dcc933d..e6bde25bf 100644 --- a/chain/vm/gas.go +++ b/chain/vm/gas.go @@ -186,6 +186,15 @@ func (ps pricedSyscalls) VerifyConsensusFault(h1 []byte, h2 []byte, extra []byte } func (ps pricedSyscalls) BatchVerifySeals(inp map[address.Address][]abi.SealVerifyInfo) (map[address.Address][]bool, error) { - ps.chargeGas(newGasCharge("BatchVerifySeals", 0, 0)) // TODO: this is only called by the cron actor. Should we even charge gas? + var gasChargeSum GasCharge + gasChargeSum.Name = "BatchVerifySeals" + ps.chargeGas(gasChargeSum) // TODO: this is only called by the cron actor. Should we even charge gas? + + for _, svis := range inp { + for _, svi := range svis { + ch := ps.pl.OnVerifySeal(svi) + ps.chargeGas(newGasCharge("BatchVerifySingle", 0, 0).WithVirtual(ch.VirtualCompute+ch.ComputeGas, 0)) + } + } return ps.under.BatchVerifySeals(inp) } diff --git a/chain/vm/gas_v0.go b/chain/vm/gas_v0.go index 6994fd544..072c1f140 100644 --- a/chain/vm/gas_v0.go +++ b/chain/vm/gas_v0.go @@ -103,17 +103,17 @@ func (pl *pricelistV0) OnMethodInvocation(value abi.TokenAmount, methodNum abi.M if methodNum != builtin.MethodSend { ret += pl.sendInvokeMethod } - return newGasCharge("OnMethodInvocation", ret, 0) + return newGasCharge("OnMethodInvocation", ret, 0).WithVirtual(ret*15000, 0) } // OnIpldGet returns the gas used for storing an object func (pl *pricelistV0) OnIpldGet(dataSize int) GasCharge { - return newGasCharge("OnIpldGet", pl.ipldGetBase+int64(dataSize)*pl.ipldGetPerByte, 0).WithExtra(dataSize) + return newGasCharge("OnIpldGet", pl.ipldGetBase+int64(dataSize)*pl.ipldGetPerByte, 0).WithExtra(dataSize).WithVirtual(pl.ipldGetBase*13750+(pl.ipldGetPerByte*100), 0) } // OnIpldPut returns the gas used for storing an object func (pl *pricelistV0) OnIpldPut(dataSize int) GasCharge { - return newGasCharge("OnIpldPut", pl.ipldPutBase, int64(dataSize)*pl.ipldPutPerByte).WithExtra(dataSize) + return newGasCharge("OnIpldPut", pl.ipldPutBase, int64(dataSize)*pl.ipldPutPerByte).WithExtra(dataSize).WithVirtual(pl.ipldPutBase*8700+(pl.ipldPutPerByte*100), 0) } // OnCreateActor returns the gas used for creating an actor @@ -144,13 +144,13 @@ func (pl *pricelistV0) OnHashing(dataSize int) GasCharge { // OnComputeUnsealedSectorCid func (pl *pricelistV0) OnComputeUnsealedSectorCid(proofType abi.RegisteredSealProof, pieces []abi.PieceInfo) GasCharge { // TODO: this needs more cost tunning, check with @lotus - return newGasCharge("OnComputeUnsealedSectorCid", pl.computeUnsealedSectorCidBase, 0) + return newGasCharge("OnComputeUnsealedSectorCid", pl.computeUnsealedSectorCidBase, 0).WithVirtual(pl.computeUnsealedSectorCidBase*24500, 0) } // OnVerifySeal func (pl *pricelistV0) OnVerifySeal(info abi.SealVerifyInfo) GasCharge { // TODO: this needs more cost tunning, check with @lotus - return newGasCharge("OnVerifySeal", pl.verifySealBase, 0) + return newGasCharge("OnVerifySeal", pl.verifySealBase, 0).WithVirtual(pl.verifySealBase*177500, 0) } // OnVerifyPost diff --git a/chain/vm/syscalls.go b/chain/vm/syscalls.go index a5910121d..a6a589761 100644 --- a/chain/vm/syscalls.go +++ b/chain/vm/syscalls.go @@ -241,10 +241,12 @@ func (ss *syscallShim) VerifySignature(sig crypto.Signature, addr address.Addres return sigs.Verify(&sig, kaddr, input) } +var BatchSealVerifyParallelism = goruntime.NumCPU() + func (ss *syscallShim) BatchVerifySeals(inp map[address.Address][]abi.SealVerifyInfo) (map[address.Address][]bool, error) { out := make(map[address.Address][]bool) - sema := make(chan struct{}, goruntime.NumCPU()) + sema := make(chan struct{}, BatchSealVerifyParallelism) var wg sync.WaitGroup for addr, seals := range inp { diff --git a/cmd/lotus-bench/import.go b/cmd/lotus-bench/import.go index 11d58d9b8..f9c20ac9a 100644 --- a/cmd/lotus-bench/import.go +++ b/cmd/lotus-bench/import.go @@ -5,7 +5,9 @@ import ( "encoding/json" "fmt" "io/ioutil" + "math" "os" + "runtime" "runtime/pprof" "sort" "time" @@ -44,8 +46,14 @@ var importBenchCmd = &cli.Command{ Name: "height", Usage: "halt validation after given height", }, + &cli.IntFlag{ + Name: "batch-seal-verify-threads", + Usage: "set the parallelism factor for batch seal verification", + Value: runtime.NumCPU(), + }, }, Action: func(cctx *cli.Context) error { + vm.BatchSealVerifyParallelism = cctx.Int("batch-seal-verify-threads") if !cctx.Args().Present() { fmt.Println("must pass car file of chain to benchmark importing") return nil @@ -162,6 +170,58 @@ type Invocation struct { Invoc *api.InvocResult } +const GasPerNs = 10 + +func countGasCosts(et *types.ExecutionTrace) (int64, int64) { + var cgas, vgas int64 + + for _, gc := range et.GasCharges { + cgas += gc.ComputeGas + vgas += gc.VirtualComputeGas + } + + for _, sub := range et.Subcalls { + c, v := countGasCosts(&sub) + cgas += c + vgas += v + } + + return cgas, vgas +} + +func compStats(vals []float64) (float64, float64) { + var sum float64 + + for _, v := range vals { + sum += v + } + + av := sum / float64(len(vals)) + + var varsum float64 + for _, v := range vals { + delta := av - v + varsum += delta * delta + } + + return av, math.Sqrt(varsum / float64(len(vals))) +} + +func tallyGasCharges(charges map[string][]float64, et *types.ExecutionTrace) { + for _, gc := range et.GasCharges { + + compGas := gc.ComputeGas + gc.VirtualComputeGas + ratio := float64(compGas) / float64(gc.TimeTaken.Nanoseconds()) + + charges[gc.Name] = append(charges[gc.Name], 1/(ratio/GasPerNs)) + //fmt.Printf("%s: %d, %s: %0.2f\n", gc.Name, compGas, gc.TimeTaken, 1/(ratio/GasPerNs)) + for _, sub := range et.Subcalls { + tallyGasCharges(charges, &sub) + } + } + +} + var importAnalyzeCmd = &cli.Command{ Name: "analyze", Action: func(cctx *cli.Context) error { @@ -180,6 +240,8 @@ var importAnalyzeCmd = &cli.Command{ return err } + chargeDeltas := make(map[string][]float64) + var invocs []Invocation var totalTime time.Duration for i, r := range results { @@ -191,9 +253,29 @@ var importAnalyzeCmd = &cli.Command{ TipSet: r.TipSet, Invoc: inv, }) + + cgas, vgas := countGasCosts(&inv.ExecutionTrace) + fmt.Printf("Invocation: %d %s: %s %d -> %0.2f\n", inv.Msg.Method, inv.Msg.To, inv.Duration, cgas+vgas, float64(GasPerNs*inv.Duration.Nanoseconds())/float64(cgas+vgas)) + + tallyGasCharges(chargeDeltas, &inv.ExecutionTrace) + } } + var keys []string + for k := range chargeDeltas { + keys = append(keys, k) + } + + fmt.Println("Gas Price Deltas") + sort.Strings(keys) + for _, k := range keys { + vals := chargeDeltas[k] + av, stdev := compStats(vals) + + fmt.Printf("%s: incr by %f (%f)\n", k, av, stdev) + } + sort.Slice(invocs, func(i, j int) bool { return invocs[i].Invoc.Duration > invocs[j].Invoc.Duration }) diff --git a/cmd/lotus-storage-miner/init.go b/cmd/lotus-storage-miner/init.go index 29e82cc2f..14972c69a 100644 --- a/cmd/lotus-storage-miner/init.go +++ b/cmd/lotus-storage-miner/init.go @@ -460,7 +460,7 @@ func storageMinerInit(ctx context.Context, cctx *cli.Context, api lapi.FullNode, } if cerr != nil { - return xerrors.Errorf("failed to configure storage miner: %w", err) + return xerrors.Errorf("failed to configure storage miner: %w", cerr) } }