From 1625a322f49f37ae0a3dbdca76d10b2b42832e90 Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Thu, 6 Nov 2025 13:49:47 +0100 Subject: [PATCH] perf: use strings.Builder instead of concatenating strings in a loop Avoids quadratic complexity --- accounts/abi/selector_parser.go | 11 ++++++++--- accounts/abi/type.go | 6 ++++-- accounts/hd.go | 4 +++- accounts/keystore/account_cache.go | 6 ++++-- cmd/devp2p/enrcmd.go | 8 +++++--- cmd/geth/dbcmd.go | 4 +++- core/blockchain.go | 8 +++++--- ethdb/leveldb/leveldb.go | 7 +++++-- log/handler_glog.go | 4 +++- params/config.go | 2 +- rlp/rlpgen/gen.go | 7 +++++-- trie/node.go | 6 ++++-- triedb/pathdb/database.go | 4 ++-- 13 files changed, 52 insertions(+), 25 deletions(-) diff --git a/accounts/abi/selector_parser.go b/accounts/abi/selector_parser.go index b8ddd7d656..bab0770a96 100644 --- a/accounts/abi/selector_parser.go +++ b/accounts/abi/selector_parser.go @@ -19,6 +19,7 @@ package abi import ( "errors" "fmt" + "strings" ) type SelectorMarshaling struct { @@ -68,19 +69,23 @@ func parseElementaryType(unescapedSelector string) (string, string, error) { return "", "", fmt.Errorf("failed to parse elementary type: %v", err) } // handle arrays + var parsedTypeSb71 strings.Builder for len(rest) > 0 && rest[0] == '[' { - parsedType = parsedType + string(rest[0]) + parsedTypeSb71.WriteString(string(rest[0])) rest = rest[1:] + var parsedTypeSb74 strings.Builder for len(rest) > 0 && isDigit(rest[0]) { - parsedType = parsedType + string(rest[0]) + parsedTypeSb74.WriteString(string(rest[0])) rest = rest[1:] } + parsedType += parsedTypeSb74.String() if len(rest) == 0 || rest[0] != ']' { return "", "", fmt.Errorf("failed to parse array: expected ']', got %c", unescapedSelector[0]) } - parsedType = parsedType + string(rest[0]) + parsedTypeSb71.WriteString(string(rest[0])) rest = rest[1:] } + parsedType += parsedTypeSb71.String() return parsedType, rest, nil } diff --git a/accounts/abi/type.go b/accounts/abi/type.go index 2fd11ac123..f962cd9a14 100644 --- a/accounts/abi/type.go +++ b/accounts/abi/type.go @@ -171,6 +171,7 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty used = make(map[string]bool) ) expression += "(" + var expressionSb174 strings.Builder for idx, c := range components { cType, err := NewType(c.Type, c.InternalType, c.Components) if err != nil { @@ -192,11 +193,12 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty }) elems = append(elems, &cType) names = append(names, c.Name) - expression += cType.stringKind + expressionSb174.WriteString(cType.stringKind) if idx != len(components)-1 { - expression += "," + expressionSb174.WriteString(",") } } + expression += expressionSb174.String() expression += ")" typ.TupleType = reflect.StructOf(fields) diff --git a/accounts/hd.go b/accounts/hd.go index daca75ebbc..ffc44d2ef6 100644 --- a/accounts/hd.go +++ b/accounts/hd.go @@ -121,6 +121,7 @@ func ParseDerivationPath(path string) (DerivationPath, error) { // to its canonical representation. func (path DerivationPath) String() string { result := "m" + var resultSb124 strings.Builder for _, component := range path { var hardened bool if component >= 0x80000000 { @@ -129,9 +130,10 @@ func (path DerivationPath) String() string { } result = fmt.Sprintf("%s/%d", result, component) if hardened { - result += "'" + resultSb124.WriteString("'") } } + result += resultSb124.String() return result } diff --git a/accounts/keystore/account_cache.go b/accounts/keystore/account_cache.go index d3a98850c7..a5169c71af 100644 --- a/accounts/keystore/account_cache.go +++ b/accounts/keystore/account_cache.go @@ -52,12 +52,14 @@ type AmbiguousAddrError struct { func (err *AmbiguousAddrError) Error() string { files := "" + var filesSb55 strings.Builder for i, a := range err.Matches { - files += a.URL.Path + filesSb55.WriteString(a.URL.Path) if i < len(err.Matches)-1 { - files += ", " + filesSb55.WriteString(", ") } } + files += filesSb55.String() return fmt.Sprintf("multiple keys match address (%s)", files) } diff --git a/cmd/devp2p/enrcmd.go b/cmd/devp2p/enrcmd.go index c9b692612f..07d87e5a61 100644 --- a/cmd/devp2p/enrcmd.go +++ b/cmd/devp2p/enrcmd.go @@ -109,22 +109,24 @@ func dumpRecordKV(kv []interface{}, indent int) string { } } // Print the keys, invoking formatters for known keys. + var outSb112 strings.Builder for i := 0; i < len(kv); i += 2 { key := kv[i].(string) val := kv[i+1].(rlp.RawValue) pad := longestKey - len(key) - out += strings.Repeat(" ", indent) + strconv.Quote(key) + strings.Repeat(" ", pad+1) + outSb112.WriteString(strings.Repeat(" ", indent) + strconv.Quote(key) + strings.Repeat(" ", pad+1)) formatter := attrFormatters[key] if formatter == nil { formatter = formatAttrRaw } fmtval, ok := formatter(val) if ok { - out += fmtval + "\n" + outSb112.WriteString(fmtval + "\n") } else { - out += hex.EncodeToString(val) + " (!)\n" + outSb112.WriteString(hex.EncodeToString(val) + " (!)\n") } } + out += outSb112.String() return out } diff --git a/cmd/geth/dbcmd.go b/cmd/geth/dbcmd.go index c57add0656..144723404c 100644 --- a/cmd/geth/dbcmd.go +++ b/cmd/geth/dbcmd.go @@ -265,9 +265,11 @@ func confirmAndRemoveDB(paths []string, kind string, ctx *cli.Context, removeFla err error ) msg := fmt.Sprintf("Location(s) of '%s': \n", kind) + var msgSb268 strings.Builder for _, path := range paths { - msg += fmt.Sprintf("\t- %s\n", path) + msgSb268.WriteString(fmt.Sprintf("\t- %s\n", path)) } + msg += msgSb268.String() fmt.Println(msg) if ctx.IsSet(removeFlagName) { confirm = ctx.Bool(removeFlagName) diff --git a/core/blockchain.go b/core/blockchain.go index b7acd12aca..06272a36da 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -711,7 +711,7 @@ func (bc *BlockChain) initializeHistoryPruning(latest uint64) error { // here, but it'd be a bit dangerous since they may not have intended this // action to happen. So just tell them how to do it. log.Error(fmt.Sprintf("Chain history mode is configured as %q, but database is not pruned.", bc.cfg.ChainHistoryMode.String())) - log.Error(fmt.Sprintf("Run 'geth prune-history' to prune pre-merge history.")) + log.Error("Run 'geth prune-history' to prune pre-merge history.") return errors.New("history pruning requested via configuration") } predefinedPoint := history.PrunePoints[bc.genesisBlock.Hash()] @@ -2704,11 +2704,13 @@ func (bc *BlockChain) logForkReadiness(block *types.Block) { // relevant information. func summarizeBadBlock(block *types.Block, receipts []*types.Receipt, config *params.ChainConfig, err error) string { var receiptString string + var receiptStringSb2707 strings.Builder for i, receipt := range receipts { - receiptString += fmt.Sprintf("\n %d: cumulative: %v gas: %v contract: %v status: %v tx: %v logs: %v bloom: %x state: %x", + receiptStringSb2707.WriteString(fmt.Sprintf("\n %d: cumulative: %v gas: %v contract: %v status: %v tx: %v logs: %v bloom: %x state: %x", i, receipt.CumulativeGasUsed, receipt.GasUsed, receipt.ContractAddress.Hex(), - receipt.Status, receipt.TxHash.Hex(), receipt.Logs, receipt.Bloom, receipt.PostState) + receipt.Status, receipt.TxHash.Hex(), receipt.Logs, receipt.Bloom, receipt.PostState)) } + receiptString += receiptStringSb2707.String() version, vcs := version.Info() platform := fmt.Sprintf("%s %s %s %s", version, runtime.Version(), runtime.GOARCH, runtime.GOOS) if vcs != "" { diff --git a/ethdb/leveldb/leveldb.go b/ethdb/leveldb/leveldb.go index b6c93907b1..33cdc4b3fe 100644 --- a/ethdb/leveldb/leveldb.go +++ b/ethdb/leveldb/leveldb.go @@ -24,6 +24,7 @@ import ( "bytes" "errors" "fmt" + "strings" "sync" "time" @@ -280,6 +281,7 @@ func (db *Database) Stat() (string, error) { if len(stats.LevelSizes) > 0 { message += " Level | Tables | Size(MB) | Time(sec) | Read(MB) | Write(MB)\n" + "-------+------------+---------------+---------------+---------------+---------------\n" + var messageSb283 strings.Builder for level, size := range stats.LevelSizes { read := stats.LevelRead[level] write := stats.LevelWrite[level] @@ -294,10 +296,11 @@ func (db *Database) Stat() (string, error) { totalRead += read totalWrite += write totalDuration += duration - message += fmt.Sprintf(" %3d | %10d | %13.5f | %13.5f | %13.5f | %13.5f\n", + messageSb283.WriteString(fmt.Sprintf(" %3d | %10d | %13.5f | %13.5f | %13.5f | %13.5f\n", level, tables, float64(size)/1048576.0, duration.Seconds(), - float64(read)/1048576.0, float64(write)/1048576.0) + float64(read)/1048576.0, float64(write)/1048576.0)) } + message += messageSb283.String() message += "-------+------------+---------------+---------------+---------------+---------------\n" message += fmt.Sprintf(" Total | %10d | %13.5f | %13.5f | %13.5f | %13.5f\n", totalTables, float64(totalSize)/1048576.0, totalDuration.Seconds(), diff --git a/log/handler_glog.go b/log/handler_glog.go index 739f8c5b42..8e17fdb300 100644 --- a/log/handler_glog.go +++ b/log/handler_glog.go @@ -113,13 +113,15 @@ func (h *GlogHandler) Vmodule(ruleset string) error { } // Compile the rule pattern into a regular expression matcher := ".*" + var matcherSb116 strings.Builder for _, comp := range strings.Split(parts[0], "/") { if comp == "*" { - matcher += "(/.*)?" + matcherSb116.WriteString("(/.*)?") } else if comp != "" { matcher += "/" + regexp.QuoteMeta(comp) } } + matcher += matcherSb116.String() if !strings.HasSuffix(parts[0], ".go") { matcher += "/[^/]+\\.go" } diff --git a/params/config.go b/params/config.go index c5dfad1cd3..8f36e2b4fd 100644 --- a/params/config.go +++ b/params/config.go @@ -693,7 +693,7 @@ func (c *ChainConfig) Description() string { if c.VerkleTime != nil { banner += fmt.Sprintf(" - Verkle: @%-10v blob: (%s)\n", *c.VerkleTime, c.BlobScheduleConfig.Verkle) } - banner += fmt.Sprintf("\nAll fork specifications can be found at https://ethereum.github.io/execution-specs/src/ethereum/forks/\n") + banner += "\nAll fork specifications can be found at https://ethereum.github.io/execution-specs/src/ethereum/forks/\n" return banner } diff --git a/rlp/rlpgen/gen.go b/rlp/rlpgen/gen.go index 64841b38a0..4698c5ac4f 100644 --- a/rlp/rlpgen/gen.go +++ b/rlp/rlpgen/gen.go @@ -22,6 +22,7 @@ import ( "go/format" "go/types" "sort" + "strings" "github.com/ethereum/go-ethereum/rlp/internal/rlpstruct" "golang.org/x/tools/go/packages" @@ -624,12 +625,14 @@ func (op structOp) writeOptionalFields(b *bytes.Buffer, ctx *genContext, v strin for i, field := range op.optionalFields { selector := v + "." + field.name cond := "" + var condSb627 strings.Builder for j := i; j < len(op.optionalFields); j++ { if j > i { - cond += " || " + condSb627.WriteString(" || ") } - cond += zeroV[j] + condSb627.WriteString(zeroV[j]) } + cond += condSb627.String() fmt.Fprintf(b, "if %s {\n", cond) fmt.Fprint(b, field.elem.genWrite(ctx, selector)) fmt.Fprintf(b, "}\n") diff --git a/trie/node.go b/trie/node.go index 74fac4fd4e..c384fa900b 100644 --- a/trie/node.go +++ b/trie/node.go @@ -101,13 +101,15 @@ func (n valueNode) String() string { return n.fstring("") } func (n *fullNode) fstring(ind string) string { resp := fmt.Sprintf("[\n%s ", ind) + var respSb104 strings.Builder for i, node := range &n.Children { if node == nil { - resp += fmt.Sprintf("%s: ", indices[i]) + respSb104.WriteString(fmt.Sprintf("%s: ", indices[i])) } else { - resp += fmt.Sprintf("%s: %v", indices[i], node.fstring(ind+" ")) + respSb104.WriteString(fmt.Sprintf("%s: %v", indices[i], node.fstring(ind+" "))) } } + resp += respSb104.String() return resp + fmt.Sprintf("\n%s] ", ind) } diff --git a/triedb/pathdb/database.go b/triedb/pathdb/database.go index 9fc65de277..14aa8f75bb 100644 --- a/triedb/pathdb/database.go +++ b/triedb/pathdb/database.go @@ -606,9 +606,9 @@ func (db *Database) journalPath() string { } var fname string if db.isVerkle { - fname = fmt.Sprintf("verkle.journal") + fname = "verkle.journal" } else { - fname = fmt.Sprintf("merkle.journal") + fname = "merkle.journal" } return filepath.Join(db.config.JournalDirectory, fname) }