import unittest from tinygrad import Tensor, dtypes from tinygrad.helpers import Context from tinygrad.uop.ops import Ops from test.helpers import KernelCountException class TestRingAllReduce(unittest.TestCase): def test_schedule_ring(self): with Context(RING=2): N = 4 ds = tuple(f"NULL:{i}" for i in range(N)) t = Tensor.empty(N, N*100).shard(ds, axis=0).realize() linear = t.sum(0).linear_with_vars()[0] copies = [si for si in linear.src if si.src[0].op is Ops.STORE] pairs = [(c.src[1].buffer.device, c.src[2].buffer.device) for c in copies] # N*(N-1) scatter reduce, and N*(N-1) allgather if len(pairs) != N*(N-1)*2: raise KernelCountException(N*(N-1)*2, len(pairs)) # copy topology forms a ring self.assertEqual(len(set(pairs)), N) @Context(RING=0, ALL2ALL=0) def test_schedule_naive(self): N = 4 ds = tuple(f"NULL:{i}" for i in range(N)) t = Tensor.empty(N, 4096).shard(ds, axis=0).realize() linear = t.sum(0).linear_with_vars()[0] copies = [si for si in linear.src if si.src[0].op is Ops.STORE] sinks = [si for si in linear.src if si.src[0].op is Ops.SINK] pairs = [(c.src[1].buffer.device, c.src[2].buffer.device) for c in copies] if len(pairs) != N*(N-1): raise KernelCountException(N*(N-1), len(pairs)) if len(sinks) != 2: raise KernelCountException(2, len(sinks)) self.assertTrue(all(dst != src for dst, src in pairs)) class TestAllreduceCast(unittest.TestCase): def _get_copy_dtypes(self, dtype, allreduce_cast): ds = tuple(f"NULL:{i}" for i in range(2)) with Context(ALLREDUCE_CAST=allreduce_cast, RING=0, SCACHE=0): t = Tensor.empty(4, 4, dtype=dtype).shard(ds, axis=0) linear = t.sum(0).linear_with_vars()[0] return {si.src[1].dtype for si in linear.src if si.src[0].op is Ops.STORE} def test_allreduce_cast_bf16(self): # with ALLREDUCE_CAST, allreduce copies stay in bfloat16 instead of promoting to float32 self.assertNotIn(dtypes.float, self._get_copy_dtypes(dtypes.bfloat16, allreduce_cast=1)) self.assertIn(dtypes.float, self._get_copy_dtypes(dtypes.bfloat16, allreduce_cast=0)) def test_allreduce_cast_half(self): self.assertNotIn(dtypes.float, self._get_copy_dtypes(dtypes.half, allreduce_cast=1)) self.assertIn(dtypes.float, self._get_copy_dtypes(dtypes.half, allreduce_cast=0)) def test_allreduce_cast_float32_noop(self): # float32 should not be affected by ALLREDUCE_CAST (no promotion happens) dtypes_on = self._get_copy_dtypes(dtypes.float, allreduce_cast=1) dtypes_off = self._get_copy_dtypes(dtypes.float, allreduce_cast=0) self.assertEqual(dtypes_on, dtypes_off) if __name__ == '__main__': unittest.main()