HIP Python’s Adapter Types#

Applies to Linux

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.

  • Pointer:

    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.

  • ctypes.c_void_p:

    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 type void 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 of Pointer:

    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!

  • ctypes.c_void_p:

    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 from pyobj’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

configure

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

  • bytes,

  • 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 of Pointer:

    In this case, init code from Pointer is used and the C owner flag remains unset. See Pointer 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:

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.

  • ctypes.c_void_p:

    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 from pyobj’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.)

  • Pointer:

    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:

  1. All datatypes presented in this chapter are subclasses of type Pointer. Therefore, they can all be passed to functions that use Pointer for type conversions for the given argument; read the section Pointer again to understand why.

  2. 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 of Pointer as adapter.

  3. You can instantiate all of the datatypes presented in this chapter in Python and Cython code.

  4. You can subclass all of the datatypes presented in this chapter in Python and Cython code.

  5. 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 variable AMD_LOG_LEVEL. Set it to a value of at least 3.)

  6. 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:

Listing 27 HIP Python Datatype Used as Adapter in Python Code#
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.