Spaces:
Running on Zero
Running on Zero
File size: 16,042 Bytes
8947dc2 | 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 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 | """Maintained Phase60 CUDA pipeline for bulk Arrow screening.
Imports are CUDA-lazy; rich/list operations use cpu_engine.
"""
from __future__ import annotations
import hashlib
import importlib
import importlib.util
import json
from pathlib import Path
import sys
import threading
import weakref
_ENGINES = weakref.WeakSet()
_REGISTRY_LOCK = threading.Lock()
_ALLOCATION_LOCK = threading.RLock()
GPU_LIMIT = 2 * 1024**3
PINNED_LIMIT = 256 * 1024**2
NATIVE_SHA256 = "8b1dddd69ccc13380cced1f2df4977f2ecb750805c9d127b0c8046fef812563f"
CUBIN_SHA256 = "dd943d9adac702e41c2bb0a2b58f78698498edd8a3dfab16cf9d224c8c6a83fa"
def _sha(path):
digest = hashlib.sha256()
with path.open("rb") as handle:
for chunk in iter(lambda: handle.read(1024**2), b""):
digest.update(chunk)
return digest.hexdigest()
def _member(root, name):
if not isinstance(name, str) or "\\" in name or ":" in name:
raise ValueError("invalid installed resource path")
parts = name.split("/")
if not parts or any(part in ("", ".", "..") for part in parts):
raise ValueError("invalid installed resource path")
path = root.joinpath(*parts)
if path.is_symlink() or not path.resolve().is_relative_to(root.resolve()):
raise ValueError("installed resource escapes its package")
return path
def _verify_files(root, files):
for name, value in files.items():
path = _member(root, name)
if not path.is_file() or path.stat().st_size != value["bytes"] or _sha(path) != value["sha256"]:
raise RuntimeError("installed package resource changed: " + name)
def _manifest(root):
path = root / "runtime-manifest.json"
if path.stat().st_size > 1024**2:
raise RuntimeError("installed runtime manifest exceeds its bound")
value = json.loads(path.read_text(encoding="utf-8"))
if (value.get("schema") != "range.cuda.installed-runtime.v1"
or value.get("selected_phase") not in (59, 60)
or value.get("native", {}).get("sha256") != NATIVE_SHA256
or value.get("cubin", {}).get("sha256") != CUBIN_SHA256
or value.get("python_tag") != "cp310-cp310-win_amd64"
or value.get("cuda_target") != "sm_75"):
raise RuntimeError("installed companion manifest is incompatible")
_verify_files(root, value["files"])
return value
def _cpu_api(root, manifest):
expected = root.parent / "range_curate"
spec = importlib.util.find_spec("range_curate")
if spec is None or not spec.origin or Path(spec.origin).resolve() != (expected / "__init__.py").resolve():
raise RuntimeError("install range-curate beside range-curate-cuda")
_verify_files(expected, manifest["cpu_api_files"])
module = importlib.import_module("range_curate.examples.general_economy")
if module.__version__ != manifest["cpu_version"]:
raise RuntimeError("CUDA companion requires its matching RANGE release")
return module
def _workers(value):
if isinstance(value, bool) or not isinstance(value, int) or value < 1:
raise ValueError("workers must be a positive integer")
return value
def _validate_arrow(data, row_count, text_column, timestamp_column):
import pyarrow as pa
if not isinstance(data, (pa.Table, pa.RecordBatch)):
raise TypeError("CUDA screening accepts a materialized Arrow Table or RecordBatch")
if row_count is not None and (isinstance(row_count, bool) or not isinstance(row_count, int) or row_count < 0):
raise ValueError("row_count must be a nonnegative integer or None")
if row_count is not None and row_count != data.num_rows:
raise ValueError("row_count differs from Arrow input")
for name in (text_column, timestamp_column):
if data.schema.get_field_index(name) < 0:
raise ValueError("missing or ambiguous Arrow column: " + name)
if not (pa.types.is_string(data.schema.field(text_column).type)
or pa.types.is_large_string(data.schema.field(text_column).type)):
raise TypeError("text must be Arrow string or large_string")
timestamp = data.schema.field(timestamp_column).type
if not pa.types.is_timestamp(timestamp) or timestamp.tz is None:
raise ValueError("timestamp must be a timezone-aware Arrow timestamp")
if data.column(text_column).null_count or data.column(timestamp_column).null_count:
raise ValueError("text and timestamp must not contain nulls")
class CudaRelevanceEngine:
"""Opt-in installed research engine for exact Arrow compact screening.
backend='cpu' explicitly uses the private block31 CPU engine. Unsupported
GPU taxonomies also use that exact engine. Missing CUDA and unexpected GPU
failures raise; no partial output or hidden runtime-error fallback is used.
Rich predictions and explanations remain CPU operations via cpu_engine.
"""
def __init__(self, taxonomy=None, workers=10, *, backend="cuda", resource_guard=None):
self.workers = _workers(workers)
if backend not in ("cuda", "cpu"):
raise ValueError("backend must be cuda or cpu")
if resource_guard is not None and not callable(resource_guard):
raise TypeError("resource_guard must be callable or None")
if sys.platform != "win32" or sys.version_info[:2] != (3, 10) or sys.maxsize <= 2**32:
raise RuntimeError("this research companion supports Windows CPython3.10 AMD64 only")
self._root = Path(__file__).resolve().parent
self._manifest = _manifest(self._root)
api = _cpu_api(self._root, self._manifest)
self._cpu_engine = api.RelevanceEngine(taxonomy=taxonomy, workers=self.workers)
# Keep the measured binary private. Never rebind range_curate._native or
# mutate the CPU backend registry; this engine owns its own scanners.
native = importlib.import_module("range_curate_cuda._private._native")
native_path = getattr(native, "__file__", None)
if not native_path or Path(native_path).resolve() != (self._root / self._manifest["native"]["member"]).resolve():
raise RuntimeError("the private native module was imported outside the installed companion")
from range_curate._core.accelerated.compact import _build_native_specs
indicator = self._cpu_engine._screening_indicator
specs = _build_native_specs(indicator)
indicator._list_scanner = native.NativeCompactFactScanner(*specs)
indicator._arrow_scanner = native.NativeArrowCompactFactScanner(*specs)
self._native = native
self._backend = backend
self._runtime = None
self._machine_ready = False
self._machine_lock = threading.Lock()
self._last_cpu_profile = {}
self._external_guard = resource_guard
with _REGISTRY_LOCK:
_ENGINES.add(self)
if backend == "cuda":
self._construct_runtime()
def _construct_runtime(self):
phase = self._manifest["selected_phase"]
name = "phase60_followup_gpu" if phase == 60 else "phase59_followup_gpu"
module = importlib.import_module("range_curate_cuda._runtime." + name)
for imported_name, imported in tuple(sys.modules.items()):
if imported_name == "range_curate_cuda._runtime" or imported_name.startswith("range_curate_cuda._runtime."):
filename = getattr(imported, "__file__", None)
if not filename or not Path(filename).resolve().is_relative_to((self._root / "_runtime").resolve()):
raise RuntimeError("a runtime module was imported outside the installed companion: " + imported_name)
scanner = self._cpu_engine._screening_indicator._arrow_scanner
def masked(data, mask, *, workers, text_column, timestamp_column):
return scanner.screen_arrow_masked(data, mask, text_column, timestamp_column, workers)
kwargs = {"masked_cpu": masked, "unicode_tables": self._root / "resources" / "unicode13",
"overlap_cpu": True, "diagnostics": False}
if phase == 60:
kind = module.ExactTwoSlotCallWideCudaClassifier
kwargs["resource_guard"] = self._resource_guard
else:
guard = self._resource_guard
class GuardedClassifier(module.ExactWarpPackedMatchingCudaClassifier):
def _guard_allocations(self, sizes):
sizes = tuple(int(size) for size in sizes)
super()._guard_allocations(sizes)
guard(self.cp, additional_device_bytes=sum((size + 511) // 512 * 512 for size in sizes),
additional_pinned_bytes=0)
kind = GuardedClassifier
self._runtime = kind(self._cpu_engine, self._root / self._manifest["cubin"]["member"], **kwargs)
def _resource_guard(self, cp, *, additional_device_bytes=0, additional_pinned_bytes=0):
if additional_device_bytes < 0 or additional_pinned_bytes < 0:
raise ValueError("allocation projection must be nonnegative")
with _REGISTRY_LOCK:
pinned = sum(engine._runtime._pinned_bytes() for engine in tuple(_ENGINES)
if engine._runtime is not None and hasattr(engine._runtime, "_pinned_bytes"))
if int(cp.get_default_memory_pool().total_bytes()) + additional_device_bytes > GPU_LIMIT:
raise MemoryError("companion device pool plus proposed allocations exceeds2GiB")
if pinned + additional_pinned_bytes > PINNED_LIMIT:
raise MemoryError("companion pinned owners plus proposed allocations exceed256MiB")
if self._external_guard is not None:
self._external_guard(cp, additional_device_bytes=additional_device_bytes,
additional_pinned_bytes=additional_pinned_bytes)
def _ensure_machine(self):
if self._runtime is None or self._machine_ready:
return
# Across companion instances admission and the complete fixed owner
# allocation are one serialized operation. Ordinary calls reuse those
# maximum arenas and never rely on an unlocked check-then-allocate gap.
with self._machine_lock, _ALLOCATION_LOCK:
if self._machine_ready:
return
runtime = self._runtime
if runtime._failed_drain or runtime._initialization_failed:
raise RuntimeError("CUDA initialization/drain previously failed; end this worker process")
try:
cp = importlib.import_module("cupy")
except ImportError as error:
raise RuntimeError("CUDA screening requires the separately installed compatible CuPy/CUDA runtime") from error
try:
capability = str(cp.cuda.Device().compute_capability)
except Exception as error:
raise RuntimeError("a usable NVIDIA CUDA device/runtime is required before CUDA screening") from error
if capability != "75":
raise RuntimeError("this bundled research cubin supports the qualified sm75 device only")
if runtime.unsupported_reason:
self._machine_ready = True
return
# Admit the complete maximum owner before any lazy config/workspace
# allocation. Existing live/cached pools and every public pinned
# owner remain charged. No credit is taken for cached reuse.
workspace = int(runtime.backend_report["workspace_requested_max_bytes"])
configs = sum((max(value.nbytes, value.dtype.itemsize) + 511) // 512 * 512
for value in runtime._arrays.values())
# At most34 workspace arrays across two slots, conservatively each
# charged one additional allocator quantum beyond logical bytes.
pinned = 70328320 if self._manifest["selected_phase"] == 60 else 0
self._resource_guard(cp, additional_device_bytes=workspace + configs + 34 * 512,
additional_pinned_bytes=pinned)
if not runtime._call_lock.acquire(blocking=False):
raise RuntimeError("the installed engine already has an active call")
try:
runtime._initialize()
runtime._reserve(runtime.batch_bytes, runtime.batch_rows)
self._machine_ready = True
finally:
runtime._call_lock.release()
def prepare(self):
"""Explicitly validate and allocate the fixed owner outside a call timer."""
self._ensure_machine()
return self.backend_report
def screen_arrow(self, data, *, text_column="text", timestamp_column="timestamp", workers=None, row_count=None):
workers = self.workers if workers is None else _workers(workers)
if self._runtime is None:
result = self._cpu_engine.screen_arrow(data, text_column=text_column, timestamp_column=timestamp_column,
workers=workers, row_count=row_count)
self._last_cpu_profile = {"backend": "cpu", "execution_path": "explicit_cpu", "rows": result.row_count}
return result
if self._runtime.unsupported_reason:
self._ensure_machine()
result = self._cpu_engine.screen_arrow(data, text_column=text_column, timestamp_column=timestamp_column,
workers=workers, row_count=row_count)
self._runtime.last_profile = {**self._runtime.backend_report, "backend": "cpu",
"execution_path": "unsupported_taxonomy_cpu", "callwide_applied": False,
"callwide_enabled": False, "rows": result.row_count,
"unsupported_taxonomy_reason": self._runtime.unsupported_reason}
return result
_validate_arrow(data, row_count, text_column, timestamp_column)
self._ensure_machine()
result = self._runtime.screen_arrow(data, text_column=text_column, timestamp_column=timestamp_column,
workers=workers, row_count=row_count)
from range_curate._core.accelerated.compact import ArrowCompactFacts
return ArrowCompactFacts(row_count=result.row_count, facts=result.facts)
@property
def cpu_engine(self):
"""CPU-only rich/list predictions and explanations for this taxonomy."""
return self._cpu_engine
@property
def runtime(self):
"""Read-only reference for maintainer ownership/parity qualification."""
return self._runtime
@property
def last_profile(self):
return self._last_cpu_profile if self._runtime is None else self._runtime.last_profile
@property
def backend_report(self):
report = {"backend": "cpu", "execution_path": "explicit_cpu"} if self._runtime is None else dict(self._runtime.backend_report)
if self._runtime is not None and self._runtime.unsupported_reason:
report.update(backend="cpu", execution_path="unsupported_taxonomy_cpu", callwide_applied=False,
unsupported_taxonomy_reason=self._runtime.unsupported_reason)
return {**report, "installed_runtime_phase": self._manifest["selected_phase"],
"distribution": self._manifest["distribution"], "version": self._manifest["version"],
"acceleration_scope": "bulk screen_arrow only; cpu_engine rich/list calls use CPU",
"native_sha256": NATIVE_SHA256, "bundled_cubin_sha256": CUBIN_SHA256,
"cuda_target": "sm_75", "local_dependency_environment_qualification_only": True}
__all__ = ["CudaRelevanceEngine"]
|