Game detection: foreign GPU holders take an external lock hold (GAME_PROCS, GPU_FOREIGN_VRAM_MB)
This commit is contained in:
@@ -61,6 +61,17 @@ type Config struct {
|
||||
ComfyIdleTimeout time.Duration
|
||||
ComfyStartTimeout time.Duration
|
||||
|
||||
// GameProcs (GAME_PROCS) is a watch list of process names; while any of
|
||||
// them runs, the GPU is treated as held by a foreign process. The
|
||||
// nvidia-smi path (GPUForeignVRAMMB, GPU_FOREIGN_VRAM_MB) does the same
|
||||
// when a process not in GPUIgnoreProcs (GPU_IGNORE_PROCS) holds more than
|
||||
// that many MiB of VRAM. GamePollInterval (GAME_POLL_INTERVAL) is how
|
||||
// often both checks run.
|
||||
GameProcs []string
|
||||
GPUForeignVRAMMB int
|
||||
GPUIgnoreProcs []string
|
||||
GamePollInterval time.Duration
|
||||
|
||||
WarmModel string
|
||||
LogLevel slog.Level
|
||||
LogJSON bool
|
||||
@@ -102,6 +113,11 @@ func Defaults() Config {
|
||||
ComfyIdleTimeout: 5 * time.Minute,
|
||||
ComfyStartTimeout: 2 * time.Minute,
|
||||
|
||||
// ComfyUI runs under python; excluding it (and Ollama) by name keeps
|
||||
// our own consumers from tripping the foreign-VRAM check.
|
||||
GPUIgnoreProcs: []string{"ollama", "ollama app", "ollama_llama_server", "python", "pythonw"},
|
||||
GamePollInterval: 5 * time.Second,
|
||||
|
||||
LogLevel: slog.LevelWarn,
|
||||
}
|
||||
}
|
||||
@@ -137,6 +153,17 @@ func ParseEnvFile(r io.Reader) (map[string]string, error) {
|
||||
return values, scanner.Err()
|
||||
}
|
||||
|
||||
// splitList parses a comma-separated setting into trimmed, non-empty items.
|
||||
func splitList(v string) []string {
|
||||
var out []string
|
||||
for _, item := range strings.Split(v, ",") {
|
||||
if item = strings.TrimSpace(item); item != "" {
|
||||
out = append(out, item)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func envDuration(getenv func(string) string, name string, dst *time.Duration) error {
|
||||
v := getenv(name)
|
||||
if v == "" {
|
||||
@@ -192,11 +219,25 @@ func Load(getenv func(string) string) (Config, error) {
|
||||
{"COMFY_IDLE_TIMEOUT", &cfg.ComfyIdleTimeout},
|
||||
{"COMFY_START_TIMEOUT", &cfg.ComfyStartTimeout},
|
||||
{"UPDATE_INTERVAL", &cfg.UpdateInterval},
|
||||
{"GAME_POLL_INTERVAL", &cfg.GamePollInterval},
|
||||
} {
|
||||
if err := envDuration(getenv, e.name, e.dst); err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
}
|
||||
if v := getenv("GAME_PROCS"); v != "" {
|
||||
cfg.GameProcs = splitList(v)
|
||||
}
|
||||
if v := getenv("GPU_IGNORE_PROCS"); v != "" {
|
||||
cfg.GPUIgnoreProcs = splitList(v)
|
||||
}
|
||||
if v := getenv("GPU_FOREIGN_VRAM_MB"); v != "" {
|
||||
n, err := strconv.Atoi(v)
|
||||
if err != nil || n < 0 {
|
||||
return cfg, fmt.Errorf("GPU_FOREIGN_VRAM_MB: must be a non-negative integer (MiB, 0 = disabled)")
|
||||
}
|
||||
cfg.GPUForeignVRAMMB = n
|
||||
}
|
||||
if v := getenv("PROMPT_CAPTURE_LIMIT"); v != "" {
|
||||
n, err := strconv.ParseInt(v, 10, 64)
|
||||
if err != nil || n < 0 {
|
||||
|
||||
@@ -157,6 +157,8 @@ func TestLoadErrors(t *testing.T) {
|
||||
{"LLM_BUSY_MODE", "bogus"},
|
||||
{"LLM_BUSY_STATUS", "200"},
|
||||
{"BUSY_RETRY_AFTER", "0"},
|
||||
{"GPU_FOREIGN_VRAM_MB", "-1"},
|
||||
{"GAME_POLL_INTERVAL", "bogus"},
|
||||
} {
|
||||
_, err := Load(func(k string) string {
|
||||
if k == tc.key {
|
||||
@@ -172,3 +174,51 @@ func TestLoadErrors(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGameDetectionSettings(t *testing.T) {
|
||||
cfg, err := Load(func(k string) string {
|
||||
switch k {
|
||||
case "OLLAMA_URL":
|
||||
return "http://127.0.0.1:11435"
|
||||
case "GAME_PROCS":
|
||||
return " cyberpunk2077.exe, hl2.exe ,, "
|
||||
case "GPU_FOREIGN_VRAM_MB":
|
||||
return "1024"
|
||||
case "GPU_IGNORE_PROCS":
|
||||
return "ollama, my-trainer"
|
||||
}
|
||||
return ""
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(cfg.GameProcs) != 2 || cfg.GameProcs[0] != "cyberpunk2077.exe" || cfg.GameProcs[1] != "hl2.exe" {
|
||||
t.Fatalf("GameProcs = %v", cfg.GameProcs)
|
||||
}
|
||||
if cfg.GPUForeignVRAMMB != 1024 {
|
||||
t.Fatalf("GPUForeignVRAMMB = %d", cfg.GPUForeignVRAMMB)
|
||||
}
|
||||
if len(cfg.GPUIgnoreProcs) != 2 || cfg.GPUIgnoreProcs[1] != "my-trainer" {
|
||||
t.Fatalf("GPUIgnoreProcs = %v", cfg.GPUIgnoreProcs)
|
||||
}
|
||||
if cfg.GamePollInterval != 5*time.Second {
|
||||
t.Fatalf("GamePollInterval = %v, want 5s default", cfg.GamePollInterval)
|
||||
}
|
||||
|
||||
// Defaults: both detection paths off, ignore list covers our consumers.
|
||||
cfg, err = Load(func(k string) string {
|
||||
if k == "OLLAMA_URL" {
|
||||
return "http://127.0.0.1:11435"
|
||||
}
|
||||
return ""
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(cfg.GameProcs) != 0 || cfg.GPUForeignVRAMMB != 0 {
|
||||
t.Fatalf("detection must be off by default: %v %d", cfg.GameProcs, cfg.GPUForeignVRAMMB)
|
||||
}
|
||||
if len(cfg.GPUIgnoreProcs) == 0 {
|
||||
t.Fatal("GPUIgnoreProcs default must not be empty")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,10 @@ func sampleEntries(logFile string) []sampleEntry {
|
||||
{"COMFY_DIR", `C:\ComfyUI`, "Working directory for COMFY_CMD (default: empty = inherit)", false},
|
||||
{"COMFY_IDLE_TIMEOUT", "5m", "Stop the managed ComfyUI after this long without requests or jobs (frees VRAM)", false},
|
||||
{"COMFY_START_TIMEOUT", "2m", "How long a request waits for the managed ComfyUI to come up", false},
|
||||
{"GAME_PROCS", "cyberpunk2077.exe,hl2.exe", "While a listed process runs, the GPU counts as held by it: requests wait, Ollama unloads, managed ComfyUI stops (default: empty = disabled)", false},
|
||||
{"GPU_FOREIGN_VRAM_MB", "1024", "Also treat the GPU as held when a process not in GPU_IGNORE_PROCS uses more VRAM than this (needs nvidia-smi; 0/empty = disabled)", false},
|
||||
{"GPU_IGNORE_PROCS", "ollama,ollama app,ollama_llama_server,python,pythonw", "Process names never counted as foreign GPU users (ComfyUI runs under python)", false},
|
||||
{"GAME_POLL_INTERVAL", "5s", "How often game/VRAM detection runs", false},
|
||||
{"UNLOAD_TIMEOUT", "60s", "How long to wait for Ollama to unload a model", false},
|
||||
{"JOB_TIMEOUT", "15m", "Maximum time to wait for a ComfyUI job", false},
|
||||
{"LLM_WAIT_TIMEOUT", "10m", "Max time an LLM request waits for the GPU before being answered 503 (wait mode)", false},
|
||||
|
||||
Reference in New Issue
Block a user