mirror of
https://github.com/tinygrad/tinygrad.git
synced 2026-08-29 12:56:07 +00:00
Merge branch 'master' into delta_attention_scan
This commit is contained in:
@@ -561,9 +561,7 @@ class AMDDevice(HCQ2Compiled):
|
||||
def is_usb(self) -> bool: return False
|
||||
|
||||
def __init__(self, device:str=""):
|
||||
self.device_id = int(device.split(":")[1]) if ":" in device else 0
|
||||
|
||||
self.iface = self._select_iface()
|
||||
self.iface = self._select_iface(device)
|
||||
|
||||
self.target:tuple[int, ...] = ((trgt:=self.iface.props['gfx_target_version']) // 10000, (trgt // 100) % 100, trgt % 100)
|
||||
self.arch = "gfx%d%x%x" % self.target
|
||||
|
||||
+18
-7
@@ -1,7 +1,7 @@
|
||||
from __future__ import annotations
|
||||
from dataclasses import dataclass, replace
|
||||
from collections import defaultdict
|
||||
from typing import Any, Generic, TypeVar, Iterator, Generator, Self, TYPE_CHECKING
|
||||
from typing import Any, Callable, Generic, TypeVar, Iterator, Generator, Self, TYPE_CHECKING
|
||||
import importlib, inspect, functools, pathlib, os, contextlib, re, atexit, pickle, decimal
|
||||
from tinygrad.helpers import LRU, getenv, diskcache_get, diskcache_put, DEBUG, GlobalCounters, PROFILE, temp, colored
|
||||
from tinygrad.helpers import Context, CCACHE, ALLOW_DEVICE_USAGE, MAX_BUFFER_SIZE, cpu_events, ProfileEvent, ProfilePointEvent, suppress_finalizing
|
||||
@@ -331,17 +331,18 @@ class Program(Generic[DeviceType]):
|
||||
wait=False) -> float|None: pass
|
||||
|
||||
class Compiled:
|
||||
ifaces:list[Callable] = []
|
||||
profile_events:list[ProfileEvent] = [ProfileDeviceEvent("CPU")] # NOTE: CPU is the default device.
|
||||
|
||||
has_copy_queue:bool = True
|
||||
|
||||
pm_lower:Any = None
|
||||
pm_bufferize:Any = None
|
||||
|
||||
has_copy_queue:bool = True
|
||||
|
||||
def __init__(self, device:str, allocator:Allocator, renderers:list[type[Renderer]], runtime:type[Program[Self]]|None, graph=None, arch=None):
|
||||
from tinygrad.renderer import Renderer
|
||||
self.device, self.allocator, self.runtime_t, self.graph, self.renderers = device, allocator, runtime, graph, renderers or [Renderer]
|
||||
self.arch = arch
|
||||
self.device_id, self.arch = (int(idx) if ":" in device and (idx:=device.split(":")[1]).isdigit() else 0), arch
|
||||
self.cached_renderer:dict[Any, Renderer] = {}
|
||||
|
||||
@property
|
||||
@@ -364,11 +365,21 @@ class Compiled:
|
||||
return select_first_inited(select_by_name(self.renderers, self._renderer_name, t.renderer, f"{self.device} has no renderer {t.renderer!r}"),
|
||||
f"No renderer for {self.device} is available", self.cached_renderer, t)
|
||||
|
||||
def _select_iface(self, device:str):
|
||||
self.device_id = int(device.split(":")[1]) if ":" in device else 0
|
||||
assert (v:=getenv(k:=f'{type(self).__name__[:-6].upper()}_IFACE', "")) == "", \
|
||||
f"{k}={v} is deprecated, use DEV={replace(DEV.target(type(self).__name__[:-6]), interface=v)} instead"
|
||||
t = DEV.target(dev:=type(self).__name__[:-6])
|
||||
filtered = select_by_name(self.ifaces, lambda i: i.__name__[:-5], t.interface, f"{dev} has no interface {t.interface!r}")
|
||||
filtered = [i for i in filtered if t.interface.startswith("MOCK") or not i.__name__[:-5].startswith("MOCK")] # never fallback to mock ifaces
|
||||
return select_first_inited([functools.partial(iface, self, self.device_id) for iface in filtered],
|
||||
f"No interface for {dev}:{self.device_id} is available")
|
||||
|
||||
def count(self) -> int:
|
||||
"""
|
||||
Returns the number of physical accelerators available to the runtime.
|
||||
"""
|
||||
return 1
|
||||
return self.iface.count if hasattr(self, 'iface') else 1
|
||||
|
||||
def synchronize(self):
|
||||
"""
|
||||
@@ -386,7 +397,7 @@ class Compiled:
|
||||
"""
|
||||
Called at the end of process lifetime to allow the device to finalize.
|
||||
"""
|
||||
# override this in your device implementation
|
||||
if hasattr(self, 'iface') and hasattr(self.iface, 'device_fini'): self.iface.device_fini()
|
||||
|
||||
if PROFILE:
|
||||
@atexit.register
|
||||
@@ -408,7 +419,7 @@ def enumerate_devices_str() -> Generator[str, None, None]:
|
||||
ren_results, iface_results = [], []
|
||||
try:
|
||||
d = Device[device]
|
||||
for iface in [i for i in getattr(d, 'ifaces', []) if not i.__name__.startswith("MOCK")]:
|
||||
for iface in [i for i in d.ifaces if not i.__name__.startswith("MOCK")]:
|
||||
try:
|
||||
name = iface.__name__[:-5]
|
||||
default_text, count = ("(default)", d.count()) if type(d.iface) is iface else (f"(DEV={name}+{device} to make default)", iface(d, 0).count) # type: ignore
|
||||
|
||||
@@ -944,9 +944,7 @@ class AMDDevice(HCQCompiled):
|
||||
def is_usb(self) -> bool: return isinstance(self.iface, USBIface)
|
||||
|
||||
def __init__(self, device:str=""):
|
||||
self.device_id = int(device.split(":")[1]) if ":" in device else 0
|
||||
|
||||
self.iface = self._select_iface()
|
||||
self.iface = self._select_iface(device)
|
||||
|
||||
self.target:tuple[int, ...] = ((trgt:=self.iface.props['gfx_target_version']) // 10000, (trgt // 100) % 100, trgt % 100)
|
||||
self.arch = "gfx%d%x%x" % self.target
|
||||
|
||||
+13
-13
@@ -24,10 +24,10 @@ class CLCompiler(Compiler):
|
||||
super().__init__(f"compile_cl_{compile_key}")
|
||||
def compile(self, src:str) -> bytes:
|
||||
program = checked(cl.clCreateProgramWithSource(self.dev.context, 1, to_char_p_p([src.encode()]), None, status := ctypes.c_int32()), status)
|
||||
build_status: int = cl.clBuildProgram(program, 1, self.dev.device_id, None, BP_CB(), None)
|
||||
build_status: int = cl.clBuildProgram(program, 1, self.dev.cl_dev, None, BP_CB(), None)
|
||||
if build_status != 0:
|
||||
cl.clGetProgramBuildInfo(program, self.dev.device_id, cl.CL_PROGRAM_BUILD_LOG, 0, None, log_size := ctypes.c_size_t())
|
||||
cl.clGetProgramBuildInfo(program, self.dev.device_id, cl.CL_PROGRAM_BUILD_LOG,
|
||||
cl.clGetProgramBuildInfo(program, self.dev.cl_dev, cl.CL_PROGRAM_BUILD_LOG, 0, None, log_size := ctypes.c_size_t())
|
||||
cl.clGetProgramBuildInfo(program, self.dev.cl_dev, cl.CL_PROGRAM_BUILD_LOG,
|
||||
log_size.value, mstr := ctypes.create_string_buffer(log_size.value), None)
|
||||
raise CompileError(f"OpenCL Compile Error\n\n{mstr.value.decode()}")
|
||||
check(cl.clGetProgramInfo(program, cl.CL_PROGRAM_BINARY_SIZES, ctypes.sizeof(ctypes.c_size_t), binary_sizes := (ctypes.c_size_t * 1)(), None))
|
||||
@@ -39,11 +39,11 @@ class CLCompiler(Compiler):
|
||||
class CLProgram(Program['CLDevice']):
|
||||
def __init__(self, device:CLDevice, obj:TinyELF):
|
||||
self.dev, self.lib, self.signature = device, device.cl_compiler.compile_cached(obj.lib.decode()), obj.signature
|
||||
self.program = checked(cl.clCreateProgramWithBinary(device.context, 1, device.device_id, (ctypes.c_size_t * 1)(len(self.lib)),
|
||||
self.program = checked(cl.clCreateProgramWithBinary(device.context, 1, device.cl_dev, (ctypes.c_size_t * 1)(len(self.lib)),
|
||||
to_char_p_p([self.lib], ctypes.c_ubyte), binary_status := ctypes.c_int32(),
|
||||
errcode_ret := ctypes.c_int32()), errcode_ret)
|
||||
check(binary_status.value)
|
||||
check(cl.clBuildProgram(self.program, 1, device.device_id, None, BP_CB(), None)) # NOTE: OSX requires this
|
||||
check(cl.clBuildProgram(self.program, 1, device.cl_dev, None, BP_CB(), None)) # NOTE: OSX requires this
|
||||
self.kernel = checked(cl.clCreateKernel(self.program, obj.name.encode(), status := ctypes.c_int32()), status)
|
||||
|
||||
def __del__(self):
|
||||
@@ -101,17 +101,17 @@ class CLDevice(Compiled):
|
||||
CLDevice.device_ids = c.init_c_var((cl.cl_device_id * num_devices.value),
|
||||
lambda x: check(cl.clGetDeviceIDs(platform_ids[0], device_type, num_devices, x, None)))
|
||||
|
||||
self.device_id = CLDevice.device_ids[0 if ":" not in device else int(device.split(":")[1])]
|
||||
self.device_name = (cl.clGetDeviceInfo(self.device_id, cl.CL_DEVICE_NAME, 256,
|
||||
self.cl_dev = CLDevice.device_ids[0 if ":" not in device else int(device.split(":")[1])]
|
||||
self.device_name = (cl.clGetDeviceInfo(self.cl_dev, cl.CL_DEVICE_NAME, 256,
|
||||
buf:=ctypes.create_string_buffer(256), None), buf.value.decode())[1]
|
||||
self.driver_version = (cl.clGetDeviceInfo(self.device_id, cl.CL_DRIVER_VERSION, 256,
|
||||
self.driver_version = (cl.clGetDeviceInfo(self.cl_dev, cl.CL_DRIVER_VERSION, 256,
|
||||
buf:=ctypes.create_string_buffer(256), None), buf.value.decode())[1]
|
||||
if DEBUG >= 1: print(f"CLDevice: opening {self.device_name} with version {self.driver_version}")
|
||||
self.context = checked(cl.clCreateContext(None, 1, self.device_id, CC_CB(), None, status := ctypes.c_int32()), status)
|
||||
self.queue = checked(cl.clCreateCommandQueue(self.context, self.device_id, cl.CL_QUEUE_PROFILING_ENABLE, status), status)
|
||||
self.context = checked(cl.clCreateContext(None, 1, self.cl_dev, CC_CB(), None, status := ctypes.c_int32()), status)
|
||||
self.queue = checked(cl.clCreateCommandQueue(self.context, self.cl_dev, cl.CL_QUEUE_PROFILING_ENABLE, status), status)
|
||||
self.pending_copyin: list[memoryview] = []
|
||||
check(cl.clGetDeviceInfo(self.device_id, cl.CL_DEVICE_EXTENSIONS, 0, None, ctypes.byref(exts_len:=ctypes.c_size_t())))
|
||||
self.device_exts = (cl.clGetDeviceInfo(self.device_id, cl.CL_DEVICE_EXTENSIONS, exts_len.value,
|
||||
check(cl.clGetDeviceInfo(self.cl_dev, cl.CL_DEVICE_EXTENSIONS, 0, None, ctypes.byref(exts_len:=ctypes.c_size_t())))
|
||||
self.device_exts = (cl.clGetDeviceInfo(self.cl_dev, cl.CL_DEVICE_EXTENSIONS, exts_len.value,
|
||||
ctypes.byref(buf := ctypes.create_string_buffer(exts_len.value)), None),
|
||||
ctypes.string_at(buf).decode().split())[1]
|
||||
|
||||
@@ -119,7 +119,7 @@ class CLDevice(Compiled):
|
||||
|
||||
arch = ",".join(self.device_exts)
|
||||
if "cl_khr_image2d_from_buffer" in self.device_exts:
|
||||
check(cl.clGetDeviceInfo(self.device_id, cl.CL_DEVICE_IMAGE_PITCH_ALIGNMENT, 4, ctypes.byref(ipa := ctypes.c_uint32()), None))
|
||||
check(cl.clGetDeviceInfo(self.cl_dev, cl.CL_DEVICE_IMAGE_PITCH_ALIGNMENT, 4, ctypes.byref(ipa := ctypes.c_uint32()), None))
|
||||
arch += f",IMAGE_PITCH_ALIGNMENT={ipa.value}"
|
||||
super().__init__(device, CLAllocator(self), [OpenCLRenderer], CLProgram, arch=arch)
|
||||
|
||||
|
||||
@@ -588,8 +588,7 @@ class NVDevice(HCQCompiled[NVSignal]):
|
||||
def is_nvd(self) -> bool: return isinstance(self.iface, PCIIface)
|
||||
|
||||
def __init__(self, device:str=""):
|
||||
self.device_id = int(device.split(":")[1]) if ":" in device else 0
|
||||
self.iface = self._select_iface()
|
||||
self.iface = self._select_iface(device)
|
||||
|
||||
device_params = nv_gpu.NV0080_ALLOC_PARAMETERS(deviceId=self.iface.gpu_instance, hClientShare=self.iface.root,
|
||||
vaMode=nv_gpu.NV_DEVICE_ALLOCATION_VAMODE_OPTIONAL_MULTIPLE_VASPACES)
|
||||
|
||||
@@ -101,6 +101,5 @@ class RDMAAllocator(HCQAllocatorBase):
|
||||
|
||||
class RDMADevice(HCQCompiled):
|
||||
def __init__(self, device:str=""):
|
||||
self.device_id = int(device.split(":")[1]) if ":" in device else 0
|
||||
self.iface = MLXIface(self, self.device_id)
|
||||
self.iface = MLXIface(self, int(device.split(":")[1]) if ":" in device else 0)
|
||||
super().__init__(device, RDMAAllocator(self), [], None, signal_t=None)
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
from __future__ import annotations
|
||||
from typing import cast, Callable, Type, TypeVar, Generic, Any
|
||||
import contextlib, decimal, statistics, time, ctypes, array, os, struct, collections, functools, itertools
|
||||
from dataclasses import replace
|
||||
import contextlib, decimal, statistics, time, ctypes, array, os, struct, collections, itertools
|
||||
try: import fcntl # windows misses that
|
||||
except ImportError: fcntl = None #type:ignore[assignment]
|
||||
from tinygrad.helpers import DEV, PROFILE, getenv, to_mv, from_mv, cpu_profile, ProfileRangeEvent, select_first_inited, select_by_name, unwrap
|
||||
from tinygrad.helpers import DEV, PROFILE, getenv, to_mv, from_mv, cpu_profile, ProfileRangeEvent, unwrap
|
||||
from tinygrad.helpers import suppress_finalizing, pluralize, TracingKey
|
||||
from tinygrad.device import Device, BufferSpec, Compiled, LRUAllocator, ProfileDeviceEvent, ProfileProgramEvent, Program, TinyELF
|
||||
from tinygrad.uop.ops import sym_infer, sint, UOp
|
||||
@@ -393,8 +392,6 @@ class HCQCompiled(Compiled, Generic[SignalType]):
|
||||
def __init__(self, device:str, allocator:HCQAllocatorBase, compilers:list[type[Renderer]], runtime:type[Program]|None,
|
||||
signal_t:Type[SignalType]|None=None, comp_queue_t:Callable[..., HWQueue]|None=None, copy_queue_t:Callable[..., HWQueue]|None=None,
|
||||
kernargs_size=(16 << 20), sigalloc_size=0x1000, can_recover:bool=False, arch=None):
|
||||
self.device_id:int = int(device.split(":")[1]) if ":" in device else 0
|
||||
|
||||
from tinygrad.runtime.graph.hcq import HCQGraph
|
||||
super().__init__(device, allocator, compilers, runtime, HCQGraph, arch=arch)
|
||||
|
||||
@@ -424,8 +421,6 @@ class HCQCompiled(Compiled, Generic[SignalType]):
|
||||
|
||||
if self._is_cpu(): HCQCompiled.cpu_devices.append(self)
|
||||
|
||||
def count(self) -> int: return self.iface.count if hasattr(self, 'iface') else 1
|
||||
|
||||
def synchronize(self, timeout:int|None=None):
|
||||
if self.error_state is not None: raise self.error_state
|
||||
if not hasattr(self, 'timeline_signal'): return
|
||||
@@ -491,16 +486,6 @@ class HCQCompiled(Compiled, Generic[SignalType]):
|
||||
buf, realloced = self.allocator.alloc(oldbuf.size if oldbuf is not None else new_size, options=options), False
|
||||
return buf, realloced
|
||||
|
||||
def _select_iface(self):
|
||||
assert (v:=getenv(k:=f'{type(self).__name__[:-6].upper()}_IFACE', "")) == "", \
|
||||
f"{k}={v} is deprecated, use DEV={replace(DEV.target(type(self).__name__[:-6]), interface=v)} instead"
|
||||
assert hasattr(self, "ifaces"), "must have ifaces to select an iface"
|
||||
t = DEV.target(dev:=type(self).__name__[:-6])
|
||||
filtered = select_by_name(self.ifaces, lambda i: i.__name__[:-5], t.interface, f"{dev} has no interface {t.interface!r}")
|
||||
filtered = [i for i in filtered if t.interface.startswith("MOCK") or not i.__name__[:-5].startswith("MOCK")] # never fallback to mock ifaces
|
||||
return select_first_inited([functools.partial(cast(Callable, iface), self, self.device_id) for iface in filtered],
|
||||
f"No interface for {dev}:{self.device_id} is available")
|
||||
|
||||
def _is_cpu(self) -> bool: return hasattr(self, 'device') and self.device.split(":")[0] == "CPU"
|
||||
|
||||
def rdma_dev(self):
|
||||
@@ -512,9 +497,7 @@ class HCQCompiled(Compiled, Generic[SignalType]):
|
||||
def finalize(self):
|
||||
try: self.synchronize() # Try to finalize device in any case.
|
||||
except RuntimeError as e: print(f"{self.device} synchronization failed before finalizing: {e}")
|
||||
|
||||
# If the device has an interface, call its device_fini method to clean up resources.
|
||||
if hasattr(self, 'iface') and hasattr(self.iface, 'device_fini'): self.iface.device_fini()
|
||||
super().finalize()
|
||||
|
||||
class HCQBuffer:
|
||||
def __init__(self, va_addr:sint, size:int, meta:Any=None, _base:HCQBuffer|None=None, view:MMIOInterface|None=None, owner:Any=None):
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
from __future__ import annotations
|
||||
from typing import cast, Callable, TypeVar, Generic, Any, Sequence, Iterable
|
||||
from typing import cast, TypeVar, Generic, Any, Sequence, Iterable
|
||||
import struct, functools, time, collections, itertools, decimal, statistics
|
||||
from dataclasses import replace, dataclass
|
||||
from tinygrad.helpers import DEV, getenv, select_first_inited, select_by_name, suppress_finalizing, dedup, pluralize, JIT_BATCH_SIZE, unwrap, PROFILE
|
||||
from tinygrad.helpers import suppress_finalizing, dedup, pluralize, JIT_BATCH_SIZE, unwrap, PROFILE
|
||||
from tinygrad.helpers import to_tuple, round_up, partition, data64_le, panic, ContextVar, perf_counter_us, Context
|
||||
from tinygrad.device import Device, Buffer, BufferSpec, Compiled, LRUAllocator, MultiBuffer, DepsTracker
|
||||
from tinygrad.device import ProfileDeviceEvent, ProfileGraphEntry, ProfileGraphEvent
|
||||
@@ -23,8 +23,7 @@ HCQDeviceType = TypeVar('HCQDeviceType', bound='HCQ2Compiled')
|
||||
HCQ_RUNTIME_DEV = ContextVar("HCQ_RUNTIME_DEV", "CPU")
|
||||
|
||||
HCQ_DEVS = frozenset(("AMD", "CPU"))
|
||||
HCQ_P2P_DEVS = HCQ_DEVS | frozenset(("CPU",))
|
||||
HCQ_CACHE_TAGS = frozenset(("program", "systems", "template"))
|
||||
HCQ_CACHE_TAGS = frozenset(("program", "systems"))
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class HCQInfo:
|
||||
@@ -94,10 +93,10 @@ pm_replace_buffers = PatternMatcher([(UPat(Ops.CALL, name="call"), replace_call_
|
||||
# *****************
|
||||
# 1.1. prep: staging copies
|
||||
|
||||
def _need_staging(a, b): return all_devices_in(a.device, HCQ_DEVS - {"CPU"}) and not all_devices_in(b.device, HCQ_P2P_DEVS)
|
||||
def _need_staging(a, b): return all_devices_in(a.device, HCQ_DEVS - {"CPU"}) and not all_devices_in(b.device, HCQ_DEVS)
|
||||
|
||||
def _get_enqueue_devs(call:UOp) -> Any|None:
|
||||
if not (bufs:=call.src[1:]) or not all(all_devices_in(b.device, HCQ_P2P_DEVS) for b in bufs): return None
|
||||
if not (bufs:=call.src[1:]) or not all(all_devices_in(b.device, HCQ_DEVS) for b in bufs): return None
|
||||
devs = min(bufs, key=lambda b: to_tuple(b.device)[0].startswith("CPU")).device # prio to enqueue on not CPU device
|
||||
return devs if all_devices_in(devs, HCQ_DEVS) else None
|
||||
|
||||
@@ -532,7 +531,6 @@ class HCQ2Compiled(Compiled):
|
||||
wait_timeout_ms: float = 30000.0
|
||||
|
||||
def __init__(self, device:str, allocator:HCQAllocator, compilers:list[type[Renderer]], runtime, can_recover:bool=False, arch=None):
|
||||
self.device_id:int = int(device.split(":")[1]) if ":" in device else 0
|
||||
self.can_recover = can_recover
|
||||
|
||||
self.pm_bufferize = PatternMatcher([
|
||||
@@ -596,26 +594,12 @@ class HCQ2Compiled(Compiled):
|
||||
|
||||
def device_props(self) -> dict[str,Any]: return {} # to be overridden if needed. dict keys are backend dependent.
|
||||
|
||||
def count(self) -> int: return self.iface.count if hasattr(self, 'iface') else 1
|
||||
|
||||
def _select_iface(self):
|
||||
assert (v:=getenv(k:=f'{type(self).__name__[:-6].upper()}_IFACE', "")) == "", \
|
||||
f"{k}={v} is deprecated, use DEV={replace(DEV.target(type(self).__name__[:-6]), interface=v)} instead"
|
||||
assert hasattr(self, "ifaces"), "must have ifaces to select an iface"
|
||||
t = DEV.target(dev:=type(self).__name__[:-6])
|
||||
filtered = select_by_name(self.ifaces, lambda i: i.__name__[:-5], t.interface, f"{dev} has no interface {t.interface!r}")
|
||||
filtered = [i for i in filtered if t.interface.startswith("MOCK") or not i.__name__[:-5].startswith("MOCK")] # never fall back to mock ifaces
|
||||
return select_first_inited([functools.partial(cast(Callable, iface), self, self.device_id) for iface in filtered],
|
||||
f"No interface for {dev}:{self.device_id} is available")
|
||||
|
||||
def _is_cpu(self) -> bool: return hasattr(self, 'device') and self.device.split(":")[0] == "CPU"
|
||||
|
||||
def finalize(self):
|
||||
try: self.synchronize() # try to finalize the device in any case
|
||||
except RuntimeError as e: print(f"{self.device} synchronization failed before finalizing: {e}")
|
||||
|
||||
# if the device has an interface, call device_fini to clean up resources
|
||||
if hasattr(self, 'iface') and hasattr(self.iface, 'device_fini'): self.iface.device_fini()
|
||||
super().finalize()
|
||||
|
||||
@dataclass
|
||||
class HCQ2Buffer:
|
||||
|
||||
Reference in New Issue
Block a user