Bound native model loading memory

This commit is contained in:
firestar5683
2026-10-23 18:09:38 -05:00
parent 748e7b13fc
commit 0eec2e2a55
3 changed files with 179 additions and 13 deletions
@@ -247,7 +247,6 @@ def _verified(path):
def _load_driving(stream, unchanged):
import io
import mmap
import pickle
import struct
from tinygrad import Device
@@ -262,21 +261,25 @@ def _load_driving(stream, unchanged):
if stream.tell() == os.fstat(stream.fileno()).st_size:
unchanged()
return pickle.loads(opcodes)
# Map the already authenticated inode. Do not reopen a path or use a cached
# DISK:/proc/self/fd device whose descriptor number can be reused later.
mapping = mmap.mmap(stream.fileno(), 0, access=mmap.ACCESS_COPY)
view = memoryview(mapping)[stream.tell():]
buffers = Buffer(HCQ_RUNTIME_DEV.device, len(view), opaque=view)
arena, chunk_size = Buffer(Device.DEFAULT, buffers.nbytes, preallocate=True), 8 << 20
# Copy from the authenticated descriptor with bounded host staging. Reserving
# a private mapping for every weight can fail even when GPU memory is free.
length = os.fstat(stream.fileno()).st_size - stream.tell()
arena = Buffer(Device.DEFAULT, length, preallocate=True)
chunk_size = min(length, 8 << 20)
staging = bytearray(chunk_size)
view = memoryview(staging)
buffers = Buffer(HCQ_RUNTIME_DEV.device, chunk_size, opaque=view)
unchanged()
for off in range(0, buffers.nbytes, chunk_size):
size = min(chunk_size, buffers.nbytes - off)
arena.view(size, off).ensure_allocated().copy_from(buffers.view(size, off).ensure_allocated())
Device.default.synchronize()
for off in range(0, length, chunk_size):
size = min(chunk_size, length - off)
if stream.readinto(view[:size]) != size:
raise ValueError('Truncated native OOB payload')
arena.view(size, off).ensure_allocated().copy_from(buffers.view(size, 0).ensure_allocated())
# A staging window must remain unchanged until its transfer completes.
Device.default.synchronize()
unchanged()
del buffers
view.release()
mapping.close()
unpickler = pickle.Unpickler(io.BytesIO(opcodes))
cast("PersistentLoad", unpickler).persistent_load = lambda record: arena.view(*record)
return unpickler.load()
@@ -0,0 +1,163 @@
"""Exact native reader AST with inert GPU buffers and a real authenticated fd."""
import ast
from contextlib import contextmanager
import hashlib
import os
from pathlib import Path
import pickle
import stat
import struct
import tempfile
from types import ModuleType, SimpleNamespace
from typing import Any, cast
import unittest
from unittest.mock import Mock, patch
SOURCE = Path(os.environ.get('STARPILOT_NATIVE_READER_SOURCE', Path(__file__).resolve().parents[1] / 'runtime_bridge/native.py'))
CHUNK = 8 << 20
class Stream:
def __init__(self, stream, events, pending, truncate=False):
self.stream, self.events, self.pending, self.truncate = stream, events, pending, truncate
def fileno(self):
return self.stream.fileno()
def tell(self):
return self.stream.tell()
def read(self, size):
self.events.append(('read', size))
return self.stream.read(size)
def readinto(self, view):
if self.pending:
raise AssertionError('staging reused before synchronization')
self.events.append(('readinto', len(view)))
count = self.stream.readinto(view)
return count - 1 if self.truncate else count
class TestNativeDrivingReader(unittest.TestCase):
def setUp(self):
self.temp = self.enterContext(tempfile.TemporaryDirectory())
self.path = Path(self.temp) / 'artifact.pkl'
self.events: list[Any] = []
self.pending: list[Any] = []
self.arenas: list[bytearray] = []
self.stages: list[memoryview] = []
self.allocations: list[Any] = []
tree = ast.parse(SOURCE.read_text())
nodes: list[ast.stmt] = [n for n in tree.body if isinstance(n, ast.FunctionDef) and n.name in ('_verified', '_load_driving')]
self.ns: dict[str, Any] = {"contextmanager": contextmanager, "os": os, "stat": stat, "hashlib": hashlib,
"Path": Path, "cast": cast, "_allowed": {}, "__name__": __name__}
exec(compile(ast.Module(body=nodes, type_ignores=[]), str(SOURCE), 'exec'), self.ns)
owner = self
class Buffer:
def __init__(self, device, size, preallocate=False, opaque=None):
owner.allocations.append((device, size, preallocate, opaque is not None))
self.device, self.offset, self.size = device, 0, size
self.data = opaque if opaque is not None else bytearray(size)
if opaque is not None:
owner.stages.append(opaque)
else:
owner.arenas.append(self.data)
def view(self, size, offset):
if size < 0 or offset < 0 or offset + size > self.size:
raise ValueError('view outside arena')
return View(self.data, self.offset + offset, size)
def __del__(self):
if self.device == 'HOST':
owner.events.append('staging-buffer-released')
class View:
def __init__(self, data, offset, size):
self.data, self.offset, self.size = data, offset, size
def ensure_allocated(self):
return self
def copy_from(self, source):
owner.events.append(('copy', self.offset, self.size, source.offset))
owner.pending.append((self, source))
def synchronize():
for target, source in self.pending:
target.data[target.offset:target.offset + target.size] = source.data[source.offset:source.offset + source.size]
self.pending.clear()
self.events.append('sync')
tg = ModuleType('tinygrad')
tg.Device = SimpleNamespace(DEFAULT='GPU', default=SimpleNamespace(synchronize=synchronize))
device = ModuleType('tinygrad.device')
device.Buffer, device.HCQ_RUNTIME_DEV = Buffer, SimpleNamespace(device='HOST')
self.enterContext(patch.dict('sys.modules', {'tinygrad': tg, 'tinygrad.device': device}))
def envelope(self, payload, opcodes=b'actual opaque opcodes'):
data = struct.pack('<q', len(opcodes)) + opcodes + payload
self.path.write_bytes(data)
self.ns['_allowed'][str(self.path)] = {'artifact_size': len(data), 'artifact_sha256': hashlib.sha256(data).hexdigest()}
return opcodes
def test_exact_chunked_bytes_offsets_alias_tail_sync_and_release(self):
payload = bytes(range(251)) * ((2 * CHUNK + 173) // 251 + 1)
payload = payload[:2 * CHUNK + 173]
opcodes = self.envelope(payload)
owner = self
class Unpickler:
persistent_load: Any
def __init__(self, stream):
owner.assertEqual(stream.read(), opcodes)
def load(self):
owner.events.append('unpickle')
owner.assertIn('staging-buffer-released', owner.events)
with owner.assertRaises(ValueError):
_ = owner.stages[0].nbytes
return (self.persistent_load((CHUNK + 9, CHUNK - 7)), self.persistent_load((173, 2 * CHUNK)))
with patch.object(pickle, 'Unpickler', Unpickler), self.ns['_verified'](self.path) as (stream, unchanged):
aliases = self.ns['_load_driving'](Stream(stream, self.events, self.pending), unchanged)
self.assertEqual(bytes(self.arenas[0]), payload)
self.assertEqual(self.allocations, [('GPU', len(payload), True, False), ('HOST', CHUNK, False, True)])
self.assertEqual([e for e in self.events if isinstance(e, tuple) and e[0] == 'read'], [('read', 8), ('read', len(opcodes))])
self.assertEqual([e for e in self.events if isinstance(e, tuple) and e[0] == 'readinto'], [('readinto', CHUNK), ('readinto', CHUNK), ('readinto', 173)])
self.assertEqual([e for e in self.events if isinstance(e, tuple) and e[0] == 'copy'],
[('copy', 0, CHUNK, 0), ('copy', CHUNK, CHUNK, 0), ('copy', 2 * CHUNK, 173, 0)])
self.assertEqual(self.events.count('sync'), 3)
for alias, offset, size in ((aliases[0], CHUNK - 7, CHUNK + 9), (aliases[1], 2 * CHUNK, 173)):
self.assertIs(alias.data, self.arenas[0])
self.assertEqual(bytes(alias.data[offset:offset + size]), payload[offset:offset + size])
self.assertEqual(self.events[-1], 'unpickle')
def test_zero_oob_uses_real_pickle_loads_without_buffer_allocation(self):
expected = {'metadata': ('unchanged', 7)}
self.envelope(b'', pickle.dumps(expected))
unchanged = Mock()
with self.path.open('rb') as stream:
self.assertEqual(self.ns['_load_driving'](Stream(stream, self.events, self.pending), unchanged), expected)
unchanged.assert_called_once_with()
self.assertEqual(self.allocations, [])
def test_truncated_payload_readinto_fails_before_deserialization(self):
self.envelope(b'payload')
with self.path.open('rb') as stream, patch.object(pickle, 'Unpickler', side_effect=AssertionError('must not deserialize')):
with self.assertRaisesRegex(ValueError, 'Truncated native OOB payload'):
self.ns['_load_driving'](Stream(stream, self.events, self.pending, truncate=True), lambda: None)
def test_unchanged_exception_propagates_before_transfer(self):
self.envelope(b'payload')
failure = RuntimeError('owned unchanged failure')
with self.path.open('rb') as stream:
with self.assertRaises(RuntimeError) as raised:
self.ns['_load_driving'](stream, Mock(side_effect=failure))
self.assertIs(raised.exception, failure)
self.assertEqual(self.pending, [])
def test_authenticated_descriptor_mutation_is_rejected_after_copy(self):
self.envelope(b'payload')
class MutatingStream(Stream):
def readinto(inner, view):
count = super().readinto(view)
with self.path.open('ab') as target:
target.write(b'mutated')
return count
with self.assertRaisesRegex(ValueError, 'changed while loading'):
with self.ns['_verified'](self.path) as (stream, unchanged):
self.ns['_load_driving'](MutatingStream(stream, self.events, self.pending), unchanged)
if __name__ == '__main__':
unittest.main()
+1 -1
View File
@@ -1 +1 @@
{"protected_inventory_sha256": "fa4314474b7db3e6ffae318ed89afaaf1a14ae9044f3410e7c190ec09c2e7a36", "schema_version": 1}
{"protected_inventory_sha256": "893fd0c69058e471f0fc1a905342ab833412dcbd460bb2222a2d71f085506295", "schema_version": 1}