hcq rename HCQCompat -> HCQ (#5577)

This commit is contained in:
nimlgen
2024-07-19 11:34:17 +03:00
committed by GitHub
parent 2b2f8ad18c
commit 9d7edc9269
5 changed files with 41 additions and 41 deletions
+9 -9
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+6 -6
View File
@@ -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
+6 -6
View File
@@ -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