|
|
@ -1,16 +1,18 @@ |
|
|
|
package main |
|
|
|
|
|
|
|
import ( |
|
|
|
"encoding/json" |
|
|
|
"flag" |
|
|
|
"fmt" |
|
|
|
"os" |
|
|
|
"strings" |
|
|
|
"text/tabwriter" |
|
|
|
"time" |
|
|
|
|
|
|
|
"github.com/go-kit/kit/log/term" |
|
|
|
metrics "github.com/rcrowley/go-metrics" |
|
|
|
|
|
|
|
"text/tabwriter" |
|
|
|
|
|
|
|
tmtypes "github.com/tendermint/tendermint/types" |
|
|
|
"github.com/tendermint/tmlibs/log" |
|
|
|
"github.com/tendermint/tools/tm-monitor/monitor" |
|
|
@ -29,17 +31,19 @@ type statistics struct { |
|
|
|
func main() { |
|
|
|
var duration, txsRate, connections int |
|
|
|
var verbose bool |
|
|
|
var outputFormat string |
|
|
|
|
|
|
|
flag.IntVar(&connections, "c", 1, "Connections to keep open per endpoint") |
|
|
|
flag.IntVar(&duration, "T", 10, "Exit after the specified amount of time in seconds") |
|
|
|
flag.IntVar(&txsRate, "r", 1000, "Txs per second to send in a connection") |
|
|
|
flag.StringVar(&outputFormat, "output-format", "plain", "Output format: plain or json") |
|
|
|
flag.BoolVar(&verbose, "v", false, "Verbose output") |
|
|
|
|
|
|
|
flag.Usage = func() { |
|
|
|
fmt.Println(`Tendermint blockchain benchmarking tool. |
|
|
|
|
|
|
|
Usage: |
|
|
|
tm-bench [-c 1] [-T 10] [-r 1000] [endpoints] |
|
|
|
tm-bench [-c 1] [-T 10] [-r 1000] [endpoints] [-output-format <plain|json>] |
|
|
|
|
|
|
|
Examples: |
|
|
|
tm-bench localhost:46657`) |
|
|
@ -55,6 +59,10 @@ Examples: |
|
|
|
} |
|
|
|
|
|
|
|
if verbose { |
|
|
|
if outputFormat == "json" { |
|
|
|
fmt.Println("Verbose mode not supported with json output.") |
|
|
|
os.Exit(1) |
|
|
|
} |
|
|
|
// Color errors red
|
|
|
|
colorFn := func(keyvals ...interface{}) term.FgBgColor { |
|
|
|
for i := 1; i < len(keyvals); i += 2 { |
|
|
@ -65,9 +73,9 @@ Examples: |
|
|
|
return term.FgBgColor{} |
|
|
|
} |
|
|
|
logger = log.NewTMLoggerWithColorFn(log.NewSyncWriter(os.Stdout), colorFn) |
|
|
|
} |
|
|
|
|
|
|
|
fmt.Printf("Running %ds test @ %s\n", duration, flag.Arg(0)) |
|
|
|
fmt.Printf("Running %ds test @ %s\n", duration, flag.Arg(0)) |
|
|
|
} |
|
|
|
|
|
|
|
endpoints := strings.Split(flag.Arg(0), ",") |
|
|
|
|
|
|
@ -110,7 +118,7 @@ Examples: |
|
|
|
t.Stop() |
|
|
|
} |
|
|
|
|
|
|
|
printStatistics(stats) |
|
|
|
printStatistics(stats, outputFormat) |
|
|
|
|
|
|
|
for _, n := range nodes { |
|
|
|
n.Stop() |
|
|
@ -154,20 +162,47 @@ func startTransacters(endpoints []string, connections int, txsRate int) []*trans |
|
|
|
return transacters |
|
|
|
} |
|
|
|
|
|
|
|
func printStatistics(stats *statistics) { |
|
|
|
w := tabwriter.NewWriter(os.Stdout, 0, 0, 5, ' ', 0) |
|
|
|
fmt.Fprintln(w, "Stats\tAvg\tStdev\tMax\t") |
|
|
|
fmt.Fprintln(w, fmt.Sprintf("Block latency\t%.2fms\t%.2fms\t%dms\t", |
|
|
|
stats.BlockLatency.Mean()/1000000.0, |
|
|
|
stats.BlockLatency.StdDev()/1000000.0, |
|
|
|
stats.BlockLatency.Max()/1000000)) |
|
|
|
fmt.Fprintln(w, fmt.Sprintf("Blocks/sec\t%.3f\t%.3f\t%d\t", |
|
|
|
stats.BlockTimeSample.Mean(), |
|
|
|
stats.BlockTimeSample.StdDev(), |
|
|
|
stats.BlockTimeSample.Max())) |
|
|
|
fmt.Fprintln(w, fmt.Sprintf("Txs/sec\t%.0f\t%.0f\t%d\t", |
|
|
|
stats.TxThroughputSample.Mean(), |
|
|
|
stats.TxThroughputSample.StdDev(), |
|
|
|
stats.TxThroughputSample.Max())) |
|
|
|
w.Flush() |
|
|
|
type Results struct { |
|
|
|
BlockLatencyMean float64 |
|
|
|
BlockLatencyMax int64 |
|
|
|
BlockLatencyStdDev float64 |
|
|
|
BlockTimeMean float64 |
|
|
|
BlockTimeMax int64 |
|
|
|
BlockTimeStdDev float64 |
|
|
|
TxThroughputMean float64 |
|
|
|
TxThroughputMax int64 |
|
|
|
TxThroughputStdDev float64 |
|
|
|
} |
|
|
|
|
|
|
|
func printStatistics(stats *statistics, outputFormat string) { |
|
|
|
if outputFormat == "json" { |
|
|
|
result, _ := json.Marshal(Results{ |
|
|
|
stats.BlockLatency.Mean() / 1000000.0, |
|
|
|
stats.BlockLatency.Max() / 1000000.0, |
|
|
|
stats.BlockLatency.StdDev() / 1000000.0, |
|
|
|
stats.BlockTimeSample.Mean(), |
|
|
|
stats.BlockTimeSample.Max(), |
|
|
|
stats.BlockTimeSample.StdDev(), |
|
|
|
stats.TxThroughputSample.Mean(), |
|
|
|
stats.TxThroughputSample.Max(), |
|
|
|
stats.TxThroughputSample.StdDev(), |
|
|
|
}) |
|
|
|
fmt.Println(string(result)) |
|
|
|
} else { |
|
|
|
w := tabwriter.NewWriter(os.Stdout, 0, 0, 5, ' ', 0) |
|
|
|
fmt.Fprintln(w, "Stats\tAvg\tStdev\tMax\t") |
|
|
|
fmt.Fprintln(w, fmt.Sprintf("Block latency\t%.2fms\t%.2fms\t%dms\t", |
|
|
|
stats.BlockLatency.Mean()/1000000.0, |
|
|
|
stats.BlockLatency.StdDev()/1000000.0, |
|
|
|
stats.BlockLatency.Max()/1000000)) |
|
|
|
fmt.Fprintln(w, fmt.Sprintf("Blocks/sec\t%.3f\t%.3f\t%d\t", |
|
|
|
stats.BlockTimeSample.Mean(), |
|
|
|
stats.BlockTimeSample.StdDev(), |
|
|
|
stats.BlockTimeSample.Max())) |
|
|
|
fmt.Fprintln(w, fmt.Sprintf("Txs/sec\t%.0f\t%.0f\t%d\t", |
|
|
|
stats.TxThroughputSample.Mean(), |
|
|
|
stats.TxThroughputSample.StdDev(), |
|
|
|
stats.TxThroughputSample.Max())) |
|
|
|
w.Flush() |
|
|
|
} |
|
|
|
} |