Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,22 @@

## Unreleased

### Added

* The hipFFT Xt API in `hipfort_hipfft`, as `bind(C)` interfaces: the multi-GPU
routines (`hipfftXtSetGPUs`, `hipfftXtMalloc`, `hipfftXtMemcpy`, `hipfftXtFree`,
the `hipfftXtExecDescriptor*` family), `hipfftXtMakePlanMany`,
`hipfftXtGetSizeMany` and `hipfftXtExec` on `hipDataType`, and the callback
routines. The `hipXtDesc` and `hipLibXtDesc` types are in the new
`hipfort_hipfft_types` module, and the Xt enums in `hipfort_hipfft_enums`.
On AMD GPUs the descriptor routines need a plan spread over two or more GPUs,
and a multi-GPU plan writes one work size per GPU. The multi-process routines
of `hipfftMp.h` (`hipfftMpAttachComm`, `hipfftXtSetDistribution`,
`hipfftXtSetSubformatDefault`) are bound too, but exist only in a hipFFT built
with MPI support, which the ROCm packages are not. On the CUDA backend the Xt
and multi-process routines are not available. The hipFFT examples document
the multi-GPU and MPI workflows.

### Changed

* **Breaking.** The array overloads of `hipMalloc`, `hipMallocManaged`,
Expand Down
21 changes: 21 additions & 0 deletions docs/doxygen/input/supported_api_hipfft.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,24 @@
32 | [hipfftDestroy](interfacehipfort__hipfft_1_1hipfftdestroy.html "Interface documentation") | C binding
33 | [hipfftGetVersion](interfacehipfort__hipfft_1_1hipfftgetversion.html "Interface documentation") | C binding, typed
34 | [hipfftGetProperty](interfacehipfort__hipfft_1_1hipfftgetproperty.html "Interface documentation") | C binding, typed
35 | [hipfftXtSetCallback](interfacehipfort__hipfft_1_1hipfftxtsetcallback.html "Interface documentation") | C binding
36 | [hipfftXtClearCallback](interfacehipfort__hipfft_1_1hipfftxtclearcallback.html "Interface documentation") | C binding
37 | [hipfftXtSetCallbackSharedSize](interfacehipfort__hipfft_1_1hipfftxtsetcallbacksharedsize.html "Interface documentation") | C binding
38 | [hipfftXtSetJITCallback](interfacehipfort__hipfft_1_1hipfftxtsetjitcallback.html "Interface documentation") | C binding
39 | [hipfftXtMakePlanMany](interfacehipfort__hipfft_1_1hipfftxtmakeplanmany.html "Interface documentation") | C binding, typed
40 | [hipfftXtGetSizeMany](interfacehipfort__hipfft_1_1hipfftxtgetsizemany.html "Interface documentation") | C binding, typed
41 | [hipfftXtExec](interfacehipfort__hipfft_1_1hipfftxtexec.html "Interface documentation") | C binding
42 | [hipfftXtSetGPUs](interfacehipfort__hipfft_1_1hipfftxtsetgpus.html "Interface documentation") | C binding
43 | [hipfftXtMalloc](interfacehipfort__hipfft_1_1hipfftxtmalloc.html "Interface documentation") | C binding
44 | [hipfftXtMemcpy](interfacehipfort__hipfft_1_1hipfftxtmemcpy.html "Interface documentation") | C binding
45 | [hipfftXtFree](interfacehipfort__hipfft_1_1hipfftxtfree.html "Interface documentation") | C binding
46 | [hipfftXtExecDescriptorC2C](interfacehipfort__hipfft_1_1hipfftxtexecdescriptorc2c.html "Interface documentation") | C binding
47 | [hipfftXtExecDescriptorR2C](interfacehipfort__hipfft_1_1hipfftxtexecdescriptorr2c.html "Interface documentation") | C binding
48 | [hipfftXtExecDescriptorC2R](interfacehipfort__hipfft_1_1hipfftxtexecdescriptorc2r.html "Interface documentation") | C binding
49 | [hipfftXtExecDescriptorZ2Z](interfacehipfort__hipfft_1_1hipfftxtexecdescriptorz2z.html "Interface documentation") | C binding
50 | [hipfftXtExecDescriptorD2Z](interfacehipfort__hipfft_1_1hipfftxtexecdescriptord2z.html "Interface documentation") | C binding
51 | [hipfftXtExecDescriptorZ2D](interfacehipfort__hipfft_1_1hipfftxtexecdescriptorz2d.html "Interface documentation") | C binding
52 | [hipfftXtExecDescriptor](interfacehipfort__hipfft_1_1hipfftxtexecdescriptor.html "Interface documentation") | C binding
53 | [hipfftMpAttachComm](interfacehipfort__hipfft_1_1hipfftmpattachcomm.html "Interface documentation") | C binding
54 | [hipfftXtSetDistribution](interfacehipfort__hipfft_1_1hipfftxtsetdistribution.html "Interface documentation") | C binding
55 | [hipfftXtSetSubformatDefault](interfacehipfort__hipfft_1_1hipfftxtsetsubformatdefault.html "Interface documentation") | C binding
108 changes: 108 additions & 0 deletions docs/tutorials/hipfft-examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,111 @@ input harmonics so that a swapped stream would be visible in the output.

.. literalinclude:: ../../test/f2008/hipfft/hipfft_setstream_z.f08
:language: fortran

Transforms on hipDataType with the Xt API
=========================================

``hipfftXtMakePlanMany`` describes a batched transform like
``hipfftMakePlanMany``, but takes 64-bit lengths, strides and distances, and
names the input, output and execution data types with ``hipDataType`` values
(``HIP_C_32F``, ``HIP_C_64F``, ``HIP_R_32F``, ...) instead of a transform type.
``hipfftXtGetSizeMany`` returns the work area size for the same parameters, and
``hipfftXtExec`` runs the plan whatever its data types, taking the direction for
complex transforms.

.. literalinclude:: ../../test/f2003/hipfft/hipfft_xt_makeplanmany_c.f03
:language: fortran

Multi-GPU transforms
====================

A plan can be spread over several GPUs of the same process. The data then lives
in a descriptor that hipFFT allocates and distributes, rather than in a buffer
of yours:

#. Create an empty plan with ``hipfftCreate``, and list the GPUs with
``hipfftXtSetGPUs`` **before** making it.
#. Make the plan with ``hipfftMakePlan2d``, ``hipfftMakePlan3d`` or
``hipfftMakePlanMany``. A multi-GPU plan writes one work area size per GPU,
so pass the first element of an ``integer(c_size_t)`` array with one slot
per GPU.
#. Allocate the data with ``hipfftXtMalloc``. It returns the address of a
``hipLibXtDesc`` in a ``type(c_ptr)``.
#. Copy the host data in and out with ``hipfftXtMemcpy``, passing that
``type(c_ptr)`` on the device side, and run the transform with the
``hipfftXtExecDescriptor`` routine matching the plan type
(``hipfftXtExecDescriptorZ2Z``, ``hipfftXtExecDescriptorC2C``, ...).
#. Release the descriptor with ``hipfftXtFree``, then the plan with
``hipfftDestroy``.

The descriptor is owned by hipFFT. Associate a ``type(hipLibXtDesc), pointer``
with the address ``hipfftXtMalloc`` returned, using ``c_f_pointer``, and pass
that pointer to ``hipfftXtExecDescriptor*`` and ``hipfftXtFree``. Never pass a
copy of the type: ``hipfftXtFree`` would then release the wrong address. The
``descriptor`` field leads in the same way to a ``hipXtDesc``, whose ``nGPUs``,
``GPUs``, ``data`` and ``size`` fields say where each part of the data is.

The ``hipLibXtDesc`` and ``hipXtDesc`` types are in the
``hipfort_hipfft_types`` module, which ``hipfort_hipfft`` uses, and the Xt
constants (``HIPFFT_XT_FORMAT_INPLACE``, ``HIPFFT_COPY_HOST_TO_DEVICE``, ...)
are in ``hipfort_hipfft_enums``.

On AMD GPUs a descriptor needs a plan over two or more GPUs, and unbatched
one-dimensional transforms are not supported. This program runs a
two-dimensional transform on two GPUs, forward then inverse, and checks the
result against the input; on a machine with fewer than two GPUs it reports so
and passes.

.. literalinclude:: ../../test/f2003/hipfft/hipfft_xt_descriptor_z.f03
:language: fortran

Multi-process transforms with MPI
=================================

``hipfftMpAttachComm``, ``hipfftXtSetDistribution`` and
``hipfftXtSetSubformatDefault`` distribute a transform over the ranks of an MPI
communicator. They are bound in ``hipfort_hipfft``, but exist only in a hipFFT
built with MPI support (``HIPFFT_MPI_ENABLE``), which the ROCm packages are
not: with those, a program that calls them does not link.

``hipfftMpAttachComm`` takes a ``type(c_ptr)`` to a **C** ``MPI_Comm``, not a
Fortran communicator handle. The size and the representation of ``MPI_Comm``
depend on the MPI implementation, so build it in C from the Fortran handle with
``MPI_Comm_f2c``, for example with this helper:

.. code-block:: c

#include <mpi.h>
#include <stdlib.h>

/* The C communicator for a Fortran one, in storage the caller frees. */
void* c_comm_from_fortran(MPI_Fint fcomm)
{
MPI_Comm* comm = malloc(sizeof *comm);
*comm = MPI_Comm_f2c(fcomm);
return comm;
}

.. code-block:: fortran

use iso_c_binding
use mpi_f08
use hipfort_check
use hipfort_hipfft

interface
function c_comm_from_fortran(fcomm) bind(c, name="c_comm_from_fortran")
import :: c_ptr, c_int
integer(c_int), value :: fcomm
type(c_ptr) :: c_comm_from_fortran
end function
end interface

type(c_ptr) :: comm

comm = c_comm_from_fortran(MPI_COMM_WORLD%MPI_VAL)
call hipfftCheck(hipfftMpAttachComm(plan, HIPFFT_COMM_MPI, comm))

hipFFT keeps the pointer rather than a copy of the communicator, so the storage
it points to has to outlive the plan. The plan must be attached to the
communicator before it is made.
Loading