perf: all: remove unnecessary allocations from strings.Builder.WriteString(fmt.Sprintf(...)) (#13230)
This change removes a code pattern that I noticed while on a late night audit of cosmovisor in which strings.Builder.WriteString(fmt.Sprintf(...)) calls were being made, yet that's counterproductive to using fmt.Fprintf which will check whether the writer implements .WriteString and then avoids the need to firstly build a string using fmt.Sprintf. The performance wins from this change transcend all dimensions as exhibited below: ```shell $ benchstat before.txt after.txt name old time/op new time/op delta DetailString-8 5.48µs ±23% 4.40µs ±11% -19.79% (p=0.000 n=20+17) name old alloc/op new alloc/op delta DetailString-8 2.63kB ± 0% 2.11kB ± 0% -19.76% (p=0.000 n=20+20) name old allocs/op new allocs/op delta DetailString-8 63.0 ± 0% 50.0 ± 0% -20.63% (p=0.000 n=20+20) ``` Fixes #13229
This commit is contained in:
parent
717611e047
commit
abe3e0c6da
|
@ -84,19 +84,17 @@ type ResultValidatorsOutput struct {
|
|||
func (rvo ResultValidatorsOutput) String() string {
|
||||
var b strings.Builder
|
||||
|
||||
b.WriteString(fmt.Sprintf("block height: %d\n", rvo.BlockHeight))
|
||||
b.WriteString(fmt.Sprintf("total count: %d\n", rvo.Total))
|
||||
fmt.Fprintf(&b, "block height: %d\n", rvo.BlockHeight)
|
||||
fmt.Fprintf(&b, "total count: %d\n", rvo.Total)
|
||||
|
||||
for _, val := range rvo.Validators {
|
||||
b.WriteString(
|
||||
fmt.Sprintf(`
|
||||
fmt.Fprintf(&b, `
|
||||
Address: %s
|
||||
Pubkey: %s
|
||||
ProposerPriority: %d
|
||||
VotingPower: %d
|
||||
`,
|
||||
val.Address, val.PubKey, val.ProposerPriority, val.VotingPower,
|
||||
),
|
||||
val.Address, val.PubKey, val.ProposerPriority, val.VotingPower,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -379,7 +379,7 @@ func (cfg Config) DetailString() string {
|
|||
var sb strings.Builder
|
||||
sb.WriteString("Configurable Values:\n")
|
||||
for _, kv := range configEntries {
|
||||
sb.WriteString(fmt.Sprintf(" %s: %s\n", kv.name, kv.value))
|
||||
fmt.Fprintf(&sb, " %s: %s\n", kv.name, kv.value)
|
||||
}
|
||||
sb.WriteString("Derived Values:\n")
|
||||
dnl := 0
|
||||
|
@ -390,7 +390,7 @@ func (cfg Config) DetailString() string {
|
|||
}
|
||||
dFmt := fmt.Sprintf(" %%%ds: %%s\n", dnl)
|
||||
for _, kv := range derivedEntries {
|
||||
sb.WriteString(fmt.Sprintf(dFmt, kv.name, kv.value))
|
||||
fmt.Fprintf(&sb, dFmt, kv.name, kv.value)
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
|
|
@ -689,3 +689,29 @@ func (s *argsTestSuite) TestLogConfigOrError() {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
var sink interface{}
|
||||
|
||||
func BenchmarkDetailString(b *testing.B) {
|
||||
cfg := &Config{
|
||||
Home: "/foo", Name: "myd",
|
||||
AllowDownloadBinaries: true,
|
||||
UnsafeSkipBackup: true,
|
||||
PollInterval: 450 * time.Second,
|
||||
PreupgradeMaxRetries: 1e7,
|
||||
}
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
sink = cfg.DetailString()
|
||||
}
|
||||
|
||||
if sink == nil {
|
||||
b.Fatal("Benchmark did not run")
|
||||
}
|
||||
|
||||
// Otherwise reset the sink.
|
||||
sink = (interface{})(nil)
|
||||
}
|
||||
|
|
|
@ -55,12 +55,12 @@ func (e MultiError) Len() int {
|
|||
// Error implements the error interface for a MultiError.
|
||||
func (e *MultiError) Error() string {
|
||||
var sb strings.Builder
|
||||
sb.WriteString(fmt.Sprintf("%d errors: ", len(e.errs)))
|
||||
fmt.Fprintf(&sb, "%d errors: ", len(e.errs))
|
||||
for i, err := range e.errs {
|
||||
if i != 0 {
|
||||
sb.WriteString(", ")
|
||||
}
|
||||
sb.WriteString(fmt.Sprintf("%d: %v", i+1, err))
|
||||
fmt.Fprintf(&sb, "%d: %v", i+1, err)
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
|
|
@ -107,7 +107,7 @@ func TestInterceptConfigsPreRunHandlerReadsConfigToml(t *testing.T) {
|
|||
t.Fatalf("creating config.toml file failed: %v", err)
|
||||
}
|
||||
|
||||
_, err = writer.WriteString(fmt.Sprintf("db_backend = '%s'\n", testDbBackend))
|
||||
_, err = fmt.Fprintf(writer, "db_backend = '%s'\n", testDbBackend)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed writing string to config.toml: %v", err)
|
||||
}
|
||||
|
@ -148,7 +148,7 @@ func TestInterceptConfigsPreRunHandlerReadsAppToml(t *testing.T) {
|
|||
t.Fatalf("creating app.toml file failed: %v", err)
|
||||
}
|
||||
|
||||
_, err = writer.WriteString(fmt.Sprintf("halt-time = %d\n", testHaltTime))
|
||||
_, err = fmt.Fprintf(writer, "halt-time = %d\n", testHaltTime)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed writing string to app.toml: %v", err)
|
||||
}
|
||||
|
@ -312,7 +312,7 @@ func (v precedenceCommon) setAll(t *testing.T, setFlag *string, setEnvVar *strin
|
|||
t.Fatalf("creating config.toml file failed: %v", err)
|
||||
}
|
||||
|
||||
_, err = writer.WriteString(fmt.Sprintf("[rpc]\nladdr = \"%s\"\n", *setConfigFile))
|
||||
_, err = fmt.Fprintf(writer, "[rpc]\nladdr = \"%s\"\n", *setConfigFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed writing string to config.toml: %v", err)
|
||||
}
|
||||
|
|
|
@ -244,10 +244,10 @@ func (se StringEvents) String() string {
|
|||
var sb strings.Builder
|
||||
|
||||
for _, e := range se {
|
||||
sb.WriteString(fmt.Sprintf("\t\t- %s\n", e.Type))
|
||||
fmt.Fprintf(&sb, "\t\t- %s\n", e.Type)
|
||||
|
||||
for _, attr := range e.Attributes {
|
||||
sb.WriteString(fmt.Sprintf("\t\t\t- %s\n", attr.String()))
|
||||
fmt.Fprintf(&sb, "\t\t\t- %s\n", attr)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,11 +8,11 @@ import (
|
|||
// String implements the Stringer interface.
|
||||
func (csp CommunityPoolSpendProposal) String() string {
|
||||
var b strings.Builder
|
||||
b.WriteString(fmt.Sprintf(`Community Pool Spend Proposal:
|
||||
fmt.Fprintf(&b, `Community Pool Spend Proposal:
|
||||
Title: %s
|
||||
Description: %s
|
||||
Recipient: %s
|
||||
Amount: %s
|
||||
`, csp.Title, csp.Description, csp.Recipient, csp.Amount))
|
||||
`, csp.Title, csp.Description, csp.Recipient, csp.Amount)
|
||||
return b.String()
|
||||
}
|
||||
|
|
|
@ -51,18 +51,18 @@ func (pcp *ParameterChangeProposal) ValidateBasic() error {
|
|||
func (pcp ParameterChangeProposal) String() string {
|
||||
var b strings.Builder
|
||||
|
||||
b.WriteString(fmt.Sprintf(`Parameter Change Proposal:
|
||||
fmt.Fprintf(&b, `Parameter Change Proposal:
|
||||
Title: %s
|
||||
Description: %s
|
||||
Changes:
|
||||
`, pcp.Title, pcp.Description))
|
||||
`, pcp.Title, pcp.Description)
|
||||
|
||||
for _, pc := range pcp.Changes {
|
||||
b.WriteString(fmt.Sprintf(` Param Change:
|
||||
fmt.Fprintf(&b, ` Param Change:
|
||||
Subspace: %s
|
||||
Key: %s
|
||||
Value: %X
|
||||
`, pc.Subspace, pc.Key, pc.Value))
|
||||
`, pc.Subspace, pc.Key, pc.Value)
|
||||
}
|
||||
|
||||
return b.String()
|
||||
|
|
Loading…
Reference in New Issue