diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 0000000..0b61b18 --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2024-04-29 - Cache Large Map Allocations at Instance Level +**Learning:** Returning a large `map[string]string` from a function like `colors.ColorMap()` on every plugin invocation creates significant repeated memory allocation overhead, particularly in parallel rendering operations like `StatusLine.Render()` which executes 5-10 times. +**Action:** Always cache large reference types (like maps) at the struct instance level (e.g. within `StatusLine`) during initialization to avoid redundant allocations in hot loops. Add fallback initialization `if sl.colorMap == nil` for backward compatibility with manual struct initialization in tests. diff --git a/internal/statusline/statusline.go b/internal/statusline/statusline.go index ebbc115..f195fb6 100644 --- a/internal/statusline/statusline.go +++ b/internal/statusline/statusline.go @@ -35,6 +35,7 @@ type StatusLine struct { isIdle bool bashPlugins []plugin.Plugin // Cached discovered bash plugins bashPluginsOnce sync.Once + colorMap map[string]string // Cached color map to avoid allocations } // New creates a new StatusLine renderer @@ -45,6 +46,7 @@ func New(input Input, cfg config.Config) *StatusLine { pluginManager: plugin.NewManager(), nativePlugins: plugins.NewRegistry(), isIdle: checkIsIdle(input.SessionID), + colorMap: colors.ColorMap(), } } @@ -612,6 +614,11 @@ func (sl *StatusLine) getConfigBool(key string, defVal bool) bool { } func (sl *StatusLine) runPlugin(name string) string { + // Initialize colorMap if nil (for tests that construct StatusLine manually) + if sl.colorMap == nil { + sl.colorMap = colors.ColorMap() + } + // Build plugin input input := plugin.Input{ Prism: plugin.PrismContext{ @@ -634,7 +641,7 @@ func (sl *StatusLine) runPlugin(name string) string { ContextWindowSize: sl.input.Context.ContextWindow, }, Config: sl.getPluginConfig(name), - Colors: colors.ColorMap(), + Colors: sl.colorMap, } // Try native plugin first (much faster - no subprocess)