HIP Python’s Adapter Types#
Advanced Micro Devices, Inc.
2023-06-23
10 min read
This chapter highlights some of the datatypes that HIP Python’s Python interfaces use to convert various Python objects to the C datatypes expected by the routines of the HIP C API.
While generic types are defined in module hip._util.types
,
specific helper types for hip.hip
are defined in hip._hip_helpers
.
Pointer
#
Whenever a C function signature argument of pointer type is too generic — e.g., already if it is a C void *
pointer — HIP Python’s code generator uses the types.Pointer
adapter to convert
a select list of Python objects to the pointer type expected by the C function:
None
:This will set the adapter’s pointer attribute to
NULL
.-
The adapter copies the pointer attribute. Python buffer handle ownership, if the input object has acquired one, is not transferred.
int
:The adapter interprets the
int
value as address and copies to its pointer attribute.-
Copies the
value
member. object
that implements the CUDA Array Interface protocol:Retrieves the address from the
"data"
tuple of the__cuda_array_interface
member of the object.object
that implements the Python buffer protocol:If the object represents a simple contiguous array, the adapter acquires the buffer handle and retrieves the buffer. Releases the handle at time of destruction.
Type checks are performed in the above order.
Usage in HIP Python#
The type is typically used as argument in the following scenarios:
When modeling generic first-degree pointers of basic type — however, typically not for (
const
)char *
— or generic first-degree pointers of typevoid
type.When modeling in-out scalar arguments, where the user supplies
ctypes.addressof(myscalar)
as argument.As place-holder whenever a complicated type expression of pointer type has not been analyzed yet.
DeviceArray
#
This is a datatype for handling device buffers returned by ~.hipMalloc
and related device memory allocation routines.
It implements the CUDA Array Interface protocol.
In case it appears as the type of a function argument, it can also be initialized from the following Python objects that you can pass instead of it:
None
:This will set the type’s pointer attribute to
NULL
. No shape and type information is available in this case!object
that is accepted as input by the__init__
routine ofPointer
:In this case, init code from
Pointer
is used.Py_buffer
object ownership is not transferred See__init__
for more information. No shape and type information is available in this case!int
:The type interprets the
int
value as address and copies to its pointer attribute. No shape and type information is available in this case!-
The type takes the pointer address from
pyobj.value
. No shape and type information is available in this case! object
with__cuda_array_interface__
member:The type takes the integer-valued pointer address, i.e. the first entry of the
data
tuple frompyobj
’s member__cuda_array_interface__
. Copies shape and type information.
Type checks are performed in the above order.
Note
Shape and type information and other metadata can be modified or overwritten after creation via the configure
member function. be aware that you might need to pass the _force=True
keyword argument —
in particular if your instance was created from a type that does not implement the
CUDA Array Interface protocol
or via a HIP Python device memory allocation routine.
See
Usage in HIP Python#
The type is used as return value of:
It can be passed to functions that expect a Pointer
argument and where passing an instance of this type instead makes sense, e.g.
you can pass it as copy destination or copy source in hipMemcpy
.
Note
The example HIP Python Device Arrays
showcases how to use ~.DeviceArray.configure
to change the shape and type
of a DeviceArray
instance and how use the []
operator
to retrieve a subarray.
List Types#
The adapter types
are used for simple Python list
or tuple
objects whose elements are
can be used to construct a
Pointer
,or can be converted to the C types
int
,unsigned
,unsigned long
, respectively.
The types can be initialized from the following Python objects:
list
/tuple
of the respective element type:In this case, adapter type allocates an array of C values wherein it stores the addresses/values obtained from the
list
/tuple
entries. Furthermore, the instance’s owner flag is set in this case.object
that is accepted as input by the__init__
routine ofPointer
:In this case, init code from
Pointer
is used and the C owner flag remains unset. SeePointer
for more information.
Usage in HIP Python#
HIP Python employs the ListOfBytes
type in scenarios
were a list of C const char *
is expected,
and the ListOfPointer
type where a list of C pointer types is expected.
The type ListOfBytes
is for example used to convert input Python types in the following routines:
hiprtcCompileProgram
for argumentoptions
,hiprtcCreateProgram
for argumentheaders
, andhiprtcCreateProgram
for argumentincludeNames
.
HIP Python functions that expect a C array of C int
, C unsigned
, or C unsigned long
element type (int *
, …)
use ListOfInt
, ListOfUnsigned
, or ListOfUnsignedLong
,
respectively, to handle the conversion from appropriate Python input types.
Note
In the examples Launching Kernels and Kernels with Arguments,
we can pass lists directly to hiprtcCreateProgram
and hiprtcCompileProgram
thanks to the type conversions performed by ListOfBytes
.
Helper type for hipModuleLaunchKernel
#
The type HipModuleLaunchKernel_extra
is a datatype for handling Python list or tuple objects with entries that
are either ctypes datatypes or that can be converted to type Pointer.
This adapter type is used for converting Python objects into a shape
that is expected by the extra
argument of HipModuleLaunchKernel
The type HipModuleLaunchKernel_extra
used as similar init procedure
as the types presented in List Types.
Note
In the example Kernels with Arguments,
we can pass lists of ctypes
types and DeviceArray
directly to hipModuleLaunchKernel
thanks to the type conversions performed by HipModuleLaunchKernel_extra
.
Autogenerated Wrapper Types#
HIP Python’s code generator autogenerates Python wrapper types
for records (C struct
and union
) and C function pointers.
As the other types presented in this chapter, these types can be initialized from different Python objects — but only if and only if they serve as input adapter for a HIP Python function.
Their initialization from within Python code differs with respect to the other discussed types. Instead of passing a Python object, you can pass initial values of their properties — either as positional arguments, keyword arguments, or a combination of both — to the constructor.
Example (Initialization of dim3
in Python code)
In the example Kernels with Arguments, the autogenerated type dim3
is initialized
using keyword arguments and positional arguments, respectively, as shown below:
block = hip.dim3(x=32)
grid = hip.dim3(math.ceil(n/block.x))
If autogenerated types are used as adapter for a function argument, the following input types are also accepted instead of passing an instance of the type:
None
:This will set the type’s pointer attribute to
NULL
.int
:The type interprets the
int
value as address and copies to its pointer attribute.-
Takes the pointer address
pyobj.value
and writes it to its pointer attribute. object
that implements the CUDA Array Interface protocol:Takes the integer-valued pointer address, i.e. the first entry of the
data
tuple frompyobj
’s member__cuda_array_interface__
and writes it to its pointer attribute.(Input type is not allowed for function pointer wrapper types.)
object
that implements the Python buffer protocol:If the object represents a simple contiguous array, the adapter acquires the buffer handle, retrieves the buffer writes the buffer’s data address to its pointer attribute. It releases the handle again at time of destruction.
(Input type is not allowed for function pointer wrapper types.)
-
Writes the data pointer of the
~.types.Pointer
instance to its own data pointer attribute. No ownership is transferred.
Type checks are performed in the above order.
Properties of the Adapter Types#
Finally, we want to highlight some important properties that are shared by all adapter types presented in this chapter:
All datatypes presented in this chapter are subclasses of type
Pointer
. Therefore, they can all be passed to functions that usePointer
for type conversions for the given argument; read the section Pointer again to understand why.Due to the way subclasses are initialized — the init procedure of
Pointer
is invoked if the specialized conversions were not applied (see DeviceArray and List Types) —Pointer
and any of its subclasses can be passed to any function that uses a subclass ofPointer
as adapter.You can instantiate all of the datatypes presented in this chapter in Python and Cython code.
You can subclass all of the datatypes presented in this chapter in Python and Cython code.
You can always use
int(<adapter_obj>)
to obtain the address of the underlying data as integer.(Hint: Print the integer address via Python routine
hex
to obtain an address that you can compare with AMD log output. AMD log output can be enabled via environment variableAMD_LOG_LEVEL
. Set it to a value of at least3
.)You can always pass an
int
to functions that use an adapter type for translating the given argument. It will be interpreted as integer representation of an address.
Implications#
Due to the first two properties, you can use one of the subclasses as adapter and then pass the
object to a HIP Python function that uses Pointer
or one of its subclasses as converter:
err = hip.hipMyFunc(...,pointer_is_used_to_convert_this_arg=hip._util.types.ListOfInt([1,2,3]),...)
Due to properties 3 and 4, you can define your own adapter types and pass them to HIP Python’s
functions wherever those use Pointer
or one of its subclasses as converter.
Obviously, your adapter must create data or link to data that makes sense for the C function
wrapped by HIP Python’s Python interfaces.