diff options
| author | Paul Buetow <paul@buetow.org> | 2026-03-05 23:30:38 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-03-05 23:30:38 +0200 |
| commit | 33dbb917be1e30d3de9640bec18d0c619a1a7f67 (patch) | |
| tree | 8a00d9d646f895a6049ce57c200493a3e06235fc | |
| parent | 642e3eca14b839c9412a092564f6a76d5777455d (diff) | |
Reduce flamegraph hot-path allocations
| -rw-r--r-- | internal/tui/flamegraph/bench_test.go | 8 | ||||
| -rw-r--r-- | internal/tui/flamegraph/renderer.go | 17 | ||||
| -rw-r--r-- | internal/tui/flamegraph/search.go | 20 |
3 files changed, 31 insertions, 14 deletions
diff --git a/internal/tui/flamegraph/bench_test.go b/internal/tui/flamegraph/bench_test.go index e908fed..c000ac0 100644 --- a/internal/tui/flamegraph/bench_test.go +++ b/internal/tui/flamegraph/bench_test.go @@ -220,9 +220,11 @@ func BenchmarkAnimationTick(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { if !state.Tick(0) { - state.SetTargets(base) - state.SetTargets(target) - _ = state.Tick(0) + for idx := range state.springs { + state.springs[idx].targetCol += 3 + state.springs[idx].targetW += 2 + } + state.settled = false } frames := state.CurrentFrames() benchIntSink = frames[len(frames)-1].Width diff --git a/internal/tui/flamegraph/renderer.go b/internal/tui/flamegraph/renderer.go index 6fba0b6..ad74173 100644 --- a/internal/tui/flamegraph/renderer.go +++ b/internal/tui/flamegraph/renderer.go @@ -14,6 +14,7 @@ import ( ) const pathSeparator = "\x1f" +const pathSeparatorByte = '\x1f' const minFlameWidth = 60 // BuildTerminalLayout converts a live trie snapshot into terminal frame cells. @@ -224,7 +225,7 @@ func renderRow(frames []indexedFrame, width int, selectedPath string, subtreeSet continue } label := padOrTrim(frame.Name, cellWidth) - style := styleForFrame(item.idx, frame, selectedPath, subtreeSet, matchSet, selectedIdx, isDark, searchActive).Width(cellWidth) + style := styleForFrame(item.idx, frame, selectedPath, subtreeSet, matchSet, selectedIdx, isDark, searchActive) cell := style.Render(label) b.WriteString(cell) cursor = frame.Col + cellWidth @@ -255,14 +256,24 @@ func computeSubtreeSetInto(frames []tuiFrame, selectedIdx int, subtree map[int]b for idx, frame := range frames { path := frame.Path if path == selectedPath || - strings.HasPrefix(path, selectedPath+pathSeparator) || - strings.HasPrefix(selectedPath, path+pathSeparator) { + hasPathBoundaryPrefix(path, selectedPath) || + hasPathBoundaryPrefix(selectedPath, path) { subtree[idx] = true } } return subtree } +func hasPathBoundaryPrefix(value, prefix string) bool { + if len(value) <= len(prefix) { + return false + } + if !strings.HasPrefix(value, prefix) { + return false + } + return value[len(prefix)] == pathSeparatorByte +} + func styleForFrame(idx int, frame tuiFrame, selectedPath string, subtreeSet, matchSet map[int]bool, selectedIdx int, isDark, searchActive bool) lipgloss.Style { base := lipgloss.NewStyle(). Foreground(common.ColorBackground). diff --git a/internal/tui/flamegraph/search.go b/internal/tui/flamegraph/search.go index 8e392d1..f42c36e 100644 --- a/internal/tui/flamegraph/search.go +++ b/internal/tui/flamegraph/search.go @@ -92,21 +92,25 @@ func (m Model) searchFooter() string { } func replaceFooterLine(content, footer string) string { - lines := strings.Split(content, "\n") - if len(lines) == 0 { + if content == "" { return footer } - lines[len(lines)-1] = footer - return strings.Join(lines, "\n") + lastNewline := strings.LastIndexByte(content, '\n') + if lastNewline == -1 { + return footer + } + return content[:lastNewline+1] + footer } func replaceHeaderLine(content, header string) string { - lines := strings.Split(content, "\n") - if len(lines) == 0 { + if content == "" { + return header + } + firstNewline := strings.IndexByte(content, '\n') + if firstNewline == -1 { return header } - lines[0] = header - return strings.Join(lines, "\n") + return header + content[firstNewline:] } func clearBoolMap[K comparable](values map[K]bool) { |
