Skip to content

refactor: use strings.Builder to improve performance#458

Closed
zjumathcode wants to merge 1 commit intoinitia-labs:mainfrom
zjumathcode:main
Closed

refactor: use strings.Builder to improve performance#458
zjumathcode wants to merge 1 commit intoinitia-labs:mainfrom
zjumathcode:main

Conversation

@zjumathcode
Copy link
Copy Markdown

@zjumathcode zjumathcode commented Dec 1, 2025

Description

strings.Builder has fewer memory allocations and better performance.
More info: golang/go#75190


Author Checklist

All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.

I have...

  • included the correct type prefix in the PR title, you can find examples of the prefixes below:
  • confirmed ! in the type prefix if API or client breaking change
  • targeted the correct branch
  • provided a link to the relevant issue or specification
  • reviewed "Files changed" and left comments if necessary
  • included the necessary unit and integration tests
  • updated the relevant documentation or specification, including comments for documenting Go code
  • confirmed all CI checks have passed

Reviewers Checklist

All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.

I have...

  • confirmed the correct type prefix in the PR title
  • confirmed all author checklist items have been addressed
  • reviewed state machine logic, API design and naming, documentation is accurate, tests and test coverage

Signed-off-by: zjumathcode <pai314159@2980.com>
@zjumathcode zjumathcode requested a review from a team as a code owner December 1, 2025 11:16
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 1, 2025

📝 Walkthrough

Walkthrough

Refactored string assembly across five files from direct concatenation to strings.Builder, improving efficiency while preserving identical functionality and output behavior.

Changes

Cohort / File(s) Change Summary
String method refactoring
x/distribution/types/validator.go, x/gov/types/proposal.go, x/mstaking/types/delegation.go
Replaced string concatenation in String() implementations with strings.Builder using WriteString(), returning builder.String() instead of accumulated string variables.
Message/list accumulation
x/gov/keeper/proposal.go, x/mstaking/keeper/invariants.go
Migrated message and comma-separated list construction from string concatenation to strings.Builder. Updated corresponding usage sites to call .String() on builders instead of using plain string variables.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

  • Rationale: All changes follow the same homogeneous refactoring pattern (string concatenation → strings.Builder), with no functional logic changes or API modifications. Verification focuses on confirming output equivalence and completeness of concatenation replacements.
  • Areas for attention:
    • Verify trimming behavior (TrimSpace(), newline trims) is preserved in each String() method
    • Confirm all string concatenation operations within affected functions were converted to WriteString() calls

Poem

🐰 We hop through strings with newfound grace,
Builder blocks now take their place,
No concatenation, just smooth writes,
Efficiency bounces to new heights!
Thump thump 🐇

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: refactoring string construction to use strings.Builder for performance improvement across multiple files.
Description check ✅ Passed The description is related to the changeset, explaining the motivation (fewer memory allocations and better performance) and referencing relevant Go documentation.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@zjumathcode
Copy link
Copy Markdown
Author

I wrote a benchmark that shows the performance advantage of using strings.Builder, as follows:

package bench_test

import (
	"fmt"
	"strings"
	"testing"
)

type Route struct {
	Group          string
	MatcherSetsRaw string
	HandlersRaw    []string
	Terminal       bool
}

func (r Route) StringOld() string {
	handlersRaw := "["
	for _, hr := range r.HandlersRaw {
		handlersRaw += " " + string(hr)
	}
	handlersRaw += "]"

	return fmt.Sprintf(`{Group:"%s" MatcherSetsRaw:%s HandlersRaw:%s Terminal:%t}`,
		r.Group, r.MatcherSetsRaw, handlersRaw, r.Terminal)
}

func (r Route) StringNew() string {
	var handlersRaw strings.Builder
	handlersRaw.WriteString("[")
	for _, hr := range r.HandlersRaw {
		handlersRaw.WriteString(" " + string(hr))
	}
	handlersRaw.WriteString("]")

	return fmt.Sprintf(`{Group:"%s" MatcherSetsRaw:%s HandlersRaw:%s Terminal:%t}`,
		r.Group, r.MatcherSetsRaw, handlersRaw.String(), r.Terminal)
}

func BenchmarkRouteString_Old(b *testing.B) {
	r := Route{
		Group:          "api",
		MatcherSetsRaw: "[matcher]",
		HandlersRaw:    []string{"foo", "bar", "baz", "qux", "quux"},
		Terminal:       true,
	}
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		_ = r.StringOld()
	}
}

func BenchmarkRouteString_New(b *testing.B) {
	r := Route{
		Group:          "api",
		MatcherSetsRaw: "[matcher]",
		HandlersRaw:    []string{"foo", "bar", "baz", "qux", "quux"},
		Terminal:       true,
	}
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		_ = r.StringNew()
	}
}
➜  benchmark go test -bench=. -benchmem
goos: darwin
goarch: arm64
pkg: demo/stringsbuilder/benchmark
cpu: Apple M4
BenchmarkRouteString_Old-10      5239779               211.3 ns/op           248 B/op         10 allocs/op
BenchmarkRouteString_New-10      6258066               193.5 ns/op           200 B/op          7 allocs/op
PASS
ok      demo/stringsbuilder/benchmark 3.482s

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (5)
x/distribution/types/validator.go (1)

39-49: ValidatorSlashEvents.String builder usage looks good; optional micro‑optimizations possible

The refactor to strings.Builder preserves the existing output shape (header + per‑slash blocks, trailing whitespace trimmed) and avoids repeated concatenation. If you want to push this a bit further, you could skip the intermediate allocations from fmt.Sprintf by writing directly into the builder, e.g.:

fmt.Fprintf(&out, "  Slash %d:\n    Period:    %d\n    Fractions: %s\n", i, sl.ValidatorPeriod, sl.Fractions)

This is purely optional given how infrequently this Stringer is likely called.

x/gov/types/proposal.go (1)

86-95: Proposals.String refactor is fine; consider minor cleanups

The switch to strings.Builder here is straightforward and should be behavior‑preserving (same lines, trailing newline trimmed). Two optional tweaks if you touch this again:

  • Avoid the intermediate allocation with fmt.Sprintf by using fmt.Fprintf(&out, "%d - %s\n", prop.Id, prop.Status).
  • Either extend the per‑proposal line to include [Type] Title or adjust the header, since the current header columns and body don’t match.

Both are cosmetic; the refactor itself looks correct.

x/mstaking/keeper/invariants.go (2)

145-174: PositiveDelegationInvariant message building is correct; builder use is fine

Switching msg from string to strings.Builder and then using msg.String() in the final fmt.Sprintf keeps the invariant output the same while avoiding repeated string concatenation in the loop. Given how rarely invariants run, this is already more than sufficient; if you ever want to shave a bit more, you could do:

fmt.Fprintf(&msg, "\tdelegation with negative shares: %+v\n", delegation)

to avoid the intermediate fmt.Sprintf allocation. Optional only.


181-223: DelegatorSharesInvariant builder refactor looks good; optional direct writes

The strings.Builder use here is also semantically equivalent to the old msg += fmt.Sprintf(...) pattern, and the final FormatInvariant call just consumes msg.String() as expected.

As above, you could switch to:

fmt.Fprintf(&msg, "broken delegator shares invariance:\n\tvalidator.DelegatorShares: %v\n\tsum of Delegator.Shares: %v\n", expValTotalDelShares, calculatedValTotalDelShares)

to skip an extra allocation, but that’s purely a readability/perf nicety.

x/mstaking/types/delegation.go (1)

290-315: Redelegation.String builder usage and trimming look correct

The new strings.Builder implementation for Redelegation.String keeps the same structure as before:

  • Header with delegator/src/dst and an Entries: section plus blank line.
  • One multi-line block per entry, each ending in \n.
  • Final strings.TrimRight(out.String(), "\n") to drop the trailing newline while preserving other whitespace.

That’s a solid pattern. As with other sites, you can optionally switch to fmt.Fprintf(&out, ...) to avoid intermediate fmt.Sprintf strings, but it’s not necessary for clarity or correctness.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between a0fda2b and 1c8e923.

📒 Files selected for processing (5)
  • x/distribution/types/validator.go (1 hunks)
  • x/gov/keeper/proposal.go (3 hunks)
  • x/gov/types/proposal.go (1 hunks)
  • x/mstaking/keeper/invariants.go (5 hunks)
  • x/mstaking/types/delegation.go (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
x/mstaking/keeper/invariants.go (1)
x/mstaking/types/keys.go (1)
  • ModuleName (5-5)
🔇 Additional comments (2)
x/mstaking/types/delegation.go (1)

185-203: Review comment is based on incorrect code snapshot

The review references a strings.Builder implementation and provides a diff for it, but the actual code in x/mstaking/types/delegation.go (lines 186-202) uses simple string concatenation with +=, not strings.Builder.

While the underlying formatting concern is valid—UnbondingDelegation.String does lack newlines between entries and will produce output like "Entries: Unbonding Delegation 0:" on a single line—the code snippet and diff in the review do not match the current implementation and cannot be applied.

The comparison with Redelegation.String (lines 290-313) is accurate: Redelegation.String properly ends raw strings with newlines and uses strings.TrimRight(out, "\n") at the end, while UnbondingDelegation.String does not.

Clarify whether this review is for a pending PR or whether the strings.Builder refactor was already applied, as the current code differs from what the review describes.

Likely an incorrect or invalid review comment.

x/gov/keeper/proposal.go (1)

42-49: Code review is based on outdated code snapshot

The review comment references code using strings.Builder and msgsStr.WriteString(), but the current version of x/gov/keeper/proposal.go (lines 42–47) uses simple string concatenation with the += operator:

msgsStr := ""
// ...
msgsStr += fmt.Sprintf(",%s", sdk.MsgTypeURL(msg))

The strings.Builder refactoring mentioned in the review does not appear to have been applied. The underlying concern about the leading comma remains valid for the current code, but cannot be addressed using the exact diff provided since it targets non-existent code.

Likely an incorrect or invalid review comment.

@beer-1
Copy link
Copy Markdown
Member

beer-1 commented Feb 23, 2026

Thanks for PR, but we are not receiving this kind of maintain PRs.
sorry for closing the PR

@beer-1 beer-1 closed this Feb 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants