Skip to content

Commit 8ff06f7

Browse files
Add full-volume fiber tracing and scalable chunk-cache scheduling (#2359)
* Fix fiber normal export, accumulator scheduling and compact pyramid execution * Preserve pyramid policies and legacy stage order in compact output * Default fiber volume inference to native network output resolution * Cache-block normal encoding without changing output bytes * Keep inference coordination nonblocking and use chunk-local mmap accumulation * Add Qualzz AFV inference with managed GPUs and paired probability Zarr arrays * Establish whole-volume chunk tracing and queued loading baseline * Add reusable approved fiber test and benchmark runner * Accelerate chunk ROI reads and reuse decoded buffers Reduce keyed-reader and queue wakeups, keep batch scheduling internal, reuse final-reader-released decode allocations within the shared budget, and use blocked ROI layout conversion. Add codec/ownership/ROI regressions and standalone multi-channel reader benchmarks with documented results. * Record matched tracing throughput after decoder acceleration * Preserve fast cache-backed fiber tracing and evaluated route policies Unify scalar-equivalent channel sampling over retained decoded chunks, with shared addressing and optional profiling. Include accumulated tracing eligibility, reverse convergence, chunk ownership and export changes with regression coverage and benchmark documentation. Planning and local experiment artifacts are excluded. * Extract reusable fiber volume coverage and collision policies * Add resumable native continuous volume fiber tracing * Prevent shared chunk-cache eviction stalls and stranded trace waiters * Record chosen beam-node observations in fiber trace commits * Persist extensible point and segment attributes in volume fibers * Register attributed fiber pilot and output audit workflows * Allow managed commands to accept partial GPU allocations
1 parent e1aa625 commit 8ff06f7

129 files changed

Lines changed: 16998 additions & 1712 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎gpu_manager/README.md‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,17 @@ grants without starting workers and retries registration until the requested
217217
allocation is available. This is opt-in; it can wait indefinitely and each new
218218
registration joins the FIFO queue anew.
219219

220+
For commands that can size their workers at startup, `accept_partial=True`
221+
instead accepts any nonempty granted GPU subset and positive CPU count within
222+
the request. Each admitted GPU must still have its requested VRAM budget.
223+
This conflicts with `wait_for_exact`. The child receives only admitted GPUs in
224+
`CUDA_VISIBLE_DEVICES` (in requested order), and the actual allocation as JSON
225+
in `VILLA_GPU_MANAGER_GRANT` (`cpu_cores`, `gpus` mapping UUIDs to bytes).
226+
The child must size CPU/GPU workers to this allocation. It remains fixed for
227+
the cohort lifetime; this is admission flexibility, not live worker resizing.
228+
Ownership checks and join-before-release cleanup remain unchanged. The default
229+
continues to require the complete requested allocation.
230+
220231
`ProcessOwner` provides a separate ownership root for sibling cohorts managed
221232
by one coordinator. Pass its identity as `Client(registration_root=...)`,
222233
track each worker session before authorizing it, and retire it only after joining

‎gpu_manager/src/villa_gpu_manager/exclusive.py‎

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ class ExclusiveTask:
201201
"""
202202
def __init__(self, path, grant, *, label, priority=0, poll_seconds=.1,
203203
client_factory=Client, stop_event=None, cleanup_timeout=30.0, exclusive=True,
204-
wait_for_exact=False, exact_retry_seconds=5.0):
204+
wait_for_exact=False, exact_retry_seconds=5.0, accept_partial=False):
205205
if type(exclusive) is not bool:
206206
raise ValueError("exclusive must be boolean")
207207
if type(priority) is not int or poll_seconds <= 0 or cleanup_timeout <= 0:
@@ -211,6 +211,7 @@ def __init__(self, path, grant, *, label, priority=0, poll_seconds=.1,
211211
raise ValueError("exclusive task requires positive GPU budgets and CPUs")
212212
sizes = list(grant["gpus"].values())
213213
self.grant = deepcopy(grant)
214+
self.requested_grant = deepcopy(grant)
214215
# The locked scheduler scales CPU promises with the sum of GPU bytes.
215216
# Compensate for heterogeneous full-device capacities so the resulting
216217
# CPU grant is exact too (ceil in cores_for cancels this floor).
@@ -225,6 +226,9 @@ def __init__(self, path, grant, *, label, priority=0, poll_seconds=.1,
225226
if type(wait_for_exact) is not bool or not math.isfinite(exact_retry_seconds) or exact_retry_seconds <= 0:
226227
raise ValueError("boolean exact waiting and positive retry interval required")
227228
self.wait_for_exact, self.exact_retry_seconds = wait_for_exact, exact_retry_seconds
229+
if type(accept_partial) is not bool or (accept_partial and wait_for_exact):
230+
raise ValueError("accept_partial must be boolean and conflicts with wait_for_exact")
231+
self.accept_partial = accept_partial
228232
self.cohort = None
229233
self.epoch = self.generation = None
230234
self.closed = False
@@ -273,7 +277,17 @@ def __enter__(self):
273277
last = status
274278
time.sleep(self.poll_seconds)
275279
task = self.client.poll()
276-
if task["granted"] != self.grant:
280+
if self.accept_partial:
281+
actual = task["granted"]
282+
requested = self.requested_grant
283+
if (type(actual["cpu_cores"]) is not int or
284+
not 0 < actual["cpu_cores"] <= requested["cpu_cores"] or
285+
not actual["gpus"] or
286+
any(gpu not in requested["gpus"] or size != requested["gpus"][gpu]
287+
for gpu, size in actual["gpus"].items())):
288+
raise RuntimeError("invalid partial grant: requires positive bounded CPUs and requested GPU memory")
289+
self.grant = deepcopy(actual)
290+
elif task["granted"] != self.grant:
277291
raise RuntimeError("manager granted only part of the requested CPU/devices/VRAM; not launching")
278292
self.epoch, self.generation = self.client.epoch, task["generation"]
279293
print(f"gpu-manager task={self.client.task_id} exclusive={self.request['exclusive']} grant={self.grant}", flush=True)
@@ -358,7 +372,7 @@ def run_exclusive(command, *, socket, gpu_uuids, priority=100, label="fiber-eval
358372
environment=None, poll_interval=1.0, stop_event=None,
359373
client_factory=Client, cleanup_timeout=30.0, before_authorize=None,
360374
exclusive=True, cpu_cores=None, vram_bytes_per_gpu=None,
361-
wait_for_exact=False, exact_retry_seconds=5.0):
375+
wait_for_exact=False, exact_retry_seconds=5.0, accept_partial=False):
362376
"""Gated command adapter around ExclusiveTask; no unmanaged fallback.
363377
364378
The caller may bootstrap the shared manager before calling. An independent
@@ -380,14 +394,16 @@ def run_exclusive(command, *, socket, gpu_uuids, priority=100, label="fiber-eval
380394
lease = ExclusiveTask(socket, grant, label=label, priority=priority,
381395
poll_seconds=poll_interval, stop_event=stop_event, client_factory=client_factory,
382396
cleanup_timeout=cleanup_timeout, exclusive=exclusive,
383-
wait_for_exact=wait_for_exact, exact_retry_seconds=exact_retry_seconds)
397+
wait_for_exact=wait_for_exact, exact_retry_seconds=exact_retry_seconds,
398+
accept_partial=accept_partial)
384399
outcome = "failed"
385400
try:
386401
lease.__enter__()
387402
if before_authorize is not None:
388403
before_authorize()
389404
env = dict(os.environ if environment is None else environment)
390-
env["CUDA_VISIBLE_DEVICES"] = ",".join(gpu_uuids)
405+
env["CUDA_VISIBLE_DEVICES"] = ",".join(gpu for gpu in gpu_uuids if gpu in lease.grant["gpus"])
406+
env["VILLA_GPU_MANAGER_GRANT"] = json.dumps(lease.grant, sort_keys=True)
391407
lease.cohort = child = GatedCommand(command, env)
392408
lease.authorize()
393409
while child.process.poll() is None:

‎gpu_manager/tests/test_exclusive.py‎

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Synthetic GPU promises and real CPU-only gated subprocesses; no CUDA."""
22
import os
3+
import json
34
from pathlib import Path
45
import sys
56
import threading
@@ -89,6 +90,66 @@ def test_fixed_nonexclusive_command_shares_inventory(tmp_path):
8990
assert e.tasks[donor["task"]["task_id"]]["phase"] == "applied"
9091

9192

93+
def test_partial_admission_exports_actual_resources_and_joins_before_release(tmp_path):
94+
e = engine()
95+
def donor_identity(op, body):
96+
if op == "register":
97+
body["root"] = dict(pid=1, start_us=1, boot_us=1)
98+
donor = memory_client(e, donor_identity)("unused")
99+
donor.connect()
100+
donor.register("occupied", dict(max_cpu_cores=4, max_gpus=1,
101+
min_vram_bytes_per_gpu=100, max_vram_bytes_per_gpu=100,
102+
allowed_gpu_uuids=["GPU-b"], priority=0, exclusive=False),
103+
pausable=False, adaptable=False)
104+
donor.report("applied", donor.poll()["granted"])
105+
released=[]
106+
def hook(op, body):
107+
if op == "report" and body["phase"] == "released":
108+
task=e.tasks[body["task_id"]]
109+
assert all(process_state(p, os.getuid()) == "dead" for p in task["processes"][1:])
110+
released.append(True)
111+
marker=tmp_path / "actual.json"
112+
command=[sys.executable,"-c",
113+
"import os,json,pathlib,sys; pathlib.Path(sys.argv[1]).write_text(json.dumps(dict(visible=os.environ['CUDA_VISIBLE_DEVICES'],grant=json.loads(os.environ['VILLA_GPU_MANAGER_GRANT']))))",
114+
str(marker)]
115+
assert run_exclusive(command,socket="unused",gpu_uuids=["GPU-b","GPU-a"],
116+
client_factory=memory_client(e,hook),exclusive=False,cpu_cores=8,
117+
vram_bytes_per_gpu=40,accept_partial=True,poll_interval=.01) == 0
118+
result=json.loads(marker.read_text())
119+
assert result == {"visible":"GPU-a","grant":{"cpu_cores":4,"gpus":{"GPU-a":40}}}
120+
task=max(e.tasks.values(),key=lambda t:t["order"])
121+
assert task["terminal"] and task["granted"] == empty() and released
122+
assert not task["adaptable"] and not task["pausable"]
123+
assert donor.poll()["phase"] == "applied"
124+
125+
126+
@pytest.mark.parametrize("kind", ["empty", "foreign", "memory", "cpu_zero", "cpu_excess"])
127+
def test_invalid_partial_grants_do_not_launch(tmp_path,kind):
128+
e=engine();base=memory_client(e)
129+
class InvalidGrant(base):
130+
def register(self,*args,**kwargs):
131+
task=super().register(*args,**kwargs)
132+
if kind=="empty":task["granted"]["gpus"]={}
133+
elif kind=="foreign":task["granted"]["gpus"]={"GPU-foreign":40}
134+
elif kind=="memory":task["granted"]["gpus"]["GPU-a"]=39
135+
elif kind=="cpu_zero":task["granted"]["cpu_cores"]=0
136+
elif kind=="cpu_excess":task["granted"]["cpu_cores"]=9
137+
return task
138+
marker=tmp_path/"forbidden"
139+
with pytest.raises(RuntimeError,match="invalid partial grant"):
140+
run_exclusive(marker_command(marker),socket="unused",gpu_uuids=["GPU-a","GPU-b"],
141+
exclusive=False,cpu_cores=8,vram_bytes_per_gpu=40,accept_partial=True,
142+
client_factory=InvalidGrant,poll_interval=.01)
143+
assert not marker.exists()
144+
145+
146+
@pytest.mark.parametrize("partial,exact", [(True,True),(1,False)])
147+
def test_partial_admission_arguments(tmp_path,partial,exact):
148+
with pytest.raises(ValueError,match="accept_partial"):
149+
run_exclusive(marker_command(tmp_path/"forbidden"),socket="unused",gpu_uuids=["GPU-a"],
150+
client_factory=memory_client(engine()),accept_partial=partial,wait_for_exact=exact)
151+
152+
92153
def test_nonexclusive_requires_explicit_budgets(tmp_path):
93154
with pytest.raises(ValueError, match="explicit CPU"):
94155
run_exclusive(marker_command(tmp_path / "no"), socket="unused", gpu_uuids=["GPU-a"],

‎lasagna/README.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ modules; direct and managed inference do not require `PYTHONPATH`.
4848

4949
### Inference manager
5050

51+
For Qualzz AFV V2 models and plain V/H probability Zarr output, see
52+
[Qualzz AFV inference](docs/qualzz_afv_inference.md). This uses the same tiled
53+
engine and GPU manager without writing Lasagna-schema metadata.
54+
5155
`las_manager` provides configuration, discovery, prefetch, and durable tmux
5256
execution for managed Fiber and Lasagna inference. Initialize its XDG configuration, fill
5357
in `cache_dir`, `output_dir`, `venv`, and one or more `snapshot_dirs`, then:

0 commit comments

Comments
 (0)