diff --git a/test/test_jit.py b/test/test_jit.py index a97dd5a589..3a9e1f04d4 100644 --- a/test/test_jit.py +++ b/test/test_jit.py @@ -15,6 +15,25 @@ class TestJit(unittest.TestCase): c = add(a, b) np.testing.assert_equal(c.numpy(), a.numpy()+b.numpy()) + def test_jit_shape_mismatch(self): + @TinyJit + def add(a, b): return (a+b).realize() + for _ in range(3): + a = Tensor.randn(10, 10) + b = Tensor.randn(10, 10) + c = add(a, b) + bad = Tensor.randn(20, 20) + with self.assertRaises(AssertionError): + add(a, bad) + + def test_jit_duplicate_fail(self): + # the jit doesn't support duplicate arguments + @TinyJit + def add(a, b): return (a+b).realize() + a = Tensor.randn(10, 10) + with self.assertRaises(AssertionError): + add(a, a) + def test_kwargs_jit(self): @TinyJit def add_kwargs(first, second): return (first+second).realize() diff --git a/tinygrad/jit.py b/tinygrad/jit.py index c22b8845dc..72e08b7ed2 100644 --- a/tinygrad/jit.py +++ b/tinygrad/jit.py @@ -1,6 +1,6 @@ from typing import Callable, List, Tuple, Any, Dict, cast, Union import functools, itertools -from tinygrad.helpers import DEBUG +from tinygrad.helpers import DEBUG, DType from tinygrad.lazy import Device from tinygrad.tensor import Tensor @@ -12,7 +12,7 @@ class TinyJit: self.cnt: int = 0 self.jit_cache: List[Tuple[Callable, Any]] = [] # TODO: Any should be List[RawBuffer], but this fails self.ret: Any = None - self.input_replace: Dict[Tuple[int, int], Union[int, str]]= {} + self.input_replace: Dict[Tuple[int, int], Tuple[Union[int, str], int, DType]]= {} # (kernel_number, buffer_number) -> (input_name, expected_size, expected_type) # add support for instance methods def __get__(self, obj, objtype): return functools.partial(self.__call__, obj) @@ -22,10 +22,13 @@ class TinyJit: # NOTE: this cast is needed since although we know realize will create a ".realized" DeviceBuffer, the type checker doesn't input_rawbuffers: Dict[Union[int, str], RawBuffer] = {cast(Union[int, str], k):cast(RawBuffer, v.realize().lazydata.realized) for k,v in itertools.chain(enumerate(args), kwargs.items()) if isinstance(v, Tensor)} assert len(input_rawbuffers) != 0, "no inputs to JIT" + assert set(input_rawbuffers.values()) == len(input_rawbuffers), "duplicate inputs to JIT" if self.cnt >= 2: - for (j,i),idx in self.input_replace.items(): self.jit_cache[j][1][i] = input_rawbuffers[idx] + for (j,i),(input_name, expected_size, expected_type) in self.input_replace.items(): + assert input_rawbuffers[input_name].size == expected_size and input_rawbuffers[input_name].dtype == expected_type, f"size or type mismatch in JIT, {input_rawbuffers[input_name]} != <{expected_size}, {expected_type}>" + self.jit_cache[j][1][i] = input_rawbuffers[input_name] for prg, args in self.jit_cache: prg(args, jit=True) - for (j,i),idx in self.input_replace.items(): self.jit_cache[j][1][i] = None + for (j,i) in self.input_replace.keys(): self.jit_cache[j][1][i] = None elif self.cnt == 1: GlobalCounters.cache = [] self.ret = self.fxn(*args, **kwargs) @@ -38,10 +41,10 @@ class TinyJit: for j,(prg,args) in enumerate(self.jit_cache): # pylint: disable=E1133 for i,a in enumerate(args): if a in input_rawbuffers.values(): - self.input_replace[(j,i)] = [k for k,v in input_rawbuffers.items() if v == a][0] + self.input_replace[(j,i)] = [(k, v.size, v.dtype) for k,v in input_rawbuffers.items() if v == a][0] #if prg.local_size is None: prg.local_size = prg.optimize_local_size(args, preserve_output=True) # the JIT can optimize local - assert set(self.input_replace.values()) == set(input_rawbuffers.keys()), "some input tensors not found" - for (j,i),idx in self.input_replace.items(): self.jit_cache[j][1][i] = None + assert set([x[0] for x in self.input_replace.values()]) == set(input_rawbuffers.keys()), "some input tensors not found" + for (j,i) in self.input_replace.keys(): self.jit_cache[j][1][i] = None elif self.cnt == 0: self.ret = self.fxn(*args, **kwargs) self.cnt += 1