mirror of
https://github.com/tinygrad/tinygrad.git
synced 2026-08-30 09:06:07 +00:00
hcq rename HCQCompat -> HCQ (#5577)
This commit is contained in:
+9
-9
@@ -61,9 +61,9 @@ To implement custom commands in the queue, use the @hcq_command decorator for yo
|
||||
|
||||
### HCQ Compatible Device
|
||||
|
||||
The `HCQCompatCompiled` class defines the API for HCQ-compatible devices. This class serves as an abstract base class that device-specific implementations should inherit from and implement.
|
||||
The `HCQCompiled` class defines the API for HCQ-compatible devices. This class serves as an abstract base class that device-specific implementations should inherit from and implement.
|
||||
|
||||
::: tinygrad.device.HCQCompatCompiled
|
||||
::: tinygrad.device.HCQCompiled
|
||||
options:
|
||||
members: [
|
||||
"_alloc_signal",
|
||||
@@ -93,13 +93,13 @@ timestamp = your_device._read_timestamp()
|
||||
|
||||
##### Synchronization signals
|
||||
|
||||
Each HCQ-compatible device must allocate two signals for global synchronization purposes. These signals are passed to the `HCQCompatCompiled` base class during initialization: an active timeline signal `self.timeline_signal` and a shadow timeline signal `self._shadow_timeline_signal` which helps to handle signal value overflow issues. You can find more about synchronization in the [synchronization section](#synchronization)
|
||||
Each HCQ-compatible device must allocate two signals for global synchronization purposes. These signals are passed to the `HCQCompiled` base class during initialization: an active timeline signal `self.timeline_signal` and a shadow timeline signal `self._shadow_timeline_signal` which helps to handle signal value overflow issues. You can find more about synchronization in the [synchronization section](#synchronization)
|
||||
|
||||
### HCQ Compatible Allocator
|
||||
|
||||
The `HCQCompatAllocator` base class simplifies allocator logic by leveraging [command queues](#commandqueues) abstractions. This class efficiently handles copy and transfer operations, leaving only the alloc and free functions to be implemented by individual backends.
|
||||
The `HCQAllocator` base class simplifies allocator logic by leveraging [command queues](#commandqueues) abstractions. This class efficiently handles copy and transfer operations, leaving only the alloc and free functions to be implemented by individual backends.
|
||||
|
||||
::: tinygrad.device.HCQCompatAllocator
|
||||
::: tinygrad.device.HCQAllocator
|
||||
options:
|
||||
members: [
|
||||
"_alloc",
|
||||
@@ -109,18 +109,18 @@ The `HCQCompatAllocator` base class simplifies allocator logic by leveraging [co
|
||||
|
||||
#### HCQ Allocator Result Protocol
|
||||
|
||||
Backends must adhere to the `HCQCompatAllocRes` protocol when returning allocation results.
|
||||
Backends must adhere to the `HCQBuffer` protocol when returning allocation results.
|
||||
|
||||
::: tinygrad.device.HCQCompatAllocRes
|
||||
::: tinygrad.device.HCQBuffer
|
||||
options:
|
||||
members: true
|
||||
show_source: false
|
||||
|
||||
### HCQ Compatible Program
|
||||
|
||||
The `HCQCompatProgram` is a helper base class for defining programs compatible with HCQ-compatible devices. Currently, the arguments consist of pointers to buffers, followed by `vals` fields. The convention expects a packed struct containing the passed pointers, followed by `vals` located at `kernargs_args_offset`.
|
||||
The `HCQProgram` is a helper base class for defining programs compatible with HCQ-compatible devices. Currently, the arguments consist of pointers to buffers, followed by `vals` fields. The convention expects a packed struct containing the passed pointers, followed by `vals` located at `kernargs_args_offset`.
|
||||
|
||||
::: tinygrad.device.HCQCompatProgram
|
||||
::: tinygrad.device.HCQProgram
|
||||
options:
|
||||
members: true
|
||||
show_source: false
|
||||
|
||||
+2
-2
@@ -1,13 +1,13 @@
|
||||
import unittest, ctypes, struct
|
||||
from tinygrad import Device, Tensor, dtypes
|
||||
from tinygrad.helpers import CI, getenv
|
||||
from tinygrad.device import Buffer, BufferOptions, HCQCompatCompiled
|
||||
from tinygrad.device import Buffer, BufferOptions, HCQCompiled
|
||||
from tinygrad.engine.schedule import create_schedule
|
||||
from tinygrad.engine.realize import get_runner
|
||||
|
||||
MOCKGPU = getenv("MOCKGPU")
|
||||
|
||||
@unittest.skipUnless(issubclass(type(Device[Device.DEFAULT]), HCQCompatCompiled), "HCQCompat device required to run")
|
||||
@unittest.skipUnless(issubclass(type(Device[Device.DEFAULT]), HCQCompiled), "HCQ device required to run")
|
||||
class TestHCQ(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(self):
|
||||
|
||||
+18
-18
@@ -287,7 +287,7 @@ class HWCommandQueue:
|
||||
return self
|
||||
def _update_wait(self, cmd_idx:int, signal:Optional[Any], value:Optional[int]): raise NotImplementedError("backend should overload this function")
|
||||
|
||||
def submit(self, device:HCQCompatCompiled):
|
||||
def submit(self, device:HCQCompiled):
|
||||
"""
|
||||
Submits the command queue to a specific device for execution.
|
||||
|
||||
@@ -296,7 +296,7 @@ class HWCommandQueue:
|
||||
"""
|
||||
self._submit(device)
|
||||
return self
|
||||
def _submit(self, device:HCQCompatCompiled): raise NotImplementedError("backend should overload this function")
|
||||
def _submit(self, device:HCQCompiled): raise NotImplementedError("backend should overload this function")
|
||||
|
||||
class HWComputeQueue(HWCommandQueue):
|
||||
@hcq_command
|
||||
@@ -308,7 +308,7 @@ class HWComputeQueue(HWCommandQueue):
|
||||
def _memory_barrier(self): pass
|
||||
|
||||
@hcq_command
|
||||
def exec(self, prg:HCQCompatProgram, kernargs:int, global_size:Tuple[int,int,int], local_size:Tuple[int,int,int]):
|
||||
def exec(self, prg:HCQProgram, kernargs:int, global_size:Tuple[int,int,int], local_size:Tuple[int,int,int]):
|
||||
"""
|
||||
Enqueues an execution command for a kernel program.
|
||||
|
||||
@@ -337,7 +337,7 @@ class HWComputeQueue(HWCommandQueue):
|
||||
|
||||
class HWCopyQueue(HWCommandQueue):
|
||||
@hcq_command
|
||||
def copy(self, dest:HCQCompatAllocRes, src:HCQCompatAllocRes, copy_size:int):
|
||||
def copy(self, dest:HCQBuffer, src:HCQBuffer, copy_size:int):
|
||||
"""
|
||||
Enqueues a copy command to transfer data.
|
||||
|
||||
@@ -347,9 +347,9 @@ class HWCopyQueue(HWCommandQueue):
|
||||
copy_size: The size of data to copy
|
||||
"""
|
||||
self._copy(dest, src, copy_size)
|
||||
def _copy(self, dest:HCQCompatAllocRes, src:HCQCompatAllocRes, copy_size:int): raise NotImplementedError("backend should overload this function")
|
||||
def _copy(self, dest:HCQBuffer, src:HCQBuffer, copy_size:int): raise NotImplementedError("backend should overload this function")
|
||||
|
||||
def update_copy(self, cmd_idx:int, dest:Optional[HCQCompatAllocRes]=None, src:Optional[HCQCompatAllocRes]=None):
|
||||
def update_copy(self, cmd_idx:int, dest:Optional[HCQBuffer]=None, src:Optional[HCQBuffer]=None):
|
||||
"""
|
||||
Updates a previously queued copy command.
|
||||
|
||||
@@ -377,12 +377,12 @@ def hcq_profile(dev, enabled, desc, queue_type=None, queue=None):
|
||||
|
||||
if enabled and PROFILE: dev.sig_prof_records.append((st, en, desc, queue_type is dev.hw_copy_queue_t))
|
||||
|
||||
class HCQCompatProgram:
|
||||
class HCQProgram:
|
||||
def __init__(self, kernargs_alloc_size:int, kernargs_args_offset:int=0):
|
||||
self.kernargs_alloc_size, self.kernargs_args_offset = kernargs_alloc_size, kernargs_args_offset
|
||||
def fill_kernargs(self, kernargs_ptr:int, bufs:Tuple[Any, ...], vals:Tuple[int, ...]=()): raise NotImplementedError("need fill_kernargs")
|
||||
|
||||
class HCQCompatCompiled(Compiled):
|
||||
class HCQCompiled(Compiled):
|
||||
"""
|
||||
A base class for devices compatible with the HCQ (Hardware Command Queue) API.
|
||||
"""
|
||||
@@ -473,27 +473,27 @@ class HCQCompatCompiled(Compiled):
|
||||
def _wrap_timeline_signal(self):
|
||||
self.timeline_signal, self._shadow_timeline_signal, self.timeline_value = self._shadow_timeline_signal, self.timeline_signal, 1
|
||||
self._set_signal(self.timeline_signal, 0)
|
||||
cast(HCQCompatAllocator, self.allocator).b_timeline = [0] * len(cast(HCQCompatAllocator, self.allocator).b)
|
||||
cast(HCQAllocator, self.allocator).b_timeline = [0] * len(cast(HCQAllocator, self.allocator).b)
|
||||
|
||||
# Protocol for hcq compatible allocators for allocated buffers to contain VA address and it's size.
|
||||
class HCQCompatAllocRes(Protocol): va_addr:int; size:int # noqa: E702
|
||||
class HCQBuffer(Protocol): va_addr:int; size:int # noqa: E702
|
||||
|
||||
class HCQCompatAllocator(LRUAllocator): # pylint: disable=abstract-method
|
||||
class HCQAllocator(LRUAllocator): # pylint: disable=abstract-method
|
||||
"""
|
||||
A base allocator class compatible with the HCQ (Hardware Command Queue) API.
|
||||
|
||||
This class implements basic copy operations following the HCQ API, utilizing both `HWComputeQueue` and `HWCopyQueue`.
|
||||
"""
|
||||
|
||||
def __init__(self, device:HCQCompatCompiled, batch_size:int=(2 << 20), batch_cnt:int=32):
|
||||
def __init__(self, device:HCQCompiled, batch_size:int=(2 << 20), batch_cnt:int=32):
|
||||
self.device:Any = device
|
||||
self.b = [self._alloc(batch_size, BufferOptions(host=True)) for _ in range(batch_cnt)]
|
||||
self.b_timeline, self.b_next = [0] * len(self.b), 0
|
||||
super().__init__()
|
||||
|
||||
def _alloc(self, size:int, options:BufferOptions) -> HCQCompatAllocRes: raise NotImplementedError("need hcq compat alloc")
|
||||
def _alloc(self, size:int, options:BufferOptions) -> HCQBuffer: raise NotImplementedError("need hcq compat alloc")
|
||||
|
||||
def copyin(self, dest:HCQCompatAllocRes, src:memoryview):
|
||||
def copyin(self, dest:HCQBuffer, src:memoryview):
|
||||
with hcq_profile(self.device, queue_type=self.device.hw_copy_queue_t, desc=f"CPU -> {self.device.dname}", enabled=PROFILE):
|
||||
for i in range(0, src.nbytes, self.b[0].size):
|
||||
self.b_next = (self.b_next + 1) % len(self.b)
|
||||
@@ -505,7 +505,7 @@ class HCQCompatAllocator(LRUAllocator): # pylint: disable=abstract-method
|
||||
self.b_timeline[self.b_next] = self.device.timeline_value
|
||||
self.device.timeline_value += 1
|
||||
|
||||
def copy_from_disk(self, dest:HCQCompatAllocRes, src, size):
|
||||
def copy_from_disk(self, dest:HCQBuffer, src, size):
|
||||
def _get_temp_buf():
|
||||
# Check if the next buffer is safe to be used (its signal has passed) and reserve it.
|
||||
if self.b_timeline[(self.b_next + 1) % len(self.b)] <= self.device._read_signal(self.device.timeline_signal):
|
||||
@@ -521,7 +521,7 @@ class HCQCompatAllocator(LRUAllocator): # pylint: disable=abstract-method
|
||||
self.b_timeline[batch_info[1]] = self.device.timeline_value
|
||||
self.device.timeline_value += 1
|
||||
|
||||
def copyout(self, dest:memoryview, src:HCQCompatAllocRes):
|
||||
def copyout(self, dest:memoryview, src:HCQBuffer):
|
||||
self.device.synchronize()
|
||||
|
||||
with hcq_profile(self.device, queue_type=self.device.hw_copy_queue_t, desc=f"{self.device.dname} -> CPU", enabled=PROFILE):
|
||||
@@ -534,7 +534,7 @@ class HCQCompatAllocator(LRUAllocator): # pylint: disable=abstract-method
|
||||
|
||||
ctypes.memmove(from_mv(dest[i:]), self.b[0].va_addr, lsize)
|
||||
|
||||
def transfer(self, dest:HCQCompatAllocRes, src:HCQCompatAllocRes, sz:int, src_dev, dest_dev):
|
||||
def transfer(self, dest:HCQBuffer, src:HCQBuffer, sz:int, src_dev, dest_dev):
|
||||
src_dev._gpu_map(dest)
|
||||
|
||||
with hcq_profile(self.device, queue_type=self.device.hw_copy_queue_t, desc=f"{src_dev.dname} -> {dest_dev.dname}", enabled=PROFILE):
|
||||
@@ -550,6 +550,6 @@ class HCQCompatAllocator(LRUAllocator): # pylint: disable=abstract-method
|
||||
.signal(dest_dev.timeline_signal, dest_dev.timeline_value).submit(dest_dev)
|
||||
dest_dev.timeline_value += 1
|
||||
|
||||
def offset(self, buf, size:int, offset:int) -> HCQCompatAllocRes:
|
||||
def offset(self, buf, size:int, offset:int) -> HCQBuffer:
|
||||
return type(buf)(va_addr=buf.va_addr + offset, size=size, **{k:v for k,v in buf.__dict__.items() if k not in ['va_addr', 'size']},
|
||||
**{x[0]:getattr(buf, x[0]) for x in getattr(buf, '_fields_', []) if x[0] not in ['va_addr', 'size']}, _base=buf)
|
||||
|
||||
@@ -2,8 +2,8 @@ from __future__ import annotations
|
||||
from typing import Tuple, List, Any
|
||||
import os, fcntl, ctypes, ctypes.util, functools, re, pathlib, mmap, errno, subprocess, time, array
|
||||
from dataclasses import dataclass
|
||||
from tinygrad.device import HCQCompatCompiled, HCQCompatAllocator, HCQCompatAllocRes, HWComputeQueue, HWCopyQueue, hcq_profile, \
|
||||
HCQCompatProgram, Compiler, CompileError, BufferOptions
|
||||
from tinygrad.device import HCQCompiled, HCQAllocator, HCQBuffer, HWComputeQueue, HWCopyQueue, hcq_profile, \
|
||||
HCQProgram, Compiler, CompileError, BufferOptions
|
||||
from tinygrad.helpers import getenv, to_mv, round_up, DEBUG, PROFILE, mv_address
|
||||
from tinygrad.renderer.cstyle import AMDRenderer
|
||||
from tinygrad.runtime.support.hip_comgr import compile_hip
|
||||
@@ -268,7 +268,7 @@ class AMDCopyQueue(HWCopyQueue):
|
||||
device.sdma_queue.write_ptr[0] = device.sdma_queue.put_value
|
||||
device.sdma_queue.doorbell[0] = device.sdma_queue.put_value
|
||||
|
||||
class AMDProgram(HCQCompatProgram):
|
||||
class AMDProgram(HCQProgram):
|
||||
def __init__(self, device:AMDDevice, name:str, lib:bytes):
|
||||
# TODO; this API needs the type signature of the function and global_size/local_size
|
||||
self.device, self.name, self.lib = device, name, lib
|
||||
@@ -335,10 +335,10 @@ class AMDProgram(HCQCompatProgram):
|
||||
if not PROFILE: self.device.signals_pool += [sig_st, sig_en]
|
||||
return (sig_en.start_ts - sig_st.start_ts) / 1e8
|
||||
|
||||
class AMDAllocator(HCQCompatAllocator):
|
||||
class AMDAllocator(HCQAllocator):
|
||||
def __init__(self, device:AMDDevice): super().__init__(device, batch_size=SDMA_MAX_COPY_SIZE)
|
||||
|
||||
def _alloc(self, size:int, options:BufferOptions) -> HCQCompatAllocRes:
|
||||
def _alloc(self, size:int, options:BufferOptions) -> HCQBuffer:
|
||||
if options.host: return self.device._gpu_alloc(size, kfd.KFD_IOC_ALLOC_MEM_FLAGS_USERPTR, public=True)
|
||||
return self.device._gpu_alloc(size, kfd.KFD_IOC_ALLOC_MEM_FLAGS_VRAM, public=options.cpu_access)
|
||||
|
||||
@@ -354,7 +354,7 @@ class AMDQueueDesc:
|
||||
doorbell: memoryview
|
||||
put_value: int = 0
|
||||
|
||||
class AMDDevice(HCQCompatCompiled):
|
||||
class AMDDevice(HCQCompiled):
|
||||
kfd:int = -1
|
||||
event_page:Any = None # TODO: fix types in kfd, Optional[kfd.struct_kfd_ioctl_alloc_memory_of_gpu_args]
|
||||
signals_page:Any = None
|
||||
|
||||
@@ -2,8 +2,8 @@ from __future__ import annotations
|
||||
import os, ctypes, contextlib, pathlib, re, fcntl, functools, mmap, struct, tempfile, hashlib, subprocess, time, array
|
||||
from typing import Tuple, List, Any, cast, Union, Dict
|
||||
from dataclasses import dataclass
|
||||
from tinygrad.device import HCQCompatCompiled, HCQCompatAllocator, HCQCompatAllocRes, HWCommandQueue, HWComputeQueue, HWCopyQueue, hcq_command, \
|
||||
HCQCompatProgram, hcq_profile, Compiler, CompileError, BufferOptions
|
||||
from tinygrad.device import HCQCompiled, HCQAllocator, HCQBuffer, HWCommandQueue, HWComputeQueue, HWCopyQueue, hcq_command, \
|
||||
HCQProgram, hcq_profile, Compiler, CompileError, BufferOptions
|
||||
from tinygrad.helpers import getenv, mv_address, init_c_struct_t, to_mv, round_up, to_char_p_p, DEBUG, prod, PROFILE
|
||||
from tinygrad.renderer.cstyle import NVRenderer
|
||||
from tinygrad.runtime.ops_cuda import check as cuda_check, _get_bytes, CUDACompiler, PTXCompiler, PTX
|
||||
@@ -217,7 +217,7 @@ class NVCopyQueue(NVCommandQueue, HWCopyQueue):
|
||||
|
||||
def _submit(self, device): self._submit_to_gpfifo(device, cast(NVDevice, device).dma_gpfifo)
|
||||
|
||||
class NVProgram(HCQCompatProgram):
|
||||
class NVProgram(HCQProgram):
|
||||
def __init__(self, device:NVDevice, name:str, lib:bytes):
|
||||
self.device, self.name, self.lib = device, name, lib
|
||||
if DEBUG >= 6:
|
||||
@@ -314,10 +314,10 @@ class NVProgram(HCQCompatProgram):
|
||||
if not PROFILE: self.device.signals_pool += [sig_st, sig_en]
|
||||
return (sig_en[1] - sig_st[1]) / 1e9
|
||||
|
||||
class NVAllocator(HCQCompatAllocator):
|
||||
class NVAllocator(HCQAllocator):
|
||||
def __init__(self, device:NVDevice): super().__init__(device)
|
||||
|
||||
def _alloc(self, size:int, options:BufferOptions) -> HCQCompatAllocRes:
|
||||
def _alloc(self, size:int, options:BufferOptions) -> HCQBuffer:
|
||||
if options.host: return self.device._gpu_host_alloc(size)
|
||||
return self.device._gpu_alloc(size, map_to_cpu=options.cpu_access, huge_page=(size > (16 << 20)))
|
||||
|
||||
@@ -335,7 +335,7 @@ class GPFifo:
|
||||
put_value: int = 0
|
||||
|
||||
MAP_FIXED, MAP_NORESERVE = 0x10, 0x400
|
||||
class NVDevice(HCQCompatCompiled):
|
||||
class NVDevice(HCQCompiled):
|
||||
root = None
|
||||
fd_ctl: int = -1
|
||||
fd_uvm: int = -1
|
||||
|
||||
Reference in New Issue
Block a user