v0.26.1 75cd777

profile.go RawHistoryFolder

1package config
2
3import (
4	"runtime"
5
6	"github.com/bnema/dumber/internal/application/port"
7)
8
9const (
10	// maxSkiaCPUThreads is the maximum number of Skia CPU painting threads
11	// supported by WebKitGTK (0-8 range).
12	maxSkiaCPUThreads = 8
13
14	// maxSkiaGPUThreads is the maximum number of Skia GPU painting threads
15	// supported by WebKitGTK (-1 to 8 range, where -1 means "let WebKit decide").
16	maxSkiaGPUThreads = 8
17
18	// Memory tier thresholds (in bytes)
19	// Explicitly typed as uint64 to prevent overflow on 32-bit systems.
20	ramTierLow    uint64 = 8 * 1024 * 1024 * 1024  // 8 GB
21	ramTierMedium uint64 = 16 * 1024 * 1024 * 1024 // 16 GB
22	ramTierHigh   uint64 = 32 * 1024 * 1024 * 1024 // 32 GB
23
24	// VRAM tier thresholds (in bytes)
25	vramTierLow  uint64 = 4 * 1024 * 1024 * 1024  // 4 GB (low-end discrete / integrated)
26	vramTierMid  uint64 = 8 * 1024 * 1024 * 1024  // 8 GB (mid-range discrete)
27	vramTierHigh uint64 = 16 * 1024 * 1024 * 1024 // 16 GB (high-end discrete)
28
29	// GPU thread scaling values
30	gpuThreadsHigh = 8 // For 16GB+ VRAM
31	gpuThreadsMid  = 6 // For 8-16GB VRAM
32	gpuThreadsLow  = 4 // For 4-8GB VRAM
33	gpuThreadsMin  = 2 // For <4GB VRAM or unknown
34
35	// Memory limit values (in MB)
36	webMemHigh     = 4096 // For 32GB+ RAM
37	webMemMedium   = 3072 // For 16-32GB RAM
38	webMemLow      = 2048 // For 8-16GB RAM
39	webMemMin      = 1024 // For <8GB RAM
40	netMemHigh     = 1024 // For 32GB+ RAM
41	netMemMedium   = 768  // For 16-32GB RAM
42	netMemLow      = 512  // For 8-16GB RAM
43	netMemMin      = 256  // For <8GB RAM
44	webMemFallback = 2048 // When RAM unknown
45	netMemFallback = 512  // When RAM unknown
46
47	// WebView pool prewarm counts
48	poolPrewarmHigh     = 12 // For 32GB+ RAM
49	poolPrewarmMedium   = 8  // For 16-32GB RAM
50	poolPrewarmLow      = 6  // For 8-16GB RAM
51	poolPrewarmMin      = 4  // For <8GB RAM
52	poolPrewarmFallback = 8  // When RAM unknown
53)
54
55// ResolvedPerformanceSettings contains the computed performance settings
56// after profile resolution. These are the actual values to apply.
57type ResolvedPerformanceSettings struct {
58	// Skia threading
59	SkiaCPUPaintingThreads int
60	SkiaGPUPaintingThreads int
61	SkiaEnableCPURendering bool
62
63	// Web process memory pressure
64	WebProcessMemoryLimitMB               int
65	WebProcessMemoryPollIntervalSec       float64
66	WebProcessMemoryConservativeThreshold float64
67	WebProcessMemoryStrictThreshold       float64
68
69	// Network process memory pressure
70	NetworkProcessMemoryLimitMB               int
71	NetworkProcessMemoryPollIntervalSec       float64
72	NetworkProcessMemoryConservativeThreshold float64
73	NetworkProcessMemoryStrictThreshold       float64
74
75	// WebView pool
76	WebViewPoolPrewarmCount int
77}
78
79// ResolvePerformanceProfile computes the actual performance settings based on
80// the selected profile. For ProfileCustom, it returns the user's configured values.
81// For other profiles, it computes appropriate values based on detected hardware.
82//
83// The hw parameter is optional - if nil, fallback values are used.
84func ResolvePerformanceProfile(cfg *PerformanceConfig, hw *port.HardwareInfo) ResolvedPerformanceSettings {
85	switch cfg.Profile {
86	case ProfileLite:
87		return resolveLiteProfile(hw)
88	case ProfileMax:
89		return resolveMaxProfile(hw)
90	case ProfileCustom:
91		return resolveCustomProfile(cfg)
92	default: // ProfileDefault or empty
93		return resolveDefaultProfile()
94	}
95}
96
97// resolveDefaultProfile returns WebKit defaults (no tuning).
98func resolveDefaultProfile() ResolvedPerformanceSettings {
99	return ResolvedPerformanceSettings{
100		// All zeros/negatives mean "unset, use WebKit defaults"
101		SkiaCPUPaintingThreads: 0,
102		SkiaGPUPaintingThreads: -1,
103		SkiaEnableCPURendering: false,
104
105		WebProcessMemoryLimitMB:               0,
106		WebProcessMemoryPollIntervalSec:       0,
107		WebProcessMemoryConservativeThreshold: 0,
108		WebProcessMemoryStrictThreshold:       0,
109
110		NetworkProcessMemoryLimitMB:               0,
111		NetworkProcessMemoryPollIntervalSec:       0,
112		NetworkProcessMemoryConservativeThreshold: 0,
113		NetworkProcessMemoryStrictThreshold:       0,
114
115		WebViewPoolPrewarmCount: 4,
116	}
117}
118
119// resolveLiteProfile returns settings optimized for low-RAM systems.
120// Uses hardware info to scale CPU threads if available.
121func resolveLiteProfile(hw *port.HardwareInfo) ResolvedPerformanceSettings {
122	// Use 2 CPU threads, or scale down on very low-core systems
123	cpuThreads := 2
124	if hw != nil && hw.CPUCores > 0 && hw.CPUCores < 4 {
125		cpuThreads = 1
126	}
127
128	return ResolvedPerformanceSettings{
129		SkiaCPUPaintingThreads: cpuThreads,
130		SkiaGPUPaintingThreads: -1, // unset
131		SkiaEnableCPURendering: false,
132
133		WebProcessMemoryLimitMB:               768,
134		WebProcessMemoryPollIntervalSec:       0, // use WebKit default (30s)
135		WebProcessMemoryConservativeThreshold: 0.25,
136		WebProcessMemoryStrictThreshold:       0.4,
137
138		NetworkProcessMemoryLimitMB:               384,
139		NetworkProcessMemoryPollIntervalSec:       0,
140		NetworkProcessMemoryConservativeThreshold: 0.25,
141		NetworkProcessMemoryStrictThreshold:       0.4,
142
143		WebViewPoolPrewarmCount: 2,
144	}
145}
146
147// resolveMaxProfile returns settings optimized for maximum responsiveness.
148// Scales values based on detected hardware capabilities.
149func resolveMaxProfile(hw *port.HardwareInfo) ResolvedPerformanceSettings {
150	// CPU threads: use physical cores (not threads), capped at WebKitGTK limit
151	cpuThreads := computeMaxCPUThreads(hw)
152
153	// GPU threads: scale based on VRAM
154	gpuThreads := computeMaxGPUThreads(hw)
155
156	// WebView pool: scale based on RAM
157	poolPrewarm := computeMaxPoolPrewarm(hw)
158
159	return ResolvedPerformanceSettings{
160		SkiaCPUPaintingThreads: cpuThreads,
161		SkiaGPUPaintingThreads: gpuThreads,
162		SkiaEnableCPURendering: false,
163
164		// Max profile: no memory limits, let WebKit use defaults
165		WebProcessMemoryLimitMB:               0,
166		WebProcessMemoryPollIntervalSec:       0,
167		WebProcessMemoryConservativeThreshold: 0,
168		WebProcessMemoryStrictThreshold:       0,
169
170		NetworkProcessMemoryLimitMB:               0,
171		NetworkProcessMemoryPollIntervalSec:       0,
172		NetworkProcessMemoryConservativeThreshold: 0,
173		NetworkProcessMemoryStrictThreshold:       0,
174
175		WebViewPoolPrewarmCount: poolPrewarm,
176	}
177}
178
179// computeMaxCPUThreads calculates Skia CPU threads for max profile.
180// Uses physical cores (not hyperthreaded) capped at WebKitGTK's limit.
181func computeMaxCPUThreads(hw *port.HardwareInfo) int {
182	var cpuThreads int
183
184	if hw != nil && hw.CPUCores > 0 {
185		// Use physical cores directly - they're better for parallel painting
186		cpuThreads = hw.CPUCores
187	} else {
188		// Fallback: assume hyperthreading, use half of logical CPUs
189		cpuThreads = runtime.NumCPU() / 2
190	}
191
192	// Minimum 4 for meaningful parallelism
193	if cpuThreads < 4 {
194		cpuThreads = 4
195	}
196	// Cap at WebKitGTK Skia limit
197	if cpuThreads > maxSkiaCPUThreads {
198		cpuThreads = maxSkiaCPUThreads
199	}
200
201	return cpuThreads
202}
203
204// computeMaxGPUThreads calculates Skia GPU threads based on VRAM.
205func computeMaxGPUThreads(hw *port.HardwareInfo) int {
206	if hw == nil || hw.VRAM == 0 {
207		// No VRAM info - use conservative default
208		return gpuThreadsMin
209	}
210
211	// Scale GPU threads based on VRAM tiers
212	switch {
213	case hw.VRAM >= vramTierHigh: // 16GB+ VRAM (high-end: RX 7900, RTX 4080+)
214		return gpuThreadsHigh
215	case hw.VRAM >= vramTierMid: // 8-16GB VRAM (mid-range: RX 7800, RTX 4070)
216		return gpuThreadsMid
217	case hw.VRAM >= vramTierLow: // 4-8GB VRAM (entry discrete)
218		return gpuThreadsLow
219	default: // <4GB VRAM (integrated / old discrete)
220		return gpuThreadsMin
221	}
222}
223
224// computeMaxMemoryLimits returns web process and network process memory limits.
225func computeMaxMemoryLimits(hw *port.HardwareInfo) (webMB, netMB int) {
226	if hw == nil || hw.TotalRAM == 0 {
227		// Fallback: assume 16GB system
228		return webMemFallback, netMemFallback
229	}
230
231	// Scale memory limits based on system RAM
232	switch {
233	case hw.TotalRAM >= ramTierHigh: // 32GB+ RAM
234		return webMemHigh, netMemHigh
235	case hw.TotalRAM >= ramTierMedium: // 16-32GB RAM
236		return webMemMedium, netMemMedium
237	case hw.TotalRAM >= ramTierLow: // 8-16GB RAM
238		return webMemLow, netMemLow
239	default: // <8GB RAM
240		return webMemMin, netMemMin
241	}
242}
243
244// computeMaxPoolPrewarm returns WebView pool prewarm count based on RAM.
245func computeMaxPoolPrewarm(hw *port.HardwareInfo) int {
246	if hw == nil || hw.TotalRAM == 0 {
247		return poolPrewarmFallback
248	}
249
250	switch {
251	case hw.TotalRAM >= ramTierHigh: // 32GB+
252		return poolPrewarmHigh
253	case hw.TotalRAM >= ramTierMedium: // 16-32GB
254		return poolPrewarmMedium
255	case hw.TotalRAM >= ramTierLow: // 8-16GB
256		return poolPrewarmLow
257	default: // <8GB
258		return poolPrewarmMin
259	}
260}
261
262// resolveCustomProfile returns the user's configured values directly.
263func resolveCustomProfile(cfg *PerformanceConfig) ResolvedPerformanceSettings {
264	return ResolvedPerformanceSettings{
265		SkiaCPUPaintingThreads: cfg.SkiaCPUPaintingThreads,
266		SkiaGPUPaintingThreads: cfg.SkiaGPUPaintingThreads,
267		SkiaEnableCPURendering: cfg.SkiaEnableCPURendering,
268
269		WebProcessMemoryLimitMB:               cfg.WebProcessMemoryLimitMB,
270		WebProcessMemoryPollIntervalSec:       cfg.WebProcessMemoryPollIntervalSec,
271		WebProcessMemoryConservativeThreshold: cfg.WebProcessMemoryConservativeThreshold,
272		WebProcessMemoryStrictThreshold:       cfg.WebProcessMemoryStrictThreshold,
273
274		NetworkProcessMemoryLimitMB:               cfg.NetworkProcessMemoryLimitMB,
275		NetworkProcessMemoryPollIntervalSec:       cfg.NetworkProcessMemoryPollIntervalSec,
276		NetworkProcessMemoryConservativeThreshold: cfg.NetworkProcessMemoryConservativeThreshold,
277		NetworkProcessMemoryStrictThreshold:       cfg.NetworkProcessMemoryStrictThreshold,
278
279		WebViewPoolPrewarmCount: cfg.WebViewPoolPrewarmCount,
280	}
281}
282
283// HasCustomPerformanceFields returns true if any individual performance tuning
284// fields are set to non-default values. Used for validation.
285func HasCustomPerformanceFields(cfg *PerformanceConfig) bool {
286	return cfg.SkiaCPUPaintingThreads != 0 ||
287		cfg.SkiaGPUPaintingThreads != -1 ||
288		cfg.SkiaEnableCPURendering ||
289		cfg.WebProcessMemoryLimitMB != 0 ||
290		cfg.WebProcessMemoryPollIntervalSec != 0 ||
291		cfg.WebProcessMemoryConservativeThreshold != 0 ||
292		cfg.WebProcessMemoryStrictThreshold != 0 ||
293		cfg.NetworkProcessMemoryLimitMB != 0 ||
294		cfg.NetworkProcessMemoryPollIntervalSec != 0 ||
295		cfg.NetworkProcessMemoryConservativeThreshold != 0 ||
296		cfg.NetworkProcessMemoryStrictThreshold != 0
297}
298
299// IsValidPerformanceProfile returns true if the profile name is recognized.
300func IsValidPerformanceProfile(profile PerformanceProfile) bool {
301	switch profile {
302	case ProfileDefault, ProfileLite, ProfileMax, ProfileCustom, "":
303		return true
304	default:
305		return false
306	}
307}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close