hipfile 0.2.0.dev1__tar.gz → 0.3.0.dev0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,6 +4,12 @@ All notable changes to the hipFile Python bindings will be documented in this fi
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.3.0dev0]
8
+
9
+ ### Added
10
+
11
+ - Docstrings for the public Python API.
12
+
7
13
  ## [0.2.0.dev1]
8
14
 
9
15
  ### Fixed
@@ -1,3 +1,7 @@
1
+ # Copyright (c) Advanced Micro Devices, Inc. All rights reserved.
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+
1
5
  cmake_minimum_required(VERSION 3.21)
2
6
  project(hipfile-python LANGUAGES C)
3
7
 
@@ -8,11 +12,27 @@ find_program(CYTHON_EXECUTABLE cython REQUIRED)
8
12
 
9
13
  # ---- hipFile library & headers --------------------------------------------
10
14
 
15
+ # ROCm install hints: prefer $ROCM_PATH from env; fall back to a glob of
16
+ # /opt/rocm/core-* (7.11+ layout) plus legacy /opt/rocm.
17
+ if(DEFINED ENV{ROCM_PATH})
18
+ set(_ROCM_ROOTS "$ENV{ROCM_PATH}")
19
+ else()
20
+ file(GLOB _ROCM_ROOTS "/opt/rocm/core-*")
21
+ list(APPEND _ROCM_ROOTS "/opt/rocm")
22
+ endif()
23
+
24
+ set(_ROCM_INCLUDE_HINTS)
25
+ set(_ROCM_LIB_HINTS)
26
+ foreach(_r IN LISTS _ROCM_ROOTS)
27
+ list(APPEND _ROCM_INCLUDE_HINTS "${_r}/include")
28
+ list(APPEND _ROCM_LIB_HINTS "${_r}/lib")
29
+ endforeach()
30
+
11
31
  # hipfile.h location (prefer sibling include/ from source, fall back to installed)
12
32
  find_path(HIPFILE_INCLUDE_DIR hipfile.h
13
33
  HINTS
14
34
  "${CMAKE_CURRENT_SOURCE_DIR}/../include"
15
- "/opt/rocm/include"
35
+ ${_ROCM_INCLUDE_HINTS}
16
36
  PATH "Path to directory containing hipfile.h"
17
37
  )
18
38
  if(NOT HIPFILE_INCLUDE_DIR)
@@ -25,7 +45,7 @@ endif()
25
45
  find_library(HIPFILE_LIBRARY hipfile
26
46
  HINTS
27
47
  "${CMAKE_CURRENT_SOURCE_DIR}/../build/src/amd_detail"
28
- "/opt/rocm/lib"
48
+ ${_ROCM_LIB_HINTS}
29
49
  )
30
50
  if(NOT HIPFILE_LIBRARY)
31
51
  message(FATAL_ERROR
@@ -36,8 +56,7 @@ endif()
36
56
  # HIP runtime headers (needed because hipfile.h includes hip/hip_runtime_api.h)
37
57
  find_path(HIP_INCLUDE_DIR hip/hip_runtime_api.h
38
58
  HINTS
39
- "/opt/rocm/include"
40
- "/opt/rocm/hip/include"
59
+ ${_ROCM_INCLUDE_HINTS}
41
60
  )
42
61
  if(NOT HIP_INCLUDE_DIR)
43
62
  message(FATAL_ERROR
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: hipfile
3
- Version: 0.2.0.dev1
3
+ Version: 0.3.0.dev0
4
4
  Summary: Python bindings for hipFile — direct-to-GPU file IO via AMD Infinity Storage (AIS)
5
5
  Keywords: hipfile,rocm,amd,hip,AIS
6
6
  Maintainer-Email: AMD hipFile Team <hipfile-maintainer@amd.com>, Riley Dixon <riley.dixon@amd.com>
@@ -1,4 +1,8 @@
1
- # pylint: disable=C0114
1
+ # Copyright (c) Advanced Micro Devices, Inc. All rights reserved.
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+
5
+ """Python bindings for the hipFile GPU-accelerated file I/O library."""
2
6
 
3
7
  from hipfile._hipfile import ( # pylint: disable=E0401,E0611
4
8
  # Constants
@@ -18,7 +18,7 @@ cdef extern from "hip/hip_runtime_api.h":
18
18
  ctypedef enum hipError_t:
19
19
  hipSuccess = 0
20
20
 
21
- hipError_t hipPeekAtLastError()
21
+ hipError_t hipPeekAtLastError() nogil
22
22
 
23
23
 
24
24
  # ---------------------------------------------------------------------------
@@ -158,33 +158,33 @@ cdef extern from "hipfile.h":
158
158
  # -- Function declarations ----------------------------------------------
159
159
 
160
160
  # Error
161
- const char *hipFileGetOpErrorString(hipFileOpError_t status)
161
+ const char *hipFileGetOpErrorString(hipFileOpError_t status) nogil
162
162
 
163
163
  # File handles
164
164
  hipFileError_t hipFileHandleRegister(hipFileHandle_t *fh,
165
- hipFileDescr_t *descr)
166
- void hipFileHandleDeregister(hipFileHandle_t fh)
165
+ hipFileDescr_t *descr) nogil
166
+ void hipFileHandleDeregister(hipFileHandle_t fh) nogil
167
167
 
168
168
  # Buffer registration
169
169
  hipFileError_t hipFileBufRegister(const void *buffer_base,
170
- size_t length, int flags)
171
- hipFileError_t hipFileBufDeregister(const void *buffer_base)
170
+ size_t length, int flags) nogil
171
+ hipFileError_t hipFileBufDeregister(const void *buffer_base) nogil
172
172
 
173
173
  # Synchronous I/O
174
174
  ssize_t hipFileRead(hipFileHandle_t fh, void *buffer_base, size_t size,
175
- hoff_t file_offset, hoff_t buffer_offset)
175
+ hoff_t file_offset, hoff_t buffer_offset) nogil
176
176
  ssize_t hipFileWrite(hipFileHandle_t fh, const void *buffer_base,
177
177
  size_t size, hoff_t file_offset,
178
- hoff_t buffer_offset)
178
+ hoff_t buffer_offset) nogil
179
179
 
180
180
  # Driver lifecycle
181
- hipFileError_t hipFileDriverOpen()
182
- hipFileError_t hipFileDriverClose()
183
- int64_t hipFileUseCount()
181
+ hipFileError_t hipFileDriverOpen() nogil
182
+ hipFileError_t hipFileDriverClose() nogil
183
+ int64_t hipFileUseCount() nogil
184
184
 
185
185
  # Driver properties
186
- hipFileError_t hipFileDriverGetProperties(hipFileDriverProps_t *props)
186
+ hipFileError_t hipFileDriverGetProperties(hipFileDriverProps_t *props) nogil
187
187
 
188
188
  # Version
189
189
  hipFileError_t hipFileGetVersion(unsigned *major, unsigned *minor,
190
- unsigned *patch)
190
+ unsigned *patch) nogil
@@ -9,7 +9,7 @@ Functions that return ``hipFileError_t`` in C return a
9
9
 
10
10
  from libc.errno cimport errno
11
11
  from libc.string cimport memset
12
- from libc.stdint cimport uintptr_t
12
+ from libc.stdint cimport int64_t, uintptr_t
13
13
 
14
14
  cimport hipfile._chipfile as _c
15
15
 
@@ -96,7 +96,9 @@ def is_hipfile_err(int err_code):
96
96
 
97
97
  def hipfile_errstr(int err_code):
98
98
  """Equivalent of the ``HIPFILE_ERRSTR`` C macro."""
99
- cdef const char *s = _c.hipFileGetOpErrorString(<_c.hipFileOpError_t>abs(err_code))
99
+ cdef const char *s
100
+ with nogil:
101
+ s = _c.hipFileGetOpErrorString(<_c.hipFileOpError_t>abs(err_code))
100
102
  if s == NULL:
101
103
  return ""
102
104
  return s.decode("utf-8")
@@ -120,7 +122,9 @@ def hip_drv_err(tuple err):
120
122
 
121
123
  def hipFileGetOpErrorString(int status):
122
124
  """Wrapper for ``hipFileGetOpErrorString``."""
123
- cdef const char *s = _c.hipFileGetOpErrorString(<_c.hipFileOpError_t>status)
125
+ cdef const char *s
126
+ with nogil:
127
+ s = _c.hipFileGetOpErrorString(<_c.hipFileOpError_t>status)
124
128
  if s == NULL:
125
129
  return ""
126
130
  return s.decode("utf-8")
@@ -132,17 +136,26 @@ def hipFileGetOpErrorString(int status):
132
136
 
133
137
  def hipFileDriverOpen():
134
138
  """Wrapper for ``hipFileDriverOpen``."""
135
- return _err(_c.hipFileDriverOpen())
139
+ cdef _c.hipFileError_t e
140
+ with nogil:
141
+ e = _c.hipFileDriverOpen()
142
+ return _err(e)
136
143
 
137
144
 
138
145
  def hipFileDriverClose():
139
146
  """Wrapper for ``hipFileDriverClose``."""
140
- return _err(_c.hipFileDriverClose())
147
+ cdef _c.hipFileError_t e
148
+ with nogil:
149
+ e = _c.hipFileDriverClose()
150
+ return _err(e)
141
151
 
142
152
 
143
153
  def hipFileUseCount():
144
154
  """Wrapper for ``hipFileUseCount``."""
145
- return <int>_c.hipFileUseCount()
155
+ cdef int64_t count
156
+ with nogil:
157
+ count = _c.hipFileUseCount()
158
+ return <int>count
146
159
 
147
160
 
148
161
  # ---------------------------------------------------------------------------
@@ -155,7 +168,9 @@ def hipFileGetVersion():
155
168
  Returns ``((major, minor, patch), error_tuple)``.
156
169
  """
157
170
  cdef unsigned major = 0, minor = 0, patch = 0
158
- cdef _c.hipFileError_t e = _c.hipFileGetVersion(&major, &minor, &patch)
171
+ cdef _c.hipFileError_t e
172
+ with nogil:
173
+ e = _c.hipFileGetVersion(&major, &minor, &patch)
159
174
  return ((major, minor, patch), _err(e))
160
175
 
161
176
 
@@ -178,19 +193,22 @@ def hipFileHandleRegister(uintptr_t handle_value, int handle_type):
178
193
  """
179
194
  cdef _c.hipFileHandle_t fh = NULL
180
195
  cdef _c.hipFileDescr_t descr
181
- memset(&descr, 0, sizeof(descr))
182
- descr.type = <_c.hipFileFileHandleType_t>handle_type
183
- if handle_type == <int>_c.hipFileHandleTypeOpaqueWin32:
184
- descr.hFile = <void *>handle_value
185
- else:
186
- descr.fd = <int>handle_value
187
- cdef _c.hipFileError_t e = _c.hipFileHandleRegister(&fh, &descr)
196
+ cdef _c.hipFileError_t e
197
+ with nogil:
198
+ memset(&descr, 0, sizeof(descr))
199
+ descr.type = <_c.hipFileFileHandleType_t>handle_type
200
+ if handle_type == <int>_c.hipFileHandleTypeOpaqueWin32:
201
+ descr.hFile = <void *>handle_value
202
+ else:
203
+ descr.fd = <int>handle_value
204
+ e = _c.hipFileHandleRegister(&fh, &descr)
188
205
  return (<uintptr_t>fh, _err(e))
189
206
 
190
207
 
191
208
  def hipFileHandleDeregister(uintptr_t handle):
192
209
  """Wrapper for ``hipFileHandleDeregister``."""
193
- _c.hipFileHandleDeregister(<_c.hipFileHandle_t>handle)
210
+ with nogil:
211
+ _c.hipFileHandleDeregister(<_c.hipFileHandle_t>handle)
194
212
 
195
213
 
196
214
  # ---------------------------------------------------------------------------
@@ -199,12 +217,18 @@ def hipFileHandleDeregister(uintptr_t handle):
199
217
 
200
218
  def hipFileBufRegister(uintptr_t buffer_base, size_t length, int flags=0):
201
219
  """Wrapper for ``hipFileBufRegister``."""
202
- return _err(_c.hipFileBufRegister(<const void *>buffer_base, length, flags))
220
+ cdef _c.hipFileError_t e
221
+ with nogil:
222
+ e = _c.hipFileBufRegister(<const void *>buffer_base, length, flags)
223
+ return _err(e)
203
224
 
204
225
 
205
226
  def hipFileBufDeregister(uintptr_t buffer_base):
206
227
  """Wrapper for ``hipFileBufDeregister``."""
207
- return _err(_c.hipFileBufDeregister(<const void *>buffer_base))
228
+ cdef _c.hipFileError_t e
229
+ with nogil:
230
+ e = _c.hipFileBufDeregister(<const void *>buffer_base)
231
+ return _err(e)
208
232
 
209
233
 
210
234
  # ---------------------------------------------------------------------------
@@ -222,14 +246,16 @@ def hipFileRead(uintptr_t handle, uintptr_t buffer_base, size_t size,
222
246
  ``-hipFileHipDriverError``, ``extra = hipError_t`` from
223
247
  ``hipPeekAtLastError()``, otherwise ``extra = 0``
224
248
  """
225
- cdef ssize_t ret = _c.hipFileRead(<_c.hipFileHandle_t>handle,
226
- <void *>buffer_base, size,
227
- file_offset, buffer_offset)
249
+ cdef ssize_t ret
228
250
  cdef int extra = 0
229
- if ret == -1:
230
- extra = errno
231
- elif ret == -<int>_c.hipFileHipDriverError:
232
- extra = <int>_c.hipPeekAtLastError()
251
+ with nogil:
252
+ ret = _c.hipFileRead(<_c.hipFileHandle_t>handle,
253
+ <void *>buffer_base, size,
254
+ file_offset, buffer_offset)
255
+ if ret == -1:
256
+ extra = errno
257
+ elif ret == -<int>_c.hipFileHipDriverError:
258
+ extra = <int>_c.hipPeekAtLastError()
233
259
  return (ret, extra)
234
260
 
235
261
 
@@ -244,14 +270,16 @@ def hipFileWrite(uintptr_t handle, uintptr_t buffer_base, size_t size,
244
270
  ``-hipFileHipDriverError``, ``extra = hipError_t`` from
245
271
  ``hipPeekAtLastError()``, otherwise ``extra = 0``
246
272
  """
247
- cdef ssize_t ret = _c.hipFileWrite(<_c.hipFileHandle_t>handle,
248
- <const void *>buffer_base, size,
249
- file_offset, buffer_offset)
273
+ cdef ssize_t ret
250
274
  cdef int extra = 0
251
- if ret == -1:
252
- extra = errno
253
- elif ret == -<int>_c.hipFileHipDriverError:
254
- extra = <int>_c.hipPeekAtLastError()
275
+ with nogil:
276
+ ret = _c.hipFileWrite(<_c.hipFileHandle_t>handle,
277
+ <const void *>buffer_base, size,
278
+ file_offset, buffer_offset)
279
+ if ret == -1:
280
+ extra = errno
281
+ elif ret == -<int>_c.hipFileHipDriverError:
282
+ extra = <int>_c.hipPeekAtLastError()
255
283
  return (ret, extra)
256
284
 
257
285
 
@@ -265,8 +293,10 @@ def hipFileDriverGetProperties():
265
293
  Returns ``(props_dict, error_tuple)``.
266
294
  """
267
295
  cdef _c.hipFileDriverProps_t props
268
- memset(&props, 0, sizeof(props))
269
- cdef _c.hipFileError_t e = _c.hipFileDriverGetProperties(&props)
296
+ cdef _c.hipFileError_t e
297
+ with nogil:
298
+ memset(&props, 0, sizeof(props))
299
+ e = _c.hipFileDriverGetProperties(&props)
270
300
  d = {
271
301
  "nvfs_major_version": props.nvfs_major_version,
272
302
  "nvfs_minor_version": props.nvfs_minor_version,
@@ -0,0 +1,133 @@
1
+ # Copyright (c) Advanced Micro Devices, Inc. All rights reserved.
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+
5
+ """GPU memory buffer registration for hipFile I/O."""
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import TYPE_CHECKING
10
+ from sys import stderr
11
+
12
+ from hipfile._hipfile import ( # pylint: disable=E0401,E0611
13
+ hipFileBufDeregister,
14
+ hipFileBufRegister,
15
+ )
16
+ from hipfile.error import HipFileException
17
+
18
+ if TYPE_CHECKING:
19
+ from ctypes import c_void_p
20
+ from types import TracebackType
21
+
22
+
23
+ class Buffer:
24
+ """Manage registration of a GPU memory buffer with hipFile.
25
+
26
+ ``Buffer`` does **not** own the underlying GPU allocation; it only
27
+ manages the hipFile registration lifetime.
28
+
29
+ Supports the context-manager protocol for automatic
30
+ ``register``/``deregister``::
31
+
32
+ with Buffer.from_ctypes_void_p(ptr, length, 0) as buf:
33
+ fh.read(buf, length, 0, 0)
34
+ """
35
+
36
+ @classmethod
37
+ def from_ctypes_void_p(
38
+ cls, ctypes_void_p: c_void_p, length: int, flags: int
39
+ ) -> Buffer:
40
+ """Create a ``Buffer`` from a ``ctypes.c_void_p``.
41
+
42
+ Parameters
43
+ ----------
44
+ ctypes_void_p : ctypes.c_void_p
45
+ Pointer to GPU memory. Must not be null.
46
+ length : int
47
+ Size of the buffer in bytes.
48
+ flags : int
49
+ Registration flags (pass ``0`` for default behavior).
50
+
51
+ Raises
52
+ ------
53
+ ValueError
54
+ If *ctypes_void_p* is null.
55
+ """
56
+ if ctypes_void_p.value is None:
57
+ raise ValueError("Cannot pass in a null pointer.")
58
+ return cls(ctypes_void_p.value, length, flags)
59
+
60
+ def __init__(self, buffer_ptr: int, length: int, flags: int) -> None:
61
+ """Initialize a ``Buffer`` from a raw integer pointer.
62
+
63
+ Parameters
64
+ ----------
65
+ buffer_ptr : int
66
+ Integer address of the GPU memory.
67
+ length : int
68
+ Size of the buffer in bytes.
69
+ flags : int
70
+ Registration flags (pass ``0`` for default behavior).
71
+ """
72
+ self._buffer_ptr = buffer_ptr
73
+ self._flags = flags
74
+ self._length = length
75
+ self._registered = False
76
+
77
+ def __del__(self) -> None:
78
+ """Deregister on garbage collection if still registered."""
79
+ # We did not create the underlying buffer. Don't try to free it.
80
+ try:
81
+ self.deregister()
82
+ except Exception: # pylint: disable=W0718 # Suppress exceptions in a dtor
83
+ print(
84
+ "Failed to deregister hipFile.Buffer at destruction time.", file=stderr
85
+ )
86
+
87
+ def __enter__(self) -> Buffer:
88
+ """Register the buffer and return *self*."""
89
+ self.register()
90
+ return self
91
+
92
+ def __exit__(
93
+ self,
94
+ exc_type: type[BaseException] | None,
95
+ exc: BaseException | None,
96
+ tb: TracebackType | None,
97
+ ) -> None:
98
+ """Deregister the buffer."""
99
+ self.deregister()
100
+
101
+ @property
102
+ def ptr(self) -> int:
103
+ """Integer address of the GPU memory."""
104
+ return self._buffer_ptr
105
+
106
+ def deregister(self) -> None:
107
+ """Deregister the buffer from the hipFile driver.
108
+
109
+ This is a no-op if the buffer is not currently registered.
110
+
111
+ Raises
112
+ ------
113
+ HipFileException
114
+ If the deregistration call fails.
115
+ """
116
+ if self._registered:
117
+ err = hipFileBufDeregister(self._buffer_ptr)
118
+ if err[0] != 0:
119
+ raise HipFileException(err[0], err[1])
120
+ self._registered = False
121
+
122
+ def register(self) -> None:
123
+ """Register the buffer with the hipFile driver.
124
+
125
+ Raises
126
+ ------
127
+ HipFileException
128
+ If the registration call fails.
129
+ """
130
+ err = hipFileBufRegister(self._buffer_ptr, self._length, self._flags)
131
+ if err[0] != 0:
132
+ raise HipFileException(err[0], err[1])
133
+ self._registered = True
@@ -0,0 +1,77 @@
1
+ # Copyright (c) Advanced Micro Devices, Inc. All rights reserved.
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+
5
+ """hipFile driver lifecycle management."""
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import TYPE_CHECKING
10
+
11
+ from hipfile._hipfile import ( # pylint: disable=E0401,E0611
12
+ hipFileDriverOpen,
13
+ hipFileDriverClose,
14
+ hipFileUseCount,
15
+ )
16
+ from hipfile.error import HipFileException
17
+
18
+ if TYPE_CHECKING:
19
+ from types import TracebackType
20
+
21
+
22
+ class Driver:
23
+ """Manage the hipFile driver lifecycle.
24
+
25
+ Wraps the driver open/close calls and supports the
26
+ context-manager protocol::
27
+
28
+ with Driver() as drv:
29
+ ... # driver is open
30
+ # driver is closed
31
+
32
+ The driver is reference-counted internally; multiple ``open``
33
+ calls are balanced by an equal number of ``close`` calls.
34
+ """
35
+
36
+ @staticmethod
37
+ def use_count() -> int:
38
+ """Return the current driver reference count."""
39
+ return hipFileUseCount()
40
+
41
+ def __enter__(self) -> Driver:
42
+ """Open the driver and return *self*."""
43
+ self.open()
44
+ return self
45
+
46
+ def __exit__(
47
+ self,
48
+ exc_type: type[BaseException] | None,
49
+ exc_value: BaseException | None,
50
+ traceback: TracebackType | None,
51
+ ) -> None:
52
+ """Close the driver."""
53
+ self.close()
54
+
55
+ def close(self) -> None:
56
+ """Close the hipFile driver.
57
+
58
+ Raises
59
+ ------
60
+ HipFileException
61
+ If the close call fails.
62
+ """
63
+ err = hipFileDriverClose()
64
+ if err[0] != 0:
65
+ raise HipFileException(err[0], err[1])
66
+
67
+ def open(self) -> None:
68
+ """Open the hipFile driver.
69
+
70
+ Raises
71
+ ------
72
+ HipFileException
73
+ If the open call fails.
74
+ """
75
+ err = hipFileDriverOpen()
76
+ if err[0] != 0:
77
+ raise HipFileException(err[0], err[1])
@@ -1,4 +1,9 @@
1
- # pylint: disable=C0114,C0115,C0116
1
+ # Copyright (c) Advanced Micro Devices, Inc. All rights reserved.
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+
5
+ """Enumerations mirroring hipFile C types."""
6
+
2
7
  from enum import IntEnum
3
8
 
4
9
  from hipfile._hipfile import ( # pylint: disable=E0401,E0611
@@ -0,0 +1,54 @@
1
+ # Copyright (c) Advanced Micro Devices, Inc. All rights reserved.
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+
5
+ """Exception type for hipFile operations."""
6
+
7
+ from __future__ import annotations
8
+
9
+ from hipfile._hipfile import hipFileGetOpErrorString # pylint: disable=E0401,E0611
10
+ from hipfile.enums import OpError
11
+
12
+
13
+ class HipFileException(Exception):
14
+ """Exception raised when a hipFile operation fails.
15
+
16
+ Wraps both the ``hipFileOpError_t`` code and, when the error is
17
+ ``HIP_DRIVER_ERROR``, the underlying ``hipError_t`` from the HIP
18
+ runtime.
19
+ """
20
+
21
+ def __init__(self, hipfile_err: int, hip_err: int) -> None:
22
+ """Initialize a ``HipFileException``.
23
+
24
+ Parameters
25
+ ----------
26
+ hipfile_err : int
27
+ ``hipFileOpError_t`` value describing the failure.
28
+ hip_err : int
29
+ ``hipError_t`` value from the HIP runtime. Only meaningful
30
+ when *hipfile_err* equals ``OpError.HIP_DRIVER_ERROR``.
31
+ """
32
+ self._hipfile_err = hipfile_err
33
+ self._hip_err = hip_err
34
+
35
+ @property
36
+ def hipfile_err(self) -> int:
37
+ """``hipFileOpError_t`` code for this error."""
38
+ return self._hipfile_err
39
+
40
+ @property
41
+ def hip_err(self) -> int:
42
+ """``hipError_t`` code from the HIP runtime.
43
+
44
+ Only meaningful when ``hipfile_err`` equals
45
+ ``OpError.HIP_DRIVER_ERROR``.
46
+ """
47
+ return self._hip_err
48
+
49
+ def __str__(self) -> str:
50
+ """Return a human-readable description of the error."""
51
+ err_msg = f"{self._hipfile_err} - {hipFileGetOpErrorString(self._hipfile_err)}"
52
+ if self._hipfile_err == OpError.HIP_DRIVER_ERROR:
53
+ err_msg += f" {self._hip_err}"
54
+ return err_msg
@@ -0,0 +1,271 @@
1
+ # Copyright (c) Advanced Micro Devices, Inc. All rights reserved.
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+
5
+ """GPU-accelerated file I/O through hipFile."""
6
+
7
+ from __future__ import annotations
8
+
9
+ import os
10
+ import stat
11
+ from sys import stderr
12
+ from typing import TYPE_CHECKING
13
+
14
+ from hipfile._hipfile import ( # pylint: disable=E0401,E0611
15
+ hipFileHandleRegister,
16
+ hipFileHandleDeregister,
17
+ hipFileRead,
18
+ hipFileWrite,
19
+ )
20
+ from hipfile.enums import FileHandleType
21
+ from hipfile.error import HipFileException
22
+
23
+ if TYPE_CHECKING:
24
+ from types import TracebackType
25
+
26
+ from hipfile.buffer import Buffer
27
+
28
+
29
+ class FileHandle:
30
+ """Manage a file descriptor registered with the hipFile driver.
31
+
32
+ Wraps ``os.open``/``os.close`` together with hipFile handle
33
+ registration so that GPU-accelerated reads and writes can be
34
+ performed through a single object.
35
+
36
+ Supports the context-manager protocol for automatic
37
+ ``open``/``close``::
38
+
39
+ with FileHandle(path, os.O_RDONLY | os.O_DIRECT) as fh:
40
+ fh.read(buf, size, 0, 0)
41
+ """
42
+
43
+ DEFAULT_MODE = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH # 0o644
44
+
45
+ def __init__(
46
+ self,
47
+ path: str | os.PathLike[str],
48
+ flags: int,
49
+ mode: int = DEFAULT_MODE,
50
+ handle_type: FileHandleType = FileHandleType.OPAQUE_FD,
51
+ ) -> None:
52
+ """Initialize a ``FileHandle``.
53
+
54
+ The file is **not** opened until ``open`` is called (or the
55
+ context manager is entered).
56
+
57
+ Parameters
58
+ ----------
59
+ path : str or os.PathLike[str]
60
+ Filesystem path to open.
61
+ flags : int
62
+ Flags passed to ``os.open`` (e.g. ``os.O_RDONLY | os.O_DIRECT``).
63
+ mode : int, optional
64
+ Permission bits used when creating a file. Defaults to
65
+ ``0o644``.
66
+ handle_type : FileHandleType, optional
67
+ Type of handle to register. Defaults to
68
+ ``FileHandleType.OPAQUE_FD``.
69
+ """
70
+ self._fd: int | None = None
71
+ self._flags = flags
72
+ self._handle: int | None = None
73
+ self._handle_type: FileHandleType | None = None
74
+ self._mode = mode
75
+ self._path = path
76
+
77
+ self.handle_type = handle_type
78
+
79
+ def __del__(self) -> None:
80
+ """Close on garbage collection if still open."""
81
+ try:
82
+ self.close()
83
+ except Exception: # pylint: disable=W0718 # Suppress exceptions in a dtor
84
+ print(
85
+ "Failed to deregister hipFile.FileHandle at destruction time.",
86
+ file=stderr,
87
+ )
88
+
89
+ def __enter__(self) -> FileHandle:
90
+ """Open the file and return *self*."""
91
+ self.open()
92
+ return self
93
+
94
+ def __exit__(
95
+ self,
96
+ exc_type: type[BaseException] | None,
97
+ exc_value: BaseException | None,
98
+ traceback: TracebackType | None,
99
+ ) -> None:
100
+ """Close the file."""
101
+ self.close()
102
+
103
+ @property
104
+ def flags(self) -> int:
105
+ """Flags passed to ``os.open``."""
106
+ return self._flags
107
+
108
+ @property
109
+ def handle(self) -> int | None:
110
+ """Opaque hipFile handle, or ``None`` if not open."""
111
+ return self._handle
112
+
113
+ @property
114
+ def handle_type(self) -> FileHandleType | None:
115
+ """Handle type used for registration."""
116
+ return self._handle_type
117
+
118
+ @handle_type.setter
119
+ def handle_type(self, _handle_type: FileHandleType) -> None:
120
+ """Set the handle type.
121
+
122
+ Raises
123
+ ------
124
+ RuntimeError
125
+ If the file handle is already open.
126
+ ValueError
127
+ If *_handle_type* is not a ``FileHandleType`` member.
128
+ NotImplementedError
129
+ If *_handle_type* is ``OPAQUE_WIN32``.
130
+ """
131
+ if self._handle is not None:
132
+ raise RuntimeError("Cannot modify handle_type while FileHandle is open")
133
+ if _handle_type not in FileHandleType:
134
+ raise ValueError(f"'{_handle_type}' is not a member of enum FileHandleType")
135
+ if _handle_type == FileHandleType.OPAQUE_WIN32:
136
+ raise NotImplementedError(
137
+ "FileHandle does not currently support Win32 Handles"
138
+ )
139
+ self._handle_type = _handle_type
140
+
141
+ @property
142
+ def mode(self) -> int:
143
+ """File creation permission bits."""
144
+ return self._mode
145
+
146
+ @property
147
+ def path(self) -> str | os.PathLike[str]:
148
+ """Filesystem path."""
149
+ return self._path
150
+
151
+ def open(self) -> None:
152
+ """Open the file and register it with the hipFile driver.
153
+
154
+ Raises
155
+ ------
156
+ RuntimeError
157
+ If the file handle is already open.
158
+ HipFileException
159
+ If hipFile handle registration fails.
160
+ """
161
+ if self._handle is not None:
162
+ raise RuntimeError("The FileHandle is already open.")
163
+ self._fd = os.open(self._path, self._flags, self._mode)
164
+ handle, err = hipFileHandleRegister(self._fd, self._handle_type)
165
+ if err[0] != 0:
166
+ os.close(self._fd)
167
+ self._fd = None
168
+ raise HipFileException(err[0], err[1])
169
+ self._handle = handle
170
+
171
+ def close(self) -> None:
172
+ """Deregister the hipFile handle and close the file descriptor.
173
+
174
+ Safe to call multiple times; subsequent calls are no-ops.
175
+ """
176
+ if self._handle is not None:
177
+ hipFileHandleDeregister(self._handle)
178
+ self._handle = None
179
+ if self._fd is not None:
180
+ os.close(self._fd)
181
+ self._fd = None
182
+
183
+ def read(
184
+ self, buffer: Buffer, size: int, file_offset: int, buffer_offset: int
185
+ ) -> int:
186
+ """Read from the file into a GPU buffer.
187
+
188
+ Parameters
189
+ ----------
190
+ buffer : Buffer
191
+ GPU buffer to read into.
192
+ size : int
193
+ Number of bytes to read.
194
+ file_offset : int
195
+ Byte offset within the file to start reading from.
196
+ buffer_offset : int
197
+ Byte offset within the GPU buffer to read into.
198
+
199
+ Returns
200
+ -------
201
+ int
202
+ Number of bytes actually read.
203
+
204
+ Raises
205
+ ------
206
+ RuntimeError
207
+ If the file handle is not open.
208
+ OSError
209
+ On a system-level I/O error (wraps ``errno``).
210
+ HipFileException
211
+ On a hipFile or HIP driver error.
212
+ """
213
+ if self._handle is None:
214
+ raise RuntimeError("The FileHandle is not open.")
215
+ bytes_read, extra_err = hipFileRead(
216
+ self._handle, buffer.ptr, size, file_offset, buffer_offset
217
+ )
218
+ if bytes_read == -1:
219
+ # extra_err is errno
220
+ raise OSError(extra_err, os.strerror(extra_err))
221
+ if bytes_read < -1:
222
+ # hipFile Error
223
+ # If -bytes_read == OpError.HIP_DRIVER_ERROR, extra_err is hipError_t.
224
+ # Otherwise, extra_err is 0.
225
+ raise HipFileException(-bytes_read, extra_err)
226
+ return bytes_read
227
+
228
+ def write(
229
+ self, buffer: Buffer, size: int, file_offset: int, buffer_offset: int
230
+ ) -> int:
231
+ """Write from a GPU buffer into the file.
232
+
233
+ Parameters
234
+ ----------
235
+ buffer : Buffer
236
+ GPU buffer to write from.
237
+ size : int
238
+ Number of bytes to write.
239
+ file_offset : int
240
+ Byte offset within the file to start writing to.
241
+ buffer_offset : int
242
+ Byte offset within the GPU buffer to write from.
243
+
244
+ Returns
245
+ -------
246
+ int
247
+ Number of bytes actually written.
248
+
249
+ Raises
250
+ ------
251
+ RuntimeError
252
+ If the file handle is not open.
253
+ OSError
254
+ On a system-level I/O error (wraps ``errno``).
255
+ HipFileException
256
+ On a hipFile or HIP driver error.
257
+ """
258
+ if self._handle is None:
259
+ raise RuntimeError("The FileHandle is not open.")
260
+ bytes_written, extra_err = hipFileWrite(
261
+ self._handle, buffer.ptr, size, file_offset, buffer_offset
262
+ )
263
+ if bytes_written == -1:
264
+ # extra_err is errno
265
+ raise OSError(extra_err, os.strerror(extra_err))
266
+ if bytes_written < -1:
267
+ # hipFile Error
268
+ # If -bytes_written == OpError.HIP_DRIVER_ERROR, extra_err is hipError_t.
269
+ # Otherwise, extra_err is 0.
270
+ raise HipFileException(-bytes_written, extra_err)
271
+ return bytes_written
@@ -1,4 +1,9 @@
1
+ # Copyright (c) Advanced Micro Devices, Inc. All rights reserved.
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+
1
5
  # pylint: disable=all
6
+
2
7
  """
3
8
  This is a hack to have some semblance of GPU memory management
4
9
  without introducing a dependency at this early stage of
@@ -28,6 +33,18 @@ _hip.hipFree.restype = ctypes.c_int
28
33
 
29
34
 
30
35
  def hipMalloc(size_bytes: int) -> ctypes.c_void_p:
36
+ """Allocate *size_bytes* of GPU device memory.
37
+
38
+ Returns
39
+ -------
40
+ ctypes.c_void_p
41
+ Pointer to the allocated device memory.
42
+
43
+ Raises
44
+ ------
45
+ RuntimeError
46
+ If the HIP runtime reports an error.
47
+ """
31
48
  d_ptr = ctypes.c_void_p()
32
49
  status = _hip.hipMalloc(ctypes.byref(d_ptr), ctypes.c_size_t(size_bytes))
33
50
  if status != 0:
@@ -36,6 +53,13 @@ def hipMalloc(size_bytes: int) -> ctypes.c_void_p:
36
53
 
37
54
 
38
55
  def hipFree(ptr: ctypes.c_void_p) -> None:
56
+ """Free GPU device memory previously allocated by ``hipMalloc``.
57
+
58
+ Raises
59
+ ------
60
+ RuntimeError
61
+ If the HIP runtime reports an error.
62
+ """
39
63
  status = _hip.hipFree(ptr)
40
64
  if status != 0:
41
65
  raise RuntimeError(f"hipFree failed ({status})")
@@ -0,0 +1,51 @@
1
+ # Copyright (c) Advanced Micro Devices, Inc. All rights reserved.
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+
5
+ """Query hipFile driver properties and version information."""
6
+
7
+ from __future__ import annotations
8
+
9
+ from hipfile._hipfile import ( # pylint: disable=E0401,E0611
10
+ hipFileDriverGetProperties,
11
+ hipFileGetVersion,
12
+ )
13
+ from hipfile.error import HipFileException
14
+
15
+
16
+ def driver_get_properties() -> dict[str, int]:
17
+ """Return the current hipFile driver properties.
18
+
19
+ Returns
20
+ -------
21
+ dict[str, int]
22
+ Property names mapped to their integer values.
23
+
24
+ Raises
25
+ ------
26
+ HipFileException
27
+ If the properties query fails.
28
+ """
29
+ _props, err = hipFileDriverGetProperties()
30
+ if err[0] != 0:
31
+ raise HipFileException(err[0], err[1])
32
+ return _props
33
+
34
+
35
+ def get_version() -> tuple[int, int, int]:
36
+ """Return the hipFile driver version.
37
+
38
+ Returns
39
+ -------
40
+ tuple[int, int, int]
41
+ ``(major, minor, patch)`` version components.
42
+
43
+ Raises
44
+ ------
45
+ HipFileException
46
+ If the version query fails.
47
+ """
48
+ version_tuple, err = hipFileGetVersion()
49
+ if err[0] != 0:
50
+ raise HipFileException(err[0], err[1])
51
+ return version_tuple
@@ -1,3 +1,7 @@
1
+ # Copyright (c) Advanced Micro Devices, Inc. All rights reserved.
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+
1
5
  """
2
6
  A quick & rough script for testing the Cython bindings to the
3
7
  hipFile C library. Reads a given file and copies it to an
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
4
4
 
5
5
  [project]
6
6
  name = "hipfile"
7
- version = "0.2.0.dev1"
7
+ version = "0.3.0.dev0"
8
8
  classifiers = [
9
9
  "Development Status :: 3 - Alpha",
10
10
  ]
@@ -1,59 +0,0 @@
1
- # pylint: disable=C0114,C0115,C0116
2
- from __future__ import annotations
3
- from typing import TYPE_CHECKING
4
- from sys import stderr
5
-
6
- from hipfile._hipfile import ( # pylint: disable=E0401,E0611
7
- hipFileBufDeregister,
8
- hipFileBufRegister,
9
- )
10
- from hipfile.error import HipFileException
11
-
12
- if TYPE_CHECKING:
13
- from ctypes import c_void_p
14
-
15
-
16
- class Buffer:
17
-
18
- @classmethod
19
- def from_ctypes_void_p(cls, ctypes_void_p: c_void_p, length, flags):
20
- return cls(ctypes_void_p.value, length, flags)
21
-
22
- def __init__(self, buffer_ptr, length, flags) -> None:
23
- self._buffer_ptr = buffer_ptr
24
- self._flags = flags
25
- self._length = length
26
- self._registered = False
27
-
28
- def __del__(self):
29
- # We did not create the underlying buffer. Don't try to free it.
30
- try:
31
- self.deregister()
32
- except Exception: # pylint: disable=W0718 # Suppress exceptions in a dtor
33
- print(
34
- "Failed to deregister hipFile.Buffer at destruction time.", file=stderr
35
- )
36
-
37
- def __enter__(self):
38
- self.register()
39
- return self
40
-
41
- def __exit__(self, exc_type, exc, tb):
42
- self.deregister()
43
-
44
- @property
45
- def ptr(self):
46
- return self._buffer_ptr
47
-
48
- def deregister(self):
49
- if self._registered:
50
- err = hipFileBufDeregister(self._buffer_ptr)
51
- if err[0] != 0:
52
- raise HipFileException(err[0], err[1])
53
- self._registered = False
54
-
55
- def register(self):
56
- err = hipFileBufRegister(self._buffer_ptr, self._length, self._flags)
57
- if err[0] != 0:
58
- raise HipFileException(err[0], err[1])
59
- self._registered = True
@@ -1,31 +0,0 @@
1
- # pylint: disable=C0114,C0115,C0116
2
- from hipfile._hipfile import ( # pylint: disable=E0401,E0611
3
- hipFileDriverOpen,
4
- hipFileDriverClose,
5
- hipFileUseCount,
6
- )
7
- from hipfile.error import HipFileException
8
-
9
-
10
- class Driver:
11
-
12
- @staticmethod
13
- def use_count():
14
- return hipFileUseCount()
15
-
16
- def __enter__(self):
17
- self.open()
18
- return self
19
-
20
- def __exit__(self, exc_type, exc_value, traceback):
21
- self.close()
22
-
23
- def close(self):
24
- err = hipFileDriverClose()
25
- if err[0] != 0:
26
- raise HipFileException(err[0], err[1])
27
-
28
- def open(self):
29
- err = hipFileDriverOpen()
30
- if err[0] != 0:
31
- raise HipFileException(err[0], err[1])
@@ -1,23 +0,0 @@
1
- # pylint: disable=C0114,C0115,C0116
2
- from hipfile._hipfile import hipFileGetOpErrorString # pylint: disable=E0401,E0611
3
- from hipfile.enums import OpError
4
-
5
-
6
- class HipFileException(Exception):
7
- def __init__(self, hipfile_err, hip_err):
8
- self._hipfile_err = hipfile_err
9
- self._hip_err = hip_err
10
-
11
- @property
12
- def hipfile_err(self):
13
- return self._hipfile_err
14
-
15
- @property
16
- def hip_err(self):
17
- return self._hip_err
18
-
19
- def __str__(self):
20
- err_msg = f"{self._hipfile_err} - {hipFileGetOpErrorString(self._hipfile_err)}"
21
- if self._hipfile_err == OpError.HIP_DRIVER_ERROR:
22
- err_msg += f" {self._hip_err}"
23
- return err_msg
@@ -1,128 +0,0 @@
1
- # pylint: disable=C0114,C0115,C0116
2
- import os
3
- import stat
4
- from sys import stderr
5
-
6
- from hipfile._hipfile import ( # pylint: disable=E0401,E0611
7
- hipFileHandleRegister,
8
- hipFileHandleDeregister,
9
- hipFileRead,
10
- hipFileWrite,
11
- )
12
- from hipfile.enums import FileHandleType
13
- from hipfile.error import HipFileException
14
-
15
-
16
- class FileHandle:
17
- DEFAULT_MODE = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH
18
-
19
- def __init__(
20
- self, path, flags, mode=DEFAULT_MODE, handle_type=FileHandleType.OPAQUE_FD
21
- ):
22
- self._fd = None
23
- self._flags = flags
24
- self._handle = None
25
- self._handle_type = None
26
- self._mode = mode
27
- self._path = path
28
-
29
- self.handle_type = handle_type
30
-
31
- def __del__(self):
32
- try:
33
- self.close()
34
- except Exception: # pylint: disable=W0718 # Suppress exceptions in a dtor
35
- print(
36
- "Failed to deregister hipFile.FileHandle at destruction time.",
37
- file=stderr,
38
- )
39
-
40
- def __enter__(self):
41
- self.open()
42
- return self
43
-
44
- def __exit__(self, exc_type, exc_value, traceback):
45
- self.close()
46
-
47
- @property
48
- def flags(self):
49
- return self._flags
50
-
51
- @property
52
- def handle(self):
53
- return self._handle
54
-
55
- @property
56
- def handle_type(self):
57
- return self._handle_type
58
-
59
- @handle_type.setter
60
- def handle_type(self, _handle_type):
61
- if self._handle is not None:
62
- raise RuntimeError("Cannot modify handle_type while FileHandle is open")
63
- if _handle_type not in FileHandleType:
64
- raise ValueError(f"'{_handle_type}' is not a member of enum FileHandleType")
65
- if _handle_type == FileHandleType.OPAQUE_WIN32:
66
- raise NotImplementedError(
67
- "FileHandle does not currently support Win32 Handles"
68
- )
69
- self._handle_type = _handle_type
70
-
71
- @property
72
- def mode(self):
73
- return self._mode
74
-
75
- @property
76
- def path(self):
77
- return self._path
78
-
79
- def open(self):
80
- if self._handle is not None:
81
- raise RuntimeError("The FileHandle is already open.")
82
- self._fd = os.open(self._path, self._flags, self._mode)
83
- handle, err = hipFileHandleRegister(self._fd, self._handle_type)
84
- if err[0] != 0:
85
- os.close(self._fd)
86
- self._fd = None
87
- raise HipFileException(err[0], err[1])
88
- self._handle = handle
89
-
90
- def close(self):
91
- if self._handle is not None:
92
- hipFileHandleDeregister(self._handle)
93
- self._handle = None
94
- if self._fd is not None:
95
- os.close(self._fd)
96
- self._fd = None
97
-
98
- def read(self, buffer, size, file_offset, buffer_offset):
99
- if self._handle is None:
100
- raise RuntimeError("The FileHandle is not open.")
101
- bytes_read, extra_err = hipFileRead(
102
- self._handle, buffer.ptr, size, file_offset, buffer_offset
103
- )
104
- if bytes_read == -1:
105
- # extra_err is errno
106
- raise OSError(extra_err, os.strerror(extra_err))
107
- if bytes_read < -1:
108
- # hipFile Error
109
- # If -bytes_read == OpError.HIP_DRIVER_ERROR, extra_err is hipError_t.
110
- # Otherwise, extra_err is 0.
111
- raise HipFileException(-bytes_read, extra_err)
112
- return bytes_read
113
-
114
- def write(self, buffer, size, file_offset, buffer_offset):
115
- if self._handle is None:
116
- raise RuntimeError("The FileHandle is not open.")
117
- bytes_written, extra_err = hipFileWrite(
118
- self._handle, buffer.ptr, size, file_offset, buffer_offset
119
- )
120
- if bytes_written == -1:
121
- # extra_err is errno
122
- raise OSError(extra_err, os.strerror(extra_err))
123
- if bytes_written < -1:
124
- # hipFile Error
125
- # If -bytes_written == OpError.HIP_DRIVER_ERROR, extra_err is hipError_t.
126
- # Otherwise, extra_err is 0.
127
- raise HipFileException(-bytes_written, extra_err)
128
- return bytes_written
@@ -1,20 +0,0 @@
1
- # pylint: disable=C0114,C0116
2
- from hipfile._hipfile import ( # pylint: disable=E0401,E0611
3
- hipFileDriverGetProperties,
4
- hipFileGetVersion,
5
- )
6
- from hipfile.error import HipFileException
7
-
8
-
9
- def driver_get_properties():
10
- _props, err = hipFileDriverGetProperties()
11
- if err[0] != 0:
12
- raise HipFileException(err[0], err[1])
13
- return _props
14
-
15
-
16
- def get_version():
17
- version_tuple, err = hipFileGetVersion()
18
- if err[0] != 0:
19
- raise HipFileException(err[0], err[1])
20
- return version_tuple
File without changes