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

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