2019-11-27 02:47:08 +00:00
package main
import (
2019-11-29 18:48:07 +00:00
"bytes"
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"
2019-11-27 12:12:42 +00:00
ffi "github.com/filecoin-project/filecoin-ffi"
2019-11-27 02:47:08 +00:00
"github.com/ipfs/go-datastore"
logging "github.com/ipfs/go-log"
"github.com/mitchellh/go-homedir"
"golang.org/x/xerrors"
"gopkg.in/urfave/cli.v2"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/address"
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
"github.com/filecoin-project/lotus/lib/sectorbuilder"
)
var log = logging . Logger ( "lotus-bench" )
type BenchResults struct {
SectorSize uint64
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" )
2019-12-17 19:59:14 +00:00
build . SectorSizes = append ( build . SectorSizes , 1024 )
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-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
}
sectorSize := uint64 ( sectorSizeInt )
2019-11-28 01:43:36 +00:00
2019-11-27 02:47:08 +00:00
mds := datastore . NewMapDatastore ( )
cfg := & sectorbuilder . Config {
Miner : maddr ,
2019-11-28 01:43:36 +00:00
SectorSize : sectorSize ,
2019-11-27 02:47:08 +00:00
WorkerThreads : 2 ,
2019-12-16 13:49:58 +00:00
Dir : 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
}
}
2019-12-04 19:44:15 +00:00
if err := build . GetParams ( 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
}
2019-12-01 22:37:53 +00:00
dataSize := sectorbuilder . UserBytesForSectorSize ( sectorSize )
2019-11-27 02:47:08 +00:00
var sealTimings [ ] SealingResult
2019-11-27 12:12:42 +00:00
var sealedSectors [ ] ffi . PublicSectorInfo
2019-11-27 02:47:08 +00:00
numSectors := uint64 ( 1 )
2019-12-10 13:01:17 +00:00
for i := uint64 ( 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 ) ) )
pi , err := sb . AddPiece ( dataSize , 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" ) ) )
ticket := sectorbuilder . SealTicket {
TicketBytes : trand ,
}
log . Info ( "Running replication..." )
pieces := [ ] sectorbuilder . PublicPieceInfo { pi }
pco , err := sb . SealPreCommit ( i , ticket , pieces )
if err != nil {
return xerrors . Errorf ( "commit: %w" , err )
}
precommit := time . Now ( )
2019-11-27 12:12:42 +00:00
sealedSectors = append ( sealedSectors , ffi . PublicSectorInfo {
2019-11-27 02:47:08 +00:00
CommR : pco . CommR ,
SectorID : i ,
} )
seed := sectorbuilder . SealSeed {
BlockHeight : 101 ,
TicketBytes : [ 32 ] byte { 1 , 2 , 3 , 4 , 5 } ,
}
log . Info ( "Generating PoRep for sector" )
proof , err := sb . SealCommit ( i , ticket , seed , pieces , pco )
if err != nil {
return err
}
sealcommit := time . Now ( )
2019-11-28 01:43:36 +00:00
commD := pi . CommP
2019-11-29 18:48:07 +00:00
ok , err := sectorbuilder . VerifySeal ( sectorSize , pco . CommR [ : ] , commD [ : ] , maddr , ticket . TicketBytes [ : ] , seed . TicketBytes [ : ] , i , proof )
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-01 22:37:53 +00:00
log . Info ( "Unsealing sector" )
rc , err := sb . ReadPieceFromSealedSector ( 1 , 0 , dataSize , ticket . TicketBytes [ : ] , commD [ : ] )
if err != nil {
return err
}
unseal := time . Now ( )
if err := rc . Close ( ) ; err != nil {
return err
}
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
}
var genmm map [ string ] genesis . GenesisMiner
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 {
sealedSectors = append ( sealedSectors , ffi . PublicSectorInfo {
CommR : s . CommR ,
SectorID : s . SectorID ,
} )
}
}
2019-11-27 02:47:08 +00:00
log . Info ( "generating election post candidates" )
sinfos := sectorbuilder . NewSortedPublicSectorInfo ( sealedSectors )
candidates , err := sb . GenerateEPostCandidates ( sinfos , challenge , [ ] uint64 { } )
if err != nil {
return err
}
gencandidates := time . Now ( )
2019-11-29 18:48:07 +00:00
log . Info ( "computing election post snark (cold)" )
proof1 , err := sb . ComputeElectionPoSt ( sinfos , challenge [ : ] , candidates [ : 1 ] )
if err != nil {
return err
}
epost1 := time . Now ( )
log . Info ( "computing election post snark (hot)" )
proof2 , err := sb . ComputeElectionPoSt ( sinfos , 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
2019-11-29 18:48:07 +00:00
if ! bytes . Equal ( proof1 , proof2 ) {
log . Warn ( "separate epost calls returned different proof values (this might be bad)" )
}
ok , err := sectorbuilder . VerifyElectionPost ( context . TODO ( ) , sectorSize , sinfos , challenge [ : ] , proof1 , candidates [ : 1 ] , maddr )
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 ( )
ok , err = sectorbuilder . VerifyElectionPost ( context . TODO ( ) , sectorSize , sinfos , challenge [ : ] , proof2 , candidates [ : 1 ] , maddr )
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 {
2019-11-27 02:47:08 +00:00
SectorSize : cfg . SectorSize ,
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 )
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
}
2019-12-10 18:04:13 +00:00
fmt . Printf ( "generate candidates: %s (%s)\n" , bo . PostGenerateCandidates , bps ( bo . SectorSize * uint64 ( len ( bo . SealingResults ) ) , bo . PostGenerateCandidates ) )
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
func bps ( data uint64 , d time . Duration ) string {
2019-12-10 19:53:39 +00:00
bdata := new ( big . Int ) . SetUint64 ( data )
bdata = bdata . Mul ( bdata , big . NewInt ( time . Second . Nanoseconds ( ) ) )
bps := bdata . Div ( bdata , big . NewInt ( d . Nanoseconds ( ) ) )
2019-12-16 13:49:58 +00:00
return ( types . BigInt { bps } ) . SizeStr ( ) + "/s"
2019-12-10 18:04:13 +00:00
}