tendermint/tools/tm-bench/statistics.go
ValarDragon aff063b79b tools/tmbench: Move statistics to a seperate file
This PR moves statistics to its own file, seperates getBlockMetas
into its own function, and removes the timeEnd parameter from
calculate statistics. The ending time is now computed directly from
the start time and the duration, to enforce that we only collect
data for the provided duration.
2018-07-13 18:00:49 -07:00

151 lines
3.9 KiB
Go

package main
import (
"encoding/json"
"fmt"
"math"
"os"
"text/tabwriter"
"time"
metrics "github.com/rcrowley/go-metrics"
tmrpc "github.com/tendermint/tendermint/rpc/client"
"github.com/tendermint/tendermint/types"
)
type statistics struct {
TxsThroughput metrics.Histogram `json:"txs_per_sec"`
BlocksThroughput metrics.Histogram `json:"blocks_per_sec"`
}
// calculateStatistics calculates the tx / second, and blocks / second based
// off of the number the transactions and number of blocks that occurred from
// the start block, and the end time.
func calculateStatistics(
client tmrpc.Client,
minHeight int64,
timeStart time.Time,
duration int,
) (*statistics, error) {
timeEnd := timeStart.Add(time.Duration(duration) * time.Second)
stats := &statistics{
BlocksThroughput: metrics.NewHistogram(metrics.NewUniformSample(1000)),
TxsThroughput: metrics.NewHistogram(metrics.NewUniformSample(1000)),
}
var (
numBlocksPerSec = make(map[int64]int64)
numTxsPerSec = make(map[int64]int64)
)
// because during some seconds blocks won't be created...
for i := int64(0); i < int64(duration); i++ {
numBlocksPerSec[i] = 0
numTxsPerSec[i] = 0
}
blockMetas, err := getBlockMetas(client, minHeight, timeStart, timeEnd)
if err != nil {
return nil, err
}
// iterates from max height to min height
for _, blockMeta := range blockMetas {
// check if block was created after timeStart
if blockMeta.Header.Time.Before(timeStart) {
break
}
// check if block was created before timeEnd
if blockMeta.Header.Time.After(timeEnd) {
continue
}
sec := secondsSinceTimeStart(timeStart, blockMeta.Header.Time)
// increase number of blocks for that second
numBlocksPerSec[sec]++
// increase number of txs for that second
numTxsPerSec[sec] += blockMeta.Header.NumTxs
logger.Debug(fmt.Sprintf("%d txs at block height %d", blockMeta.Header.NumTxs, blockMeta.Header.Height))
}
for i := int64(0); i < int64(duration); i++ {
stats.BlocksThroughput.Update(numBlocksPerSec[i])
stats.TxsThroughput.Update(numTxsPerSec[i])
}
return stats, nil
}
func getBlockMetas(client tmrpc.Client, minHeight int64, timeStart, timeEnd time.Time) ([]*types.BlockMeta, error) {
// get blocks between minHeight and last height
// This returns max(minHeight,(last_height - 20)) to last_height
info, err := client.BlockchainInfo(minHeight, 0)
if err != nil {
return nil, err
}
var (
blockMetas = info.BlockMetas
lastHeight = info.LastHeight
diff = lastHeight - minHeight
offset = len(blockMetas)
)
for offset < int(diff) {
// get blocks between minHeight and last height
info, err := client.BlockchainInfo(minHeight, lastHeight-int64(offset))
if err != nil {
return nil, err
}
blockMetas = append(blockMetas, info.BlockMetas...)
offset = len(blockMetas)
}
return blockMetas, nil
}
func secondsSinceTimeStart(timeStart, timePassed time.Time) int64 {
return int64(math.Round(timePassed.Sub(timeStart).Seconds()))
}
func printStatistics(stats *statistics, outputFormat string) {
if outputFormat == "json" {
result, err := json.Marshal(struct {
TxsThroughput float64 `json:"txs_per_sec_avg"`
BlocksThroughput float64 `json:"blocks_per_sec_avg"`
}{stats.TxsThroughput.Mean(), stats.BlocksThroughput.Mean()})
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fmt.Println(string(result))
} else {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 5, ' ', 0)
fmt.Fprintln(w, "Stats\tAvg\tStdDev\tMax\tTotal\t")
fmt.Fprintln(
w,
fmt.Sprintf(
"Txs/sec\t%.0f\t%.0f\t%d\t%d\t",
stats.TxsThroughput.Mean(),
stats.TxsThroughput.StdDev(),
stats.TxsThroughput.Max(),
stats.TxsThroughput.Sum(),
),
)
fmt.Fprintln(
w,
fmt.Sprintf("Blocks/sec\t%.3f\t%.3f\t%d\t%d\t",
stats.BlocksThroughput.Mean(),
stats.BlocksThroughput.StdDev(),
stats.BlocksThroughput.Max(),
stats.BlocksThroughput.Sum(),
),
)
w.Flush()
}
}