From 37a54dc7cf764bbb9a74f6f72aa7345d481f0f87 Mon Sep 17 00:00:00 2001 From: George Hotz <72895+geohot@users.noreply.github.com> Date: Sun, 16 Aug 2026 23:39:37 -0700 Subject: [PATCH] add some dels to jit for OOM fixes (#17566) --- tinygrad/engine/jit.py | 4 ++++ tinygrad/uop/ops.py | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/tinygrad/engine/jit.py b/tinygrad/engine/jit.py index f812f0b9eb..a543623072 100644 --- a/tinygrad/engine/jit.py +++ b/tinygrad/engine/jit.py @@ -269,10 +269,14 @@ class _TinyJit(Generic[ReturnType]): big_linear, onetime_linear = prune_linear(big_linear, set(input_buf_uops)) if DEBUG >= 1: print(f"pruned from {len(big_linear.src) + len(onetime_linear.src)} -> {len(big_linear.src)} kernels") run_linear(onetime_linear, var_vals) + del onetime_linear # hold all buffers reachable from live Tensors (e.g. lazy .grad created during capture), the memory planner can't suballocate those held_bufs = set(buffers) | {u for tref in list(all_tensors) if (t:=tref()) is not None for u in t.uop.toposort() if u.op is Ops.BUFFER} linear = jit_lower(big_linear, held_bufs, input_buf_uops) + # drop the pre-planning graph: it keeps the whole capture-time working set allocated (big_linear) or referenced (held_bufs). + # the planned linear only uses the arena/held buffers, so the intermediates must be freed before linking and first exec + del big_linear, held_bufs self.captured = CapturedJit(ret, linear, names, expected_input_info) ret = self.captured(input_buf_uops, var_vals) elif self.cnt >= 2: diff --git a/tinygrad/uop/ops.py b/tinygrad/uop/ops.py index 63eabaa0b5..f1f641fe54 100644 --- a/tinygrad/uop/ops.py +++ b/tinygrad/uop/ops.py @@ -242,9 +242,10 @@ class UOp(RandMixin, metaclass=UOpMetaClass): arg:Any = None tag:Any = None def __del__(self): - if Ops is not None and self.op is Ops.BUFFER and (buffer:=buffers.get(self)) is not None: buffer.ref(-1) + # NOTE: getattr because this object may be partially constructed (e.g. if __init__ raised, like the BEAM timeout SIGALRM) + if Ops is not None and getattr(self, 'op', None) is Ops.BUFFER and (buffer:=buffers.get(self)) is not None: buffer.ref(-1) try: del UOpMetaClass.ucache[(self.op, self.dtype, self.src, self.arg, self.tag)] - except AttributeError: pass + except (AttributeError, KeyError): pass def __reduce__(self): args = [self.op, self.dtype, self.src, self.arg, self.tag, self.metadata] if self.op is Ops.BUFFER and self.realized is not None: args.append(self.realized)