"""One bounded decision, two fabrics, two attributions. CPU: the kernel's per-process counter — Coverage::Whole for CPU work, Attribution::Process, declared period 10 ms. GPU: IOReport's `GPU Energy` rail — a real reading of the whole GPU that cannot tell one process from another, so Attribution::Machine, bracketed by idle baselines. """ import ctypes, os, time, json, statistics, warnings warnings.filterwarnings("ignore") exec(open('rails.py').read().split('import time, subprocess, sys')[0]) # IOReport _l = ctypes.CDLL("/usr/lib/libSystem.B.dylib"); _b = ctypes.create_string_buffer(464); _pid = os.getpid() def cpu_nj(): _l.proc_pid_rusage(ctypes.c_int(_pid), ctypes.c_int(6), ctypes.byref(_b)) return int.from_bytes(_b.raw[336:344], "little") def gpu_nj(s0, s1): d = IOR.IOReportCreateSamplesDelta(s0, s1, None); t = 0 for g, sg, name, unit, val in rails(d): if name == "GPU Energy": assert unit == "nJ"; t += val return t PERIOD_NS = 10_000_000 # the measured, declared period of both counters STATE = ("Guest in room 1408 says the AC has been out since yesterday and they want to " "move tonight or leave. They also asked for the incidentals hold to be released.") SCHEMA = { "intent": ["maintenance", "room_change", "checkout", "billing", "complaint", "amenity_request"], "priority": ["low", "normal", "high", "urgent"], "needs_human": ["yes", "no"], } N = 60 def idle(secs): s0 = sample(); c0 = cpu_nj(); t = time.perf_counter(); time.sleep(secs) s1 = sample(); c1 = cpu_nj(); dt = time.perf_counter() - t return gpu_nj(s0, s1) / dt, (c1 - c0) / dt # ─── CPU: GLiNER2.5-Decide, 340M, Apache-2.0 ──────────────────────────────── import torch torch.set_num_threads(os.cpu_count() or 8) from gliner2 import AutoExtractor t0 = time.perf_counter() m = AutoExtractor.from_pretrained("fastino/GLiNER2.5-Decide") load_s = time.perf_counter() - t0 try: m.model.to("cpu") except Exception: pass for _ in range(3): m.classify_text(STATE, SCHEMA) pre_g, pre_c = idle(1.0) s0 = sample(); c0 = cpu_nj(); t0 = time.perf_counter(); lat = [] for _ in range(N): t = time.perf_counter(); out = m.classify_text(STATE, SCHEMA); lat.append((time.perf_counter()-t)*1e3) span = time.perf_counter() - t0 s1 = sample(); c1 = cpu_nj() post_g, post_c = idle(1.0) cpu_j = (c1 - c0) / 1e9 gpu_leak = (gpu_nj(s0, s1) - (pre_g + post_g) / 2 * span) / 1e9 gliner = { "model": "fastino/GLiNER2.5-Decide (340M, Apache-2.0, DeBERTa-v3-large)", "fabric": "cpu", "attribution": "Process", "coverage": "Whole (CPU work)", "load_s": round(load_s, 2), "decisions": N, "span_s": round(span, 3), "p50_ms": round(statistics.median(lat), 2), "p95_ms": round(sorted(lat)[int(0.95*N)-1], 2), "cpu_J_per_decision": round(cpu_j / N, 6), "gpu_J_per_decision_leak": round(gpu_leak / N, 6), "span_gradeable": span * 1e9 >= PERIOD_NS, "answer": out, } print(json.dumps(gliner, indent=2, default=str))