mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-05-19 14:29:27 +00:00
collect block level times
This commit is contained in:
parent
c8592a42a8
commit
23bb0c49de
1 changed files with 48 additions and 46 deletions
|
|
@ -17,7 +17,6 @@
|
||||||
package live
|
package live
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/csv"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
@ -36,20 +35,21 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
type perfTracerConfig struct {
|
type perfTracerConfig struct {
|
||||||
CSVPath string `json:"csvPath"`
|
Path string `json:"path"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// perfTracer is a live tracer that measures and records transaction processing performance metrics.
|
// perfTracer is a live tracer that measures and records transaction processing performance metrics.
|
||||||
// It tracks total processing time, IO time (account and storage reads), and EVM execution time for
|
// It tracks total processing time, IO time (account and storage reads), and EVM execution time for
|
||||||
// each transaction. The metrics are written to a CSV file.
|
// each transaction. The metrics are written to a JSONL file.
|
||||||
type perfTracer struct {
|
type perfTracer struct {
|
||||||
csvPath string
|
path string
|
||||||
writer *csv.Writer
|
|
||||||
file *os.File
|
file *os.File
|
||||||
|
encoder *json.Encoder
|
||||||
|
|
||||||
// Block context
|
// Block context
|
||||||
currentBlock *types.Block
|
currentBlock *types.Block
|
||||||
currentBlockHash common.Hash
|
currentBlockHash common.Hash
|
||||||
|
blockStartTime time.Time
|
||||||
|
|
||||||
// Transaction tracking
|
// Transaction tracking
|
||||||
txStartTime time.Time
|
txStartTime time.Time
|
||||||
|
|
@ -59,6 +59,9 @@ type perfTracer struct {
|
||||||
prevAccountReads time.Duration
|
prevAccountReads time.Duration
|
||||||
prevStorageReads time.Duration
|
prevStorageReads time.Duration
|
||||||
|
|
||||||
|
// Transaction data collection
|
||||||
|
txData []map[string]interface{}
|
||||||
|
|
||||||
statedb tracing.StateDB
|
statedb tracing.StateDB
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -67,36 +70,20 @@ func newPerfTracer(cfg json.RawMessage) (*tracing.Hooks, error) {
|
||||||
if err := json.Unmarshal(cfg, &config); err != nil {
|
if err := json.Unmarshal(cfg, &config); err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse config: %v", err)
|
return nil, fmt.Errorf("failed to parse config: %v", err)
|
||||||
}
|
}
|
||||||
if config.CSVPath == "" {
|
if config.Path == "" {
|
||||||
return nil, errors.New("csv path is required")
|
return nil, errors.New("path is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open CSV file
|
// Open JSONL file
|
||||||
file, err := os.OpenFile(config.CSVPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
file, err := os.OpenFile(config.Path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to open CSV file: %v", err)
|
return nil, fmt.Errorf("failed to open JSONL file: %v", err)
|
||||||
}
|
|
||||||
writer := csv.NewWriter(file)
|
|
||||||
|
|
||||||
// Write header if file is empty
|
|
||||||
info, err := file.Stat()
|
|
||||||
if err != nil {
|
|
||||||
file.Close()
|
|
||||||
return nil, fmt.Errorf("failed to get file info: %v", err)
|
|
||||||
}
|
|
||||||
if info.Size() == 0 {
|
|
||||||
header := []string{"block_number", "block_hash", "tx_index", "tx_hash", "total_time_ns", "io_time_ns", "evm_time_ns", "gas_used"}
|
|
||||||
if err := writer.Write(header); err != nil {
|
|
||||||
file.Close()
|
|
||||||
return nil, fmt.Errorf("failed to write CSV header: %v", err)
|
|
||||||
}
|
|
||||||
writer.Flush()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
t := &perfTracer{
|
t := &perfTracer{
|
||||||
csvPath: config.CSVPath,
|
path: config.Path,
|
||||||
writer: writer,
|
|
||||||
file: file,
|
file: file,
|
||||||
|
encoder: json.NewEncoder(file),
|
||||||
}
|
}
|
||||||
|
|
||||||
return &tracing.Hooks{
|
return &tracing.Hooks{
|
||||||
|
|
@ -112,6 +99,8 @@ func (t *perfTracer) OnBlockStart(event tracing.BlockEvent) {
|
||||||
t.currentBlock = event.Block
|
t.currentBlock = event.Block
|
||||||
t.currentBlockHash = event.Block.Hash()
|
t.currentBlockHash = event.Block.Hash()
|
||||||
t.txIndex = 0
|
t.txIndex = 0
|
||||||
|
t.txData = make([]map[string]interface{}, 0)
|
||||||
|
t.blockStartTime = time.Now()
|
||||||
// Reset previous IO measurements for the new block
|
// Reset previous IO measurements for the new block
|
||||||
t.prevAccountReads = 0
|
t.prevAccountReads = 0
|
||||||
t.prevStorageReads = 0
|
t.prevStorageReads = 0
|
||||||
|
|
@ -143,36 +132,49 @@ func (t *perfTracer) OnTxEnd(receipt *types.Receipt, err error) {
|
||||||
evmTime = totalTime - ioTime
|
evmTime = totalTime - ioTime
|
||||||
}
|
}
|
||||||
|
|
||||||
row := []string{
|
txRecord := map[string]interface{}{
|
||||||
t.currentBlock.Number().String(),
|
"txIndex": fmt.Sprintf("0x%x", t.txIndex),
|
||||||
t.currentBlockHash.Hex(),
|
"txHash": receipt.TxHash.Hex(),
|
||||||
fmt.Sprintf("%d", t.txIndex),
|
"gasUsed": fmt.Sprintf("0x%x", receipt.GasUsed),
|
||||||
receipt.TxHash.Hex(),
|
"totalTime": fmt.Sprintf("0x%x", totalTime.Nanoseconds()),
|
||||||
fmt.Sprintf("%d", totalTime.Nanoseconds()),
|
"ioTime": fmt.Sprintf("0x%x", ioTime.Nanoseconds()),
|
||||||
fmt.Sprintf("%d", ioTime.Nanoseconds()),
|
"evmTime": fmt.Sprintf("0x%x", evmTime.Nanoseconds()),
|
||||||
fmt.Sprintf("%d", evmTime.Nanoseconds()),
|
|
||||||
fmt.Sprintf("%d", receipt.GasUsed),
|
|
||||||
}
|
|
||||||
if err := t.writer.Write(row); err != nil {
|
|
||||||
fmt.Printf("Failed to write CSV row: %v\n", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t.txData = append(t.txData, txRecord)
|
||||||
|
|
||||||
t.prevAccountReads = accumulatedIO.AccountReads
|
t.prevAccountReads = accumulatedIO.AccountReads
|
||||||
t.prevStorageReads = accumulatedIO.StorageReads
|
t.prevStorageReads = accumulatedIO.StorageReads
|
||||||
t.txIndex++
|
t.txIndex++
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnBlockEnd implements tracing.BlockEndHook
|
|
||||||
func (t *perfTracer) OnBlockEnd(err error) {
|
func (t *perfTracer) OnBlockEnd(err error) {
|
||||||
if t.writer != nil {
|
// Calculate block-level timings
|
||||||
t.writer.Flush()
|
totalTime := time.Since(t.blockStartTime)
|
||||||
|
blockEndIO := t.statedb.GetAccumulatedIOMeasurements()
|
||||||
|
ioTime := blockEndIO.AccountReads + blockEndIO.StorageReads
|
||||||
|
evmTime := totalTime - ioTime
|
||||||
|
|
||||||
|
blockRecord := map[string]interface{}{
|
||||||
|
"blockNumber": fmt.Sprintf("0x%x", t.currentBlock.Number()),
|
||||||
|
"blockHash": t.currentBlockHash.Hex(),
|
||||||
|
"gasUsed": fmt.Sprintf("0x%x", t.currentBlock.GasUsed()),
|
||||||
|
"totalTime": fmt.Sprintf("0x%x", totalTime.Nanoseconds()),
|
||||||
|
"ioTime": fmt.Sprintf("0x%x", ioTime.Nanoseconds()),
|
||||||
|
"evmTime": fmt.Sprintf("0x%x", evmTime.Nanoseconds()),
|
||||||
|
"transactions": t.txData,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := t.encoder.Encode(blockRecord); err != nil {
|
||||||
|
fmt.Printf("Failed to write block record: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.file != nil {
|
||||||
|
t.file.Sync()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *perfTracer) OnClose() {
|
func (t *perfTracer) OnClose() {
|
||||||
if t.writer != nil {
|
|
||||||
t.writer.Flush()
|
|
||||||
}
|
|
||||||
if t.file != nil {
|
if t.file != nil {
|
||||||
t.file.Close()
|
t.file.Close()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue