diff --git a/testground/benchmark/benchmark/stateless.py b/testground/benchmark/benchmark/stateless.py index e903508f1f..4469bab88c 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,9 +23,10 @@ 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" @@ -34,7 +34,6 @@ DEFAULT_DENOM = "basecro" # the container must be deployed with the prefixed name HOSTNAME_TEMPLATE = "testplan-{index}" -LOCAL_RPC = "http://localhost:26657" ECHO_SERVER_PORT = 26659 @@ -291,19 +290,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, group: str, group_seq: int, ip: str ) -> PeerPacket: @@ -354,16 +340,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 0b0580e20b..51c9af6fa3 100644 --- a/testground/benchmark/benchmark/utils.py +++ b/testground/benchmark/benchmark/utils.py @@ -4,12 +4,15 @@ from pathlib import Path import bech32 +import requests import tomlkit import web3 from eth_account import Account from hexbytes import HexBytes from web3._utils.transactions import fill_nonce, fill_transaction_defaults +LOCAL_RPC = "http://localhost:26657" + def patch_dict(doc, kwargs): for k, v in kwargs.items(): @@ -124,3 +127,16 @@ def send_transactions(w3, txs, acct, wait=True): def export_eth_account(cli, name: str, **kwargs) -> Account: kwargs.setdefault("keyring_backend", "test") return Account.from_key(cli("keys", "unsafe-export-eth-key", name, **kwargs)) + + +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"]