cazyundee commited on
Commit
f0a025b
·
verified ·
1 Parent(s): eed5f09

Read CPU/RAM from cgroup limits instead of host

Browse files
Files changed (1) hide show
  1. app.py +80 -13
app.py CHANGED
@@ -39,7 +39,79 @@ def _log(message):
39
  print(message.encode("ascii", "backslashreplace").decode("ascii"), flush=True)
40
 
41
 
42
- def _get_ram_bytes():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  try:
44
  with open("/proc/meminfo", "r", encoding="utf-8") as meminfo:
45
  for line in meminfo:
@@ -91,20 +163,20 @@ API_SPECS = {
91
 
92
  def _get_runtime_specs():
93
  storage = shutil.disk_usage(os.getcwd())
94
- host_ram = _get_ram_bytes()
95
- host_cores = os.cpu_count()
96
-
97
- # ZeroGPU shares a large host machine. /proc/meminfo and
98
- # os.cpu_count() report the *full host*, not the Space's slice.
99
- # The actual allocation depends on ZeroGPU scheduling.
100
  hardware = os.environ.get("SPACE_HARDWARE", "zero-a10g")
101
  is_zerogpu = "zero" in hardware.lower()
102
 
 
 
 
 
 
103
  return {
104
  "platform": platform.platform(),
105
  "python_version": platform.python_version(),
 
106
  "host_cpu_cores": host_cores,
107
- "host_ram_bytes": host_ram,
108
  "storage_total_bytes": storage.total,
109
  "storage_used_bytes": storage.used,
110
  "storage_free_bytes": storage.free,
@@ -114,11 +186,6 @@ def _get_runtime_specs():
114
  "vram_bytes": 48 * 1024**3 if is_zerogpu else None,
115
  "shared_zero_gpu": is_zerogpu,
116
  } if is_zerogpu else None,
117
- "note": (
118
- "Host machine resources shown; Space gets a shared slice "
119
- "via ZeroGPU dynamic allocation."
120
- if is_zerogpu else None
121
- ),
122
  }
123
 
124
 
 
39
  print(message.encode("ascii", "backslashreplace").decode("ascii"), flush=True)
40
 
41
 
42
+ def _read_cgroup_file(path):
43
+ try:
44
+ with open(path, "r", encoding="utf-8") as f:
45
+ return f.read().strip()
46
+ except (FileNotFoundError, OSError):
47
+ return None
48
+
49
+
50
+ def _parse_cpu_count():
51
+ """Read actual CPU allocation from cgroup limits."""
52
+ # cgroup v2: /sys/fs/cgroup/cpu.max -> "quota period" or "max period"
53
+ cpu_max = _read_cgroup_file("/sys/fs/cgroup/cpu.max")
54
+ if cpu_max:
55
+ parts = cpu_max.split()
56
+ if len(parts) == 2 and parts[0] != "max":
57
+ try:
58
+ quota, period = int(parts[0]), int(parts[1])
59
+ if period > 0:
60
+ return quota // period
61
+ except ValueError:
62
+ pass
63
+
64
+ # cgroup v1: /sys/fs/cgroup/cpu/cpu.cfs_quota_us and cpu.cfs_period_us
65
+ quota = _read_cgroup_file("/sys/fs/cgroup/cpu/cpu.cfs_quota_us")
66
+ period = _read_cgroup_file("/sys/fs/cgroup/cpu/cpu.cfs_period_us")
67
+ if quota and period:
68
+ try:
69
+ q, p = int(quota), int(period)
70
+ if q > 0 and p > 0:
71
+ return q // p
72
+ except ValueError:
73
+ pass
74
+
75
+ # cpuset: /sys/fs/cgroup/cpuset.cpus -> "0-3" or "0,1,2,3"
76
+ cpuset = _read_cgroup_file("/sys/fs/cgroup/cpuset.cpus")
77
+ if cpuset:
78
+ count = 0
79
+ for part in cpuset.split(","):
80
+ if "-" in part:
81
+ start, end = part.split("-", 1)
82
+ count += int(end) - int(start) + 1
83
+ else:
84
+ count += 1
85
+ if count > 0:
86
+ return count
87
+
88
+ return os.cpu_count()
89
+
90
+
91
+ def _parse_ram_bytes():
92
+ """Read actual memory limit from cgroup limits."""
93
+ # cgroup v2: /sys/fs/cgroup/memory.max
94
+ mem_max = _read_cgroup_file("/sys/fs/cgroup/memory.max")
95
+ if mem_max and mem_max != "max":
96
+ try:
97
+ val = int(mem_max)
98
+ # cgroup v2 reports in bytes
99
+ if val < 2**62: # sanity check: not unlimited
100
+ return val
101
+ except ValueError:
102
+ pass
103
+
104
+ # cgroup v1: /sys/fs/cgroup/memory/memory.limit_in_bytes
105
+ mem_limit = _read_cgroup_file("/sys/fs/cgroup/memory/memory.limit_in_bytes")
106
+ if mem_limit:
107
+ try:
108
+ val = int(mem_limit)
109
+ if val < 2**62: # sanity check: not unlimited (reported as huge number)
110
+ return val
111
+ except ValueError:
112
+ pass
113
+
114
+ # Fallback to /proc/meminfo
115
  try:
116
  with open("/proc/meminfo", "r", encoding="utf-8") as meminfo:
117
  for line in meminfo:
 
163
 
164
  def _get_runtime_specs():
165
  storage = shutil.disk_usage(os.getcwd())
 
 
 
 
 
 
166
  hardware = os.environ.get("SPACE_HARDWARE", "zero-a10g")
167
  is_zerogpu = "zero" in hardware.lower()
168
 
169
+ # Read actual container limits from cgroup, falling back to host values
170
+ allocated_cores = _parse_cpu_count()
171
+ allocated_ram = _parse_ram_bytes()
172
+ host_cores = os.cpu_count()
173
+
174
  return {
175
  "platform": platform.platform(),
176
  "python_version": platform.python_version(),
177
+ "cpu_cores": allocated_cores,
178
  "host_cpu_cores": host_cores,
179
+ "ram_bytes": allocated_ram,
180
  "storage_total_bytes": storage.total,
181
  "storage_used_bytes": storage.used,
182
  "storage_free_bytes": storage.free,
 
186
  "vram_bytes": 48 * 1024**3 if is_zerogpu else None,
187
  "shared_zero_gpu": is_zerogpu,
188
  } if is_zerogpu else None,
 
 
 
 
 
189
  }
190
 
191