File size: 1,838 Bytes
5c331a4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
"""Admission control from live memory and CPU limits, not a fixed worker count."""
import math
import os
from dataclasses import dataclass
from pathlib import Path

import psutil

RESERVE = 0.10


@dataclass(frozen=True)
class Resources:
    ram_free: int
    vram_free: int
    cpu_capacity: float


def _read_limit(path: str) -> int | None:
    try:
        text = Path(path).read_text().strip()
        return None if text == "max" else int(text)
    except (OSError, ValueError):
        return None


def snapshot(vram_free: int) -> Resources:
    ram = psutil.virtual_memory().available
    limit = _read_limit("/sys/fs/cgroup/memory.max")
    used = _read_limit("/sys/fs/cgroup/memory.current")
    if limit is not None and used is not None:
        ram = min(ram, max(0, limit - used))
    cpus = float(len(os.sched_getaffinity(0)) if hasattr(os, "sched_getaffinity") else os.cpu_count() or 1)
    try:
        quota, period = Path("/sys/fs/cgroup/cpu.max").read_text().split()
        if quota != "max":
            cpus = min(cpus, int(quota) / int(period))
    except (OSError, ValueError):
        pass
    return Resources(ram, vram_free, cpus)


def parallelism(resources: Resources, ram_per_task: int, vram_per_task: int, pending: int) -> int:
    if pending < 1 or min(ram_per_task, vram_per_task) <= 0:
        raise ValueError("Pending work and measured memory costs must be positive.")
    ram_slots = int(resources.ram_free * (1 - RESERVE)) // ram_per_task
    gpu_slots = int(resources.vram_free * (1 - RESERVE)) // vram_per_task
    if min(ram_slots, gpu_slots) < 1:
        raise MemoryError("Even one texture task cannot fit with the 10% memory reserve; reduce tile size.")
    cpu_slots = max(1, math.floor(resources.cpu_capacity * (1 - RESERVE)))
    return max(1, min(pending, ram_slots, gpu_slots, cpu_slots))