From 509d39efa096dc161ade2b492e461e5e6f28226c Mon Sep 17 00:00:00 2001 From: Christian Rocha Date: Sat, 7 Jun 2025 08:54:35 -0400 Subject: [PATCH 1/4] perf(anim): use faster PRNG --- internal/tui/components/anim/anim.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/tui/components/anim/anim.go b/internal/tui/components/anim/anim.go index 46c4156b02d148f51e8ff03afd7341354de1ad44..18585f1a82a8dadc916b0af69ad4c1f067286bf4 100644 --- a/internal/tui/components/anim/anim.go +++ b/internal/tui/components/anim/anim.go @@ -3,7 +3,7 @@ package anim import ( "fmt" "image/color" - "math/rand" + "math/rand/v2" "strings" "time" @@ -41,7 +41,7 @@ type cyclingChar struct { } func (c cyclingChar) randomRune() rune { - return (charRunes)[rand.Intn(len(charRunes))] //nolint:gosec + return (charRunes)[rand.IntN(len(charRunes))] //nolint:gosec } func (c cyclingChar) state(start time.Time) charState { @@ -137,7 +137,7 @@ func New(cyclingCharsSize uint, label string, opts ...animOption) Animation { } makeDelay := func(a int32, b time.Duration) time.Duration { - return time.Duration(rand.Int31n(a)) * (time.Millisecond * b) //nolint:gosec + return time.Duration(rand.Int32N(a)) * (time.Millisecond * b) //nolint:gosec } makeInitialDelay := func() time.Duration { From 301d42f7197944baf83c1d8dd90beb3328479006 Mon Sep 17 00:00:00 2001 From: Christian Rocha Date: Sat, 7 Jun 2025 08:56:08 -0400 Subject: [PATCH 2/4] perf(anim): eliminate string concatenation --- internal/tui/components/anim/anim.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/tui/components/anim/anim.go b/internal/tui/components/anim/anim.go index 18585f1a82a8dadc916b0af69ad4c1f067286bf4..016165313008e5d46875de3f02a9ea0dde016894 100644 --- a/internal/tui/components/anim/anim.go +++ b/internal/tui/components/anim/anim.go @@ -258,7 +258,7 @@ func (a anim) View() tea.View { textStyle.Render(string(c.currentValue)), ) } - return tea.NewView(b.String() + textStyle.Render(a.ellipsis.View())) + b.WriteString(textStyle.Render(a.ellipsis.View())) } return tea.NewView(b.String()) From c519c8ed1071f3b209eef4e95d2291c44386ff57 Mon Sep 17 00:00:00 2001 From: Christian Rocha Date: Sat, 7 Jun 2025 08:57:53 -0400 Subject: [PATCH 3/4] perf(anim): reduce pointer dereferences --- internal/tui/components/anim/anim.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/internal/tui/components/anim/anim.go b/internal/tui/components/anim/anim.go index 016165313008e5d46875de3f02a9ea0dde016894..3a7b615f6b09a7b8c3a6fac5909cccc6eccb4bb3 100644 --- a/internal/tui/components/anim/anim.go +++ b/internal/tui/components/anim/anim.go @@ -226,14 +226,15 @@ func (a anim) ID() string { } func (a *anim) updateChars(chars *[]cyclingChar) { - for i, c := range *chars { + charSlice := *chars // dereference to avoid repeated pointer access + for i, c := range charSlice { switch c.state(a.start) { case charInitialState: - (*chars)[i].currentValue = '.' + charSlice[i].currentValue = '.' case charCyclingState: - (*chars)[i].currentValue = c.randomRune() + charSlice[i].currentValue = c.randomRune() case charEndOfLifeState: - (*chars)[i].currentValue = c.finalValue + charSlice[i].currentValue = c.finalValue } } } From cec27925c596a9e04d2e1674b4893e853f87efa1 Mon Sep 17 00:00:00 2001 From: Christian Rocha Date: Sat, 7 Jun 2025 09:00:33 -0400 Subject: [PATCH 4/4] perf(anim): preallocate strings.Builder capacities --- internal/tui/components/anim/anim.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/internal/tui/components/anim/anim.go b/internal/tui/components/anim/anim.go index 3a7b615f6b09a7b8c3a6fac5909cccc6eccb4bb3..c39de0d899a1b4eaf3896ea32b02883374af1195 100644 --- a/internal/tui/components/anim/anim.go +++ b/internal/tui/components/anim/anim.go @@ -241,8 +241,19 @@ func (a *anim) updateChars(chars *[]cyclingChar) { // View renders the animation. func (a anim) View() tea.View { - t := styles.CurrentTheme() - var b strings.Builder + var ( + t = styles.CurrentTheme() + b strings.Builder + ) + + // Pre-allocate builder capacity to avoid reallocations. + // Estimate: cycling chars + label chars + ellipsis + style overhead. + const ( + bytesPerChar = 20 // ANSI styling + bufferSize = 50 // ellipsis and safety margin + ) + estimatedCap := len(a.cyclingChars)*bytesPerChar + len(a.labelChars)*bytesPerChar + bufferSize + b.Grow(estimatedCap) for i, c := range a.cyclingChars { if len(a.ramp) > i {