2019-11-27 02:47:08 +00:00
package main
import (
2019-11-28 01:43:36 +00:00
"context"
2019-11-27 02:47:08 +00:00
"crypto/sha256"
2019-12-10 13:22:39 +00:00
"encoding/json"
2019-11-27 02:47:08 +00:00
"fmt"
"io/ioutil"
2019-12-10 19:53:39 +00:00
"math/big"
2019-11-27 02:47:08 +00:00
"math/rand"
"os"
"path/filepath"
"time"
2019-12-16 13:49:58 +00:00
"github.com/docker/go-units"
2020-01-02 19:08:49 +00:00
paramfetch "github.com/filecoin-project/go-paramfetch"
2020-02-10 19:16:36 +00:00
"github.com/filecoin-project/specs-actors/actors/abi"
2019-11-27 02:47:08 +00:00
"github.com/ipfs/go-datastore"
2020-01-08 19:10:57 +00:00
logging "github.com/ipfs/go-log/v2"
2019-11-27 02:47:08 +00:00
"github.com/mitchellh/go-homedir"
"golang.org/x/xerrors"
"gopkg.in/urfave/cli.v2"
2019-12-19 20:13:17 +00:00
"github.com/filecoin-project/go-address"
2020-01-07 16:18:35 +00:00
"github.com/filecoin-project/go-sectorbuilder"
2020-02-27 22:23:05 +00:00
lapi "github.com/filecoin-project/lotus/api"
2019-11-27 02:47:08 +00:00
"github.com/filecoin-project/lotus/build"
2019-12-10 19:53:39 +00:00
"github.com/filecoin-project/lotus/chain/types"
2019-12-10 13:22:39 +00:00
"github.com/filecoin-project/lotus/genesis"
2019-11-27 02:47:08 +00:00
)
var log = logging . Logger ( "lotus-bench" )
type BenchResults struct {
2020-02-10 19:16:36 +00:00
SectorSize abi . SectorSize
2019-11-27 02:47:08 +00:00
SealingResults [ ] SealingResult
PostGenerateCandidates time . Duration
2019-11-29 18:48:07 +00:00
PostEProofCold time . Duration
PostEProofHot time . Duration
VerifyEPostCold time . Duration
VerifyEPostHot time . Duration
2019-11-27 02:47:08 +00:00
}
type SealingResult struct {
AddPiece time . Duration
PreCommit time . Duration
Commit time . Duration
2019-11-28 01:43:36 +00:00
Verify time . Duration
2019-12-01 22:37:53 +00:00
Unseal time . Duration
2019-11-27 02:47:08 +00:00
}
func main ( ) {
logging . SetLogLevel ( "*" , "INFO" )
log . Info ( "Starting lotus-bench" )
2020-02-28 00:12:52 +00:00
build . SectorSizes = append ( build . SectorSizes , 2048 )
2019-12-17 19:59:14 +00:00
2019-11-27 02:47:08 +00:00
app := & cli . App {
Name : "lotus-bench" ,
Usage : "Benchmark performance of lotus on your hardware" ,
2019-12-11 22:00:39 +00:00
Version : build . UserVersion ,
2019-11-27 02:47:08 +00:00
Flags : [ ] cli . Flag {
& cli . StringFlag {
Name : "storage-dir" ,
Value : "~/.lotus-bench" ,
Usage : "Path to the storage directory that will store sectors long term" ,
} ,
2019-12-11 02:06:28 +00:00
& cli . StringFlag {
2019-11-27 02:47:08 +00:00
Name : "sector-size" ,
2019-12-11 02:06:28 +00:00
Value : "1GiB" ,
Usage : "size of the sectors in bytes, i.e. 32GiB" ,
2019-11-27 02:47:08 +00:00
} ,
2019-12-02 19:25:10 +00:00
& cli . BoolFlag {
Name : "no-gpu" ,
Usage : "disable gpu usage for the benchmark run" ,
} ,
2019-12-10 13:22:39 +00:00
& cli . StringFlag {
Name : "miner-addr" ,
Usage : "pass miner address (only necessary if using existing sectorbuilder)" ,
Value : "t0101" ,
} ,
2019-12-10 13:01:17 +00:00
& cli . StringFlag {
Name : "benchmark-existing-sectorbuilder" ,
Usage : "pass a directory to run election-post timings on an existing sectorbuilder" ,
} ,
2019-12-10 14:05:41 +00:00
& cli . BoolFlag {
Name : "json-out" ,
Usage : "output results in json format" ,
} ,
2019-12-19 19:45:15 +00:00
& cli . BoolFlag {
Name : "skip-unseal" ,
Usage : "skip the unseal portion of the benchmark" ,
} ,
2019-11-27 02:47:08 +00:00
} ,
Action : func ( c * cli . Context ) error {
2019-12-02 19:25:10 +00:00
if c . Bool ( "no-gpu" ) {
os . Setenv ( "BELLMAN_NO_GPU" , "1" )
}
2019-11-27 02:47:08 +00:00
2019-12-10 13:01:17 +00:00
robench := c . String ( "benchmark-existing-sectorbuilder" )
2019-11-27 02:47:08 +00:00
2019-12-10 13:01:17 +00:00
var sbdir string
if robench == "" {
sdir , err := homedir . Expand ( c . String ( "storage-dir" ) )
if err != nil {
return err
}
os . MkdirAll ( sdir , 0775 )
tsdir , err := ioutil . TempDir ( sdir , "bench" )
if err != nil {
return err
2019-11-27 02:47:08 +00:00
}
2019-12-10 13:01:17 +00:00
defer func ( ) {
if err := os . RemoveAll ( tsdir ) ; err != nil {
log . Warn ( "remove all: " , err )
}
} ( )
sbdir = tsdir
} else {
2019-12-10 13:47:49 +00:00
exp , err := homedir . Expand ( robench )
if err != nil {
return err
}
sbdir = exp
2019-12-10 13:01:17 +00:00
}
2019-11-27 02:47:08 +00:00
2019-12-10 13:22:39 +00:00
maddr , err := address . NewFromString ( c . String ( "miner-addr" ) )
2019-11-27 02:47:08 +00:00
if err != nil {
return err
}
2019-12-16 19:44:48 +00:00
sectorSizeInt , err := units . RAMInBytes ( c . String ( "sector-size" ) )
2019-12-11 02:06:28 +00:00
if err != nil {
return err
}
2020-02-10 19:16:36 +00:00
sectorSize := abi . SectorSize ( sectorSizeInt )
2019-11-28 01:43:36 +00:00
2020-02-27 22:23:05 +00:00
ppt , spt , err := lapi . ProofTypeFromSectorSize ( sectorSize )
if err != nil {
return err
}
2019-11-27 02:47:08 +00:00
mds := datastore . NewMapDatastore ( )
cfg := & sectorbuilder . Config {
Miner : maddr ,
2020-02-27 22:23:05 +00:00
SealProofType : spt ,
PoStProofType : ppt ,
2019-11-27 02:47:08 +00:00
WorkerThreads : 2 ,
2020-01-28 23:08:02 +00:00
Paths : sectorbuilder . SimplePath ( sbdir ) ,
2019-11-27 02:47:08 +00:00
}
2019-12-10 13:01:17 +00:00
if robench == "" {
2019-12-16 13:49:58 +00:00
if err := os . MkdirAll ( sbdir , 0775 ) ; err != nil {
return err
2019-11-27 02:47:08 +00:00
}
}
2020-02-10 19:16:36 +00:00
if err := paramfetch . GetParams ( build . ParametersJson ( ) , uint64 ( sectorSize ) ) ; err != nil {
2019-11-27 02:47:08 +00:00
return xerrors . Errorf ( "getting params: %w" , err )
}
sb , err := sectorbuilder . New ( cfg , mds )
if err != nil {
return err
}
2020-02-27 22:23:05 +00:00
amid , err := address . IDFromAddress ( maddr )
if err != nil {
return err
}
mid := abi . ActorID ( amid )
2019-11-27 02:47:08 +00:00
var sealTimings [ ] SealingResult
2020-02-27 22:23:05 +00:00
var sealedSectors [ ] abi . SectorInfo
2020-02-10 19:16:36 +00:00
numSectors := abi . SectorNumber ( 1 )
for i := abi . SectorNumber ( 1 ) ; i <= numSectors && robench == "" ; i ++ {
2019-11-27 02:47:08 +00:00
start := time . Now ( )
log . Info ( "Writing piece into sector..." )
2019-12-01 22:37:53 +00:00
r := rand . New ( rand . NewSource ( 100 + int64 ( i ) ) )
2020-02-27 22:23:05 +00:00
pi , err := sb . AddPiece ( context . TODO ( ) , abi . PaddedPieceSize ( sectorSize ) . Unpadded ( ) , i , r , nil )
2019-11-27 02:47:08 +00:00
if err != nil {
return err
}
addpiece := time . Now ( )
trand := sha256 . Sum256 ( [ ] byte ( c . String ( "ticket-preimage" ) ) )
2020-02-27 22:23:05 +00:00
ticket := abi . SealRandomness ( trand [ : ] )
2019-11-27 02:47:08 +00:00
log . Info ( "Running replication..." )
2020-02-27 22:23:05 +00:00
pieces := [ ] abi . PieceInfo { pi }
commR , commD , err := sb . SealPreCommit ( context . TODO ( ) , i , ticket , pieces )
2019-11-27 02:47:08 +00:00
if err != nil {
return xerrors . Errorf ( "commit: %w" , err )
}
precommit := time . Now ( )
2020-02-27 22:23:05 +00:00
sealedSectors = append ( sealedSectors , abi . SectorInfo {
RegisteredProof : ppt ,
SectorNumber : i ,
SealedCID : commR ,
2019-11-27 02:47:08 +00:00
} )
2020-02-27 22:23:05 +00:00
seed := lapi . SealSeed {
Epoch : 101 ,
2020-02-28 20:52:14 +00:00
Value : [ ] byte { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 , 16 , 17 , 18 , 19 , 20 , 21 , 22 , 23 , 24 , 25 , 26 , 27 , 28 , 29 , 30 , 31 , 255 } ,
2019-11-27 02:47:08 +00:00
}
log . Info ( "Generating PoRep for sector" )
2020-02-27 22:23:05 +00:00
proof , err := sb . SealCommit ( context . TODO ( ) , i , ticket , seed . Value , pieces , commR , commD )
2019-11-27 02:47:08 +00:00
if err != nil {
return err
}
sealcommit := time . Now ( )
2020-02-27 22:23:05 +00:00
svi := abi . SealVerifyInfo {
SectorID : abi . SectorID { Miner : abi . ActorID ( mid ) , Number : i } ,
OnChain : abi . OnChainSealVerifyInfo {
SealedCID : commR ,
InteractiveEpoch : seed . Epoch ,
RegisteredProof : ppt ,
Proof : proof ,
DealIDs : nil ,
SectorNumber : i ,
SealRandEpoch : 0 ,
} ,
Randomness : ticket ,
InteractiveRandomness : seed . Value ,
UnsealedCID : commD ,
}
ok , err := sectorbuilder . ProofVerifier . VerifySeal ( svi )
2019-11-29 18:48:07 +00:00
if err != nil {
return err
}
if ! ok {
return xerrors . Errorf ( "porep proof for sector %d was invalid" , i )
}
2019-11-28 01:43:36 +00:00
verifySeal := time . Now ( )
2019-11-27 02:47:08 +00:00
2019-12-19 19:45:15 +00:00
if ! c . Bool ( "skip-unseal" ) {
log . Info ( "Unsealing sector" )
2020-02-27 22:23:05 +00:00
rc , err := sb . ReadPieceFromSealedSector ( context . TODO ( ) , 1 , 0 , abi . UnpaddedPieceSize ( sectorSize ) , ticket , commD )
2019-12-19 19:45:15 +00:00
if err != nil {
return err
}
2019-12-01 22:37:53 +00:00
2019-12-19 19:45:15 +00:00
if err := rc . Close ( ) ; err != nil {
return err
}
2019-12-01 22:37:53 +00:00
}
2019-12-19 19:45:15 +00:00
unseal := time . Now ( )
2019-12-01 22:37:53 +00:00
2019-11-27 02:47:08 +00:00
sealTimings = append ( sealTimings , SealingResult {
AddPiece : addpiece . Sub ( start ) ,
PreCommit : precommit . Sub ( addpiece ) ,
Commit : sealcommit . Sub ( precommit ) ,
2019-11-28 01:43:36 +00:00
Verify : verifySeal . Sub ( sealcommit ) ,
2019-12-01 22:37:53 +00:00
Unseal : unseal . Sub ( verifySeal ) ,
2019-11-27 02:47:08 +00:00
} )
}
beforePost := time . Now ( )
var challenge [ 32 ] byte
rand . Read ( challenge [ : ] )
2019-12-10 13:22:39 +00:00
if robench != "" {
// TODO: this assumes we only ever benchmark a preseal
// sectorbuilder directory... we need a better way to handle
// this in other cases
fdata , err := ioutil . ReadFile ( filepath . Join ( sbdir , "pre-seal-" + maddr . String ( ) + ".json" ) )
if err != nil {
return err
}
2020-02-25 21:09:22 +00:00
var genmm map [ string ] genesis . Miner
2019-12-10 13:22:39 +00:00
if err := json . Unmarshal ( fdata , & genmm ) ; err != nil {
return err
}
genm , ok := genmm [ maddr . String ( ) ]
if ! ok {
return xerrors . Errorf ( "preseal file didnt have expected miner in it" )
}
for _ , s := range genm . Sectors {
2020-02-27 22:23:05 +00:00
sealedSectors = append ( sealedSectors , abi . SectorInfo {
SealedCID : s . CommR ,
SectorNumber : s . SectorID ,
2019-12-10 13:22:39 +00:00
} )
}
}
2019-11-27 02:47:08 +00:00
log . Info ( "generating election post candidates" )
2020-02-27 22:23:05 +00:00
fcandidates , err := sb . GenerateEPostCandidates ( sealedSectors , abi . PoStRandomness ( challenge [ : ] ) , [ ] abi . SectorNumber { } )
2019-11-27 02:47:08 +00:00
if err != nil {
return err
}
2020-02-27 22:23:05 +00:00
var candidates [ ] abi . PoStCandidate
for _ , c := range fcandidates {
candidates = append ( candidates , c . Candidate )
}
2019-11-27 02:47:08 +00:00
gencandidates := time . Now ( )
2019-11-29 18:48:07 +00:00
log . Info ( "computing election post snark (cold)" )
2020-02-27 22:23:05 +00:00
proof1 , err := sb . ComputeElectionPoSt ( sealedSectors , challenge [ : ] , candidates [ : 1 ] )
2019-11-29 18:48:07 +00:00
if err != nil {
return err
}
epost1 := time . Now ( )
log . Info ( "computing election post snark (hot)" )
2020-02-27 22:23:05 +00:00
proof2 , err := sb . ComputeElectionPoSt ( sealedSectors , challenge [ : ] , candidates [ : 1 ] )
2019-11-27 02:47:08 +00:00
if err != nil {
return err
}
2019-11-29 18:48:07 +00:00
epost2 := time . Now ( )
2019-11-27 02:47:08 +00:00
2020-02-27 22:23:05 +00:00
ccount := sectorbuilder . ElectionPostChallengeCount ( uint64 ( len ( sealedSectors ) ) , 0 )
2019-11-29 18:48:07 +00:00
2020-02-27 22:23:05 +00:00
pvi1 := abi . PoStVerifyInfo {
Randomness : abi . PoStRandomness ( challenge [ : ] ) ,
Candidates : candidates [ : 1 ] ,
Proofs : proof1 ,
EligibleSectors : sealedSectors ,
Prover : abi . ActorID ( mid ) ,
ChallengeCount : ccount ,
}
ok , err := sectorbuilder . ProofVerifier . VerifyElectionPost ( context . TODO ( ) , pvi1 )
2019-11-28 01:43:36 +00:00
if err != nil {
return err
}
if ! ok {
log . Error ( "post verification failed" )
}
2019-11-29 18:48:07 +00:00
verifypost1 := time . Now ( )
2020-02-27 22:23:05 +00:00
pvi2 := abi . PoStVerifyInfo {
Randomness : abi . PoStRandomness ( challenge [ : ] ) ,
Candidates : candidates [ : 1 ] ,
Proofs : proof2 ,
EligibleSectors : sealedSectors ,
Prover : abi . ActorID ( mid ) ,
ChallengeCount : ccount ,
}
ok , err = sectorbuilder . ProofVerifier . VerifyElectionPost ( context . TODO ( ) , pvi2 )
2019-11-29 18:48:07 +00:00
if err != nil {
return err
}
if ! ok {
log . Error ( "post verification failed" )
}
verifypost2 := time . Now ( )
2019-11-28 01:43:36 +00:00
2019-12-10 18:04:13 +00:00
bo := BenchResults {
2020-02-27 22:23:05 +00:00
SectorSize : sectorSize ,
2019-11-27 02:47:08 +00:00
SealingResults : sealTimings ,
PostGenerateCandidates : gencandidates . Sub ( beforePost ) ,
2019-11-29 18:48:07 +00:00
PostEProofCold : epost1 . Sub ( gencandidates ) ,
PostEProofHot : epost2 . Sub ( epost1 ) ,
VerifyEPostCold : verifypost1 . Sub ( epost2 ) ,
VerifyEPostHot : verifypost2 . Sub ( verifypost1 ) ,
2019-11-27 02:47:08 +00:00
} // TODO: optionally write this as json to a file
2019-12-10 14:05:41 +00:00
if c . Bool ( "json-out" ) {
2019-12-10 18:04:13 +00:00
data , err := json . MarshalIndent ( bo , "" , " " )
2019-12-10 14:05:41 +00:00
if err != nil {
return err
}
fmt . Println ( string ( data ) )
} else {
2019-12-10 14:11:21 +00:00
fmt . Printf ( "results (%d)\n" , sectorSize )
2019-12-10 14:05:41 +00:00
if robench == "" {
2019-12-10 18:04:13 +00:00
fmt . Printf ( "seal: addPiece: %s (%s)\n" , bo . SealingResults [ 0 ] . AddPiece , bps ( bo . SectorSize , bo . SealingResults [ 0 ] . AddPiece ) ) // TODO: average across multiple sealings
fmt . Printf ( "seal: preCommit: %s (%s)\n" , bo . SealingResults [ 0 ] . PreCommit , bps ( bo . SectorSize , bo . SealingResults [ 0 ] . PreCommit ) )
fmt . Printf ( "seal: commit: %s (%s)\n" , bo . SealingResults [ 0 ] . Commit , bps ( bo . SectorSize , bo . SealingResults [ 0 ] . Commit ) )
fmt . Printf ( "seal: verify: %s\n" , bo . SealingResults [ 0 ] . Verify )
2020-01-19 14:56:37 +00:00
if ! c . Bool ( "skip-unseal" ) {
fmt . Printf ( "unseal: %s (%s)\n" , bo . SealingResults [ 0 ] . Unseal , bps ( bo . SectorSize , bo . SealingResults [ 0 ] . Unseal ) )
}
2019-12-10 14:05:41 +00:00
}
2020-02-10 19:16:36 +00:00
fmt . Printf ( "generate candidates: %s (%s)\n" , bo . PostGenerateCandidates , bps ( bo . SectorSize * abi . SectorSize ( len ( bo . SealingResults ) ) , bo . PostGenerateCandidates ) )
2019-12-10 18:04:13 +00:00
fmt . Printf ( "compute epost proof (cold): %s\n" , bo . PostEProofCold )
fmt . Printf ( "compute epost proof (hot): %s\n" , bo . PostEProofHot )
fmt . Printf ( "verify epost proof (cold): %s\n" , bo . VerifyEPostCold )
fmt . Printf ( "verify epost proof (hot): %s\n" , bo . VerifyEPostHot )
2019-12-10 13:01:17 +00:00
}
2019-11-27 02:47:08 +00:00
return nil
} ,
}
if err := app . Run ( os . Args ) ; err != nil {
log . Warn ( err )
return
}
}
2019-12-10 18:04:13 +00:00
2020-02-10 19:16:36 +00:00
func bps ( data abi . SectorSize , d time . Duration ) string {
bdata := new ( big . Int ) . SetUint64 ( uint64 ( data ) )
2019-12-10 19:53:39 +00:00
bdata = bdata . Mul ( bdata , big . NewInt ( time . Second . Nanoseconds ( ) ) )
bps := bdata . Div ( bdata , big . NewInt ( d . Nanoseconds ( ) ) )
2020-02-10 19:16:36 +00:00
return types . SizeStr ( types . BigInt { bps } ) + "/s"
2019-12-10 18:04:13 +00:00
}