From 3b3b7783163ca98bb356f6afe8471c9e07c73b3e Mon Sep 17 00:00:00 2001 From: huangyi Date: Wed, 18 Sep 2024 10:05:42 +0800 Subject: [PATCH] Problem: testground block stats don't calculate tps directly Solution: - add tps calculation, extract from #1575 --- testground/benchmark/benchmark/stateless.py | 29 +------------ testground/benchmark/benchmark/stats.py | 48 +++++++++++++++++++++ testground/benchmark/benchmark/utils.py | 15 +++++++ 3 files changed, 65 insertions(+), 27 deletions(-) create mode 100644 testground/benchmark/benchmark/stats.py diff --git a/testground/benchmark/benchmark/stateless.py b/testground/benchmark/benchmark/stateless.py index 45df0b2e3a..f8c32b817f 100644 --- a/testground/benchmark/benchmark/stateless.py +++ b/testground/benchmark/benchmark/stateless.py @@ -10,7 +10,6 @@ from typing import List import click -import requests import tomlkit from .cli import ChainCommand @@ -24,16 +23,16 @@ patch_configs, ) from .sendtx import generate_load +from .stats import dump_block_stats from .topology import connect_all from .types import PeerPacket -from .utils import wait_for_block, wait_for_port, wait_for_w3 +from .utils import block_height, block_txs, wait_for_block, wait_for_port, wait_for_w3 # use cronosd on host machine LOCAL_CRONOSD_PATH = "cronosd" DEFAULT_CHAIN_ID = "cronos_777-1" # the container must be deployed with the prefixed name HOSTNAME_TEMPLATE = "testplan-{index}" -LOCAL_RPC = "http://localhost:26657" ECHO_SERVER_PORT = 26659 @@ -309,19 +308,6 @@ def detect_idle_halted(idle_blocks: int, interval: int, chain_halt_interval=120) return -def block_height(): - rsp = requests.get(f"{LOCAL_RPC}/status").json() - return int(rsp["result"]["sync_info"]["latest_block_height"]) - - -def block(height): - return requests.get(f"{LOCAL_RPC}/block?height={height}").json() - - -def block_txs(height): - return block(height)["result"]["block"]["data"]["txs"] - - def init_node_local( cli: ChainCommand, outdir: Path, @@ -380,16 +366,5 @@ def wait_for_peers(home: Path): wait_for_port(ECHO_SERVER_PORT, host=host, timeout=2400) -def dump_block_stats(fp): - """ - dump simple statistics for blocks for analysis - """ - for i in range(1, block_height() + 1): - blk = block(i) - timestamp = blk["result"]["block"]["header"]["time"] - txs = len(blk["result"]["block"]["data"]["txs"]) - print("block", i, txs, timestamp, file=fp) - - if __name__ == "__main__": cli() diff --git a/testground/benchmark/benchmark/stats.py b/testground/benchmark/benchmark/stats.py new file mode 100644 index 0000000000..68d06abd28 --- /dev/null +++ b/testground/benchmark/benchmark/stats.py @@ -0,0 +1,48 @@ +from datetime import datetime + +from .utils import block, block_height + +# the tps calculation use the average of the last 10 blocks +TPS_WINDOW = 10 + + +def truncate_fractional_seconds(timestamp): + ( + date_time_part, + _, + _, + ) = timestamp.partition("Z") + if "." in date_time_part: + date_time_part, fractional_part = date_time_part.split(".") + fractional_part = fractional_part[:6] + return f"{date_time_part}.{fractional_part}Z" + return timestamp + + +def calculate_tps(blocks): + if len(blocks) < 2: + return 0 + + txs = sum(n for n, _ in blocks) + _, t1 = blocks[0] + _, t2 = blocks[-1] + return txs / (t2 - t1).total_seconds() + + +def dump_block_stats(fp): + """ + dump simple statistics for blocks for analysis + """ + tps_list = [] + current = block_height() + blocks = [] + for i in range(1, current + 1): + blk = block(i) + timestamp = datetime.fromisoformat(blk["result"]["block"]["header"]["time"]) + txs = len(blk["result"]["block"]["data"]["txs"]) + blocks.append((txs, timestamp)) + tps = calculate_tps(blocks[-TPS_WINDOW:]) + tps_list.append(tps) + print("block", i, txs, timestamp, tps, file=fp) + tps_list.sort(reverse=True) + print("top_tps", tps_list[:5], file=fp) diff --git a/testground/benchmark/benchmark/utils.py b/testground/benchmark/benchmark/utils.py index 0d890c4999..172a956887 100644 --- a/testground/benchmark/benchmark/utils.py +++ b/testground/benchmark/benchmark/utils.py @@ -4,6 +4,7 @@ from pathlib import Path import bech32 +import requests import tomlkit import web3 from eth_account import Account @@ -11,6 +12,7 @@ from web3._utils.transactions import fill_nonce, fill_transaction_defaults CRONOS_ADDRESS_PREFIX = "crc" +LOCAL_RPC = "http://localhost:26657" def patch_dict(doc, kwargs): @@ -139,3 +141,16 @@ def gen_account(global_seq: int, index: int) -> Account: index 0 is reserved for validator account. """ return Account.from_key(((global_seq + 1) << 32 | index).to_bytes(32)) + + +def block_height(): + rsp = requests.get(f"{LOCAL_RPC}/status").json() + return int(rsp["result"]["sync_info"]["latest_block_height"]) + + +def block(height): + return requests.get(f"{LOCAL_RPC}/block?height={height}").json() + + +def block_txs(height): + return block(height)["result"]["block"]["data"]["txs"]