Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use strings.Builder to save some memory #649

Open
apocelipes opened this issue Mar 13, 2024 · 0 comments · May be fixed by #650
Open

Use strings.Builder to save some memory #649

apocelipes opened this issue Mar 13, 2024 · 0 comments · May be fixed by #650
Labels
proposal Proposal to add a new feature to pterm

Comments

@apocelipes
Copy link

apocelipes commented Mar 13, 2024

Currently, the Srender and Sprint method of some components uses "+=" to add the result. The string returned by Srender would be very long, and using "+=" will waste some memory.

strings.Builder is specially designed to handle such scenarios. strings.Builder was introduced in Go 1.10, and its doc says "A Builder is used to efficiently build a string".

I did a simple benchmark using Barchart as an example:

func BenchmarkBarchart(b *testing.B) {
	bars := []Bar{
		{Label: "A", Value: 10},
		{Label: "B", Value: 20},
		{Label: "C", Value: 30},
		{Label: "D", Value: 40},
		{Label: "E", Value: 50},
		{Label: "F", Value: 40},
		{Label: "G", Value: 30},
		{Label: "H", Value: 20},
		{Label: "I", Value: 10},
	}

	for i := 0; i < b.N; i++ {
		DefaultBarChart.WithBars(bars).Render()
	}
}

and this is the result:

$ go test -run='^$' -bench='BenchmarkBarchart' -benchmem -count=10 > old.bench

# change `var ret string` to `var ret strings.Builder`
$ go test -run='^$' -bench='BenchmarkBarchart' -benchmem -count=10 > new.bench
$ benchstat old.bench new.bench

goos: windows
goarch: amd64
pkg: github.com/pterm/pterm
cpu: Intel(R) Core(TM) i5-10200H CPU @ 2.40GHz
           │  old.bench  │           new.bench           │
           │   sec/op    │   sec/op     vs base          │
Barchart-8   97.84m ± 7%   94.15m ± 4%  ~ (p=0.123 n=10)

           │  old.bench   │              new.bench               │
           │     B/op     │     B/op      vs base                │
Barchart-8   5.803Mi ± 8%   4.179Mi ± 7%  -27.99% (p=0.000 n=10)

           │  old.bench  │             new.bench              │
           │  allocs/op  │  allocs/op   vs base               │
Barchart-8   9.902k ± 0%   9.501k ± 0%  -4.05% (p=0.000 n=10)

Using strings.Builder can save 28% of memory and reduce 4% allocations. There is no obvious change in the speed of Srender (in fact, the speed can become faster after reducing the number of memory allocations)

The changes of code is also quite simple:

func (p BarChartPrinter) Srender() (string, error) {
-	var ret string
+	var ret strings.Builder
	...
	for i := 0; i <= maxBarHeight; i++ {
		for _, barString := range renderedBars {
			...
-			ret += barLine
+			ret.WriteString(barLine)
		}
-		ret += "\n"
+		ret.WriteByte('\n')
	}

-	return ret, nil
+	return ret.String(), nil
}

Code that needs to be modified: Srender and Sprint methods that require a lot of string concatenation. All modifications can be seen in the related PR submitted later.

These modifications will not change the existing APIs. It is a simple and harmless refactoring.

I hope you would take the time to review this proposal.

@apocelipes apocelipes added the proposal Proposal to add a new feature to pterm label Mar 13, 2024
apocelipes added a commit to apocelipes/pterm that referenced this issue Mar 18, 2024
@apocelipes apocelipes linked a pull request Mar 18, 2024 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
proposal Proposal to add a new feature to pterm
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant