diff --git a/go.mod b/go.mod index 35b9097ad..e04285a19 100644 --- a/go.mod +++ b/go.mod @@ -257,6 +257,7 @@ require ( golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/tools v0.1.12 // indirect + gonum.org/v1/gonum v0.11.0 // indirect google.golang.org/genproto v0.0.0-20220725144611-272f38e5d71b // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/ini.v1 v1.66.6 // indirect diff --git a/go.sum b/go.sum index fe4fdea8a..f524e007e 100644 --- a/go.sum +++ b/go.sum @@ -1660,6 +1660,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= +gonum.org/v1/gonum v0.11.0 h1:f1IJhK4Km5tBJmaiJXtk/PkL4cdVX6J+tGiM187uT5E= +gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= diff --git a/test/loadtime/cmd/report/main.go b/test/loadtime/cmd/report/main.go index e3bebf4f4..356dfcd27 100644 --- a/test/loadtime/cmd/report/main.go +++ b/test/loadtime/cmd/report/main.go @@ -1,28 +1,86 @@ package main import ( + "encoding/csv" + "flag" "fmt" + "log" + "os" + "strconv" + "strings" + "time" "github.com/tendermint/tendermint/store" "github.com/tendermint/tendermint/test/loadtime/report" dbm "github.com/tendermint/tm-db" ) +var ( + db = flag.String("database-type", "goleveldb", "the type of database holding the blockstore") + dir = flag.String("data-dir", "~/.tendermint/data", "path to the directory containing the tendermint databases") + csvOut = flag.String("csv", "", "dump the extracted latencies as raw csv for use in additional tooling") +) + func main() { - // parse out the db location - db, err := dbm.NewDB("blockstore", dbm.GoLevelDBBackend, "/home/william/.tendermint/data") + flag.Parse() + if *db == "" { + log.Fatalf("must specifiy a database-type") + } + if *dir == "" { + log.Fatalf("must specifiy a data-dir") + } + d := strings.TrimPrefix(*dir, "~/") + if d != *dir { + h, err := os.UserHomeDir() + if err != nil { + panic(err) + } + d = h + "/" + d + } + _, err := os.Stat(d) + if err != nil { + panic(err) + } + dbType := dbm.BackendType(*db) + db, err := dbm.NewDB("blockstore", dbType, d) if err != nil { panic(err) } s := store.NewBlockStore(db) + defer s.Close() r, err := report.GenerateFromBlockStore(s) if err != nil { panic(err) } - fmt.Println(r.ErrorCount) - fmt.Println(len(r.All)) - fmt.Println(r.Min) - fmt.Println(r.Max) - fmt.Println(r.Avg) - fmt.Println(r.StdDev) + if *csvOut != "" { + cf, err := os.Create(*csvOut) + if err != nil { + panic(err) + } + w := csv.NewWriter(cf) + err = w.WriteAll(toRecords(r.All)) + if err != nil { + panic(err) + } + return + } + + fmt.Printf(""+ + "Total Valid Tx: %d\n"+ + "Total Invalid Tx: %d\n"+ + "Minimum Latency: %s\n"+ + "Maximum Latency: %s\n"+ + "Average Latency: %s\n"+ + "Standard Deviation: %s\n", len(r.All), r.ErrorCount, r.Min, r.Max, r.Avg, r.StdDev) +} + +func toRecords(l []time.Duration) [][]string { + res := make([][]string, len(l)+1) + + res[0] = make([]string, 1) + res[0][0] = "duration_ns" + for i, v := range l { + res[1+i] = []string{strconv.FormatInt(int64(v), 10)} + } + return res } diff --git a/test/loadtime/report/report.go b/test/loadtime/report/report.go index 85844c518..59467f14d 100644 --- a/test/loadtime/report/report.go +++ b/test/loadtime/report/report.go @@ -1,13 +1,13 @@ package report import ( - "fmt" "math" "sync" "time" "github.com/tendermint/tendermint/test/loadtime/payload" "github.com/tendermint/tendermint/types" + "gonum.org/v1/gonum/stat" ) // BlockStore defines the set of methods needed by the report generator from @@ -102,23 +102,19 @@ func GenerateFromBlockStore(s BlockStore) (Report, error) { // We are therefore assuming that the data does not exceed these bounds. sum += int64(pd.l) } + if len(r.All) == 0 { + r.Min = 0 + return r, nil + } r.Avg = time.Duration(sum / int64(len(r.All))) - r.StdDev = time.Duration(stddev(r.All)) + r.StdDev = time.Duration(int64(stat.StdDev(toFloat(r.All), nil))) return r, nil } -func stddev(l []time.Duration) int64 { - var s1 int64 - for _, x := range l { - s1 += int64(x) +func toFloat(in []time.Duration) []float64 { + r := make([]float64, len(in)) + for i, v := range in { + r[i] = float64(int64(v)) } - u := s1 / int64(len(l)) - var s2 int64 - for _, x := range l { - s2 += (int64(x) - u) * (int64(x) - u) - } - r := s2 / (int64(len(l))) - sqr := math.Sqrt(float64(r)) - fmt.Println(sqr) - return int64(sqr) + return r }