v0.29.0 21b3150

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 ProfileBalanced:
89		return resolveBalancedProfile()
90	case ProfileMax:
91		return resolveMaxProfile(hw)
92	case ProfileCustom:
93		return resolveCustomProfile(cfg)
94	default: // ProfileDefault or empty
95		return resolveDefaultProfile()
96	}
97}
98
99// resolveDefaultProfile returns WebKit defaults (no tuning).
100func resolveDefaultProfile() ResolvedPerformanceSettings {
101	return ResolvedPerformanceSettings{
102		// All zeros/negatives mean "unset, use WebKit defaults"
103		SkiaCPUPaintingThreads: 0,
104		SkiaGPUPaintingThreads: -1,
105		SkiaEnableCPURendering: false,
106
107		WebProcessMemoryLimitMB:               0,
108		WebProcessMemoryPollIntervalSec:       0,
109		WebProcessMemoryConservativeThreshold: 0,
110		WebProcessMemoryStrictThreshold:       0,
111
112		NetworkProcessMemoryLimitMB:               0,
113		NetworkProcessMemoryPollIntervalSec:       0,
114		NetworkProcessMemoryConservativeThreshold: 0,
115		NetworkProcessMemoryStrictThreshold:       0,
116
117		WebViewPoolPrewarmCount: 4,
118	}
119}
120
121// resolveBalancedProfile preserves the legacy balanced profile as WebKit's
122// default tuning with the normal WebView prewarm count.
123func resolveBalancedProfile() ResolvedPerformanceSettings {
124	return resolveDefaultProfile()
125}
126
127// resolveLiteProfile returns settings optimized for low-RAM systems.
128// Uses hardware info to scale CPU threads if available.
129func resolveLiteProfile(hw *port.HardwareInfo) ResolvedPerformanceSettings {
130	// Use 2 CPU threads, or scale down on very low-core systems
131	cpuThreads := 2
132	if hw != nil && hw.CPUCores > 0 && hw.CPUCores < 4 {
133		cpuThreads = 1
134	}
135
136	return ResolvedPerformanceSettings{
137		SkiaCPUPaintingThreads: cpuThreads,
138		SkiaGPUPaintingThreads: -1, // unset
139		SkiaEnableCPURendering: false,
140
141		WebProcessMemoryLimitMB:               768,
142		WebProcessMemoryPollIntervalSec:       0, // use WebKit default (30s)
143		WebProcessMemoryConservativeThreshold: 0.25,
144		WebProcessMemoryStrictThreshold:       0.4,
145
146		NetworkProcessMemoryLimitMB:               384,
147		NetworkProcessMemoryPollIntervalSec:       0,
148		NetworkProcessMemoryConservativeThreshold: 0.25,
149		NetworkProcessMemoryStrictThreshold:       0.4,
150
151		WebViewPoolPrewarmCount: 2,
152	}
153}
154
155// resolveMaxProfile returns settings optimized for maximum responsiveness.
156// Scales values based on detected hardware capabilities.
157func resolveMaxProfile(hw *port.HardwareInfo) ResolvedPerformanceSettings {
158	// CPU threads: use physical cores (not threads), capped at WebKitGTK limit
159	cpuThreads := computeMaxCPUThreads(hw)
160
161	// GPU threads: scale based on VRAM
162	gpuThreads := computeMaxGPUThreads(hw)
163
164	// WebView pool: scale based on RAM
165	poolPrewarm := computeMaxPoolPrewarm(hw)
166
167	return ResolvedPerformanceSettings{
168		SkiaCPUPaintingThreads: cpuThreads,
169		SkiaGPUPaintingThreads: gpuThreads,
170		SkiaEnableCPURendering: false,
171
172		// Max profile: no memory limits, let WebKit use defaults
173		WebProcessMemoryLimitMB:               0,
174		WebProcessMemoryPollIntervalSec:       0,
175		WebProcessMemoryConservativeThreshold: 0,
176		WebProcessMemoryStrictThreshold:       0,
177
178		NetworkProcessMemoryLimitMB:               0,
179		NetworkProcessMemoryPollIntervalSec:       0,
180		NetworkProcessMemoryConservativeThreshold: 0,
181		NetworkProcessMemoryStrictThreshold:       0,
182
183		WebViewPoolPrewarmCount: poolPrewarm,
184	}
185}
186
187// computeMaxCPUThreads calculates Skia CPU threads for max profile.
188// Uses physical cores (not hyperthreaded) capped at WebKitGTK's limit.
189func computeMaxCPUThreads(hw *port.HardwareInfo) int {
190	var cpuThreads int
191
192	if hw != nil && hw.CPUCores > 0 {
193		// Use physical cores directly - they're better for parallel painting
194		cpuThreads = hw.CPUCores
195	} else {
196		// Fallback: assume hyperthreading, use half of logical CPUs
197		cpuThreads = runtime.NumCPU() / 2
198	}
199
200	// Minimum 4 for meaningful parallelism
201	if cpuThreads < 4 {
202		cpuThreads = 4
203	}
204	// Cap at WebKitGTK Skia limit
205	if cpuThreads > maxSkiaCPUThreads {
206		cpuThreads = maxSkiaCPUThreads
207	}
208
209	return cpuThreads
210}
211
212// computeMaxGPUThreads calculates Skia GPU threads based on VRAM.
213func computeMaxGPUThreads(hw *port.HardwareInfo) int {
214	if hw == nil || hw.VRAM == 0 {
215		// No VRAM info - use conservative default
216		return gpuThreadsMin
217	}
218
219	// Scale GPU threads based on VRAM tiers
220	switch {
221	case hw.VRAM >= vramTierHigh: // 16GB+ VRAM (high-end: RX 7900, RTX 4080+)
222		return gpuThreadsHigh
223	case hw.VRAM >= vramTierMid: // 8-16GB VRAM (mid-range: RX 7800, RTX 4070)
224		return gpuThreadsMid
225	case hw.VRAM >= vramTierLow: // 4-8GB VRAM (entry discrete)
226		return gpuThreadsLow
227	default: // <4GB VRAM (integrated / old discrete)
228		return gpuThreadsMin
229	}
230}
231
232// computeMaxMemoryLimits returns web process and network process memory limits.
233func computeMaxMemoryLimits(hw *port.HardwareInfo) (webMB, netMB int) {
234	if hw == nil || hw.TotalRAM == 0 {
235		// Fallback: assume 16GB system
236		return webMemFallback, netMemFallback
237	}
238
239	// Scale memory limits based on system RAM
240	switch {
241	case hw.TotalRAM >= ramTierHigh: // 32GB+ RAM
242		return webMemHigh, netMemHigh
243	case hw.TotalRAM >= ramTierMedium: // 16-32GB RAM
244		return webMemMedium, netMemMedium
245	case hw.TotalRAM >= ramTierLow: // 8-16GB RAM
246		return webMemLow, netMemLow
247	default: // <8GB RAM
248		return webMemMin, netMemMin
249	}
250}
251
252// computeMaxPoolPrewarm returns WebView pool prewarm count based on RAM.
253func computeMaxPoolPrewarm(hw *port.HardwareInfo) int {
254	if hw == nil || hw.TotalRAM == 0 {
255		return poolPrewarmFallback
256	}
257
258	switch {
259	case hw.TotalRAM >= ramTierHigh: // 32GB+
260		return poolPrewarmHigh
261	case hw.TotalRAM >= ramTierMedium: // 16-32GB
262		return poolPrewarmMedium
263	case hw.TotalRAM >= ramTierLow: // 8-16GB
264		return poolPrewarmLow
265	default: // <8GB
266		return poolPrewarmMin
267	}
268}
269
270// resolveCustomProfile returns the user's configured values directly.
271func resolveCustomProfile(cfg *PerformanceConfig) ResolvedPerformanceSettings {
272	return ResolvedPerformanceSettings{
273		SkiaCPUPaintingThreads: cfg.SkiaCPUPaintingThreads,
274		SkiaGPUPaintingThreads: cfg.SkiaGPUPaintingThreads,
275		SkiaEnableCPURendering: cfg.SkiaEnableCPURendering,
276
277		WebProcessMemoryLimitMB:               cfg.WebProcessMemoryLimitMB,
278		WebProcessMemoryPollIntervalSec:       cfg.WebProcessMemoryPollIntervalSec,
279		WebProcessMemoryConservativeThreshold: cfg.WebProcessMemoryConservativeThreshold,
280		WebProcessMemoryStrictThreshold:       cfg.WebProcessMemoryStrictThreshold,
281
282		NetworkProcessMemoryLimitMB:               cfg.NetworkProcessMemoryLimitMB,
283		NetworkProcessMemoryPollIntervalSec:       cfg.NetworkProcessMemoryPollIntervalSec,
284		NetworkProcessMemoryConservativeThreshold: cfg.NetworkProcessMemoryConservativeThreshold,
285		NetworkProcessMemoryStrictThreshold:       cfg.NetworkProcessMemoryStrictThreshold,
286
287		WebViewPoolPrewarmCount: cfg.WebViewPoolPrewarmCount,
288	}
289}
290
291// HasCustomPerformanceFields returns true if any individual performance tuning
292// fields are set to non-default values. Used for validation.
293func HasCustomPerformanceFields(cfg *PerformanceConfig) bool {
294	return cfg.SkiaCPUPaintingThreads != 0 ||
295		(cfg.SkiaGPUPaintingThreads != -1 && cfg.SkiaGPUPaintingThreads != 0) ||
296		cfg.SkiaEnableCPURendering ||
297		cfg.WebProcessMemoryLimitMB != 0 ||
298		cfg.WebProcessMemoryPollIntervalSec != 0 ||
299		cfg.WebProcessMemoryConservativeThreshold != 0 ||
300		cfg.WebProcessMemoryStrictThreshold != 0 ||
301		cfg.NetworkProcessMemoryLimitMB != 0 ||
302		cfg.NetworkProcessMemoryPollIntervalSec != 0 ||
303		cfg.NetworkProcessMemoryConservativeThreshold != 0 ||
304		cfg.NetworkProcessMemoryStrictThreshold != 0
305}
306
307// IsValidPerformanceProfile returns true if the profile name is recognized.
308func IsValidPerformanceProfile(profile PerformanceProfile) bool {
309	switch profile {
310	case ProfileDefault, ProfileLite, ProfileBalanced, ProfileMax, ProfileCustom, "":
311		return true
312	default:
313		return false
314	}
315}

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