Skip to content

gpu-direct: make it optional and have fallback solution with device-host explicit transfers - #389

Open
svalat wants to merge 33 commits into
idefix-code:developfrom
svalat:feature/no-gpu-direct-support
Open

gpu-direct: make it optional and have fallback solution with device-host explicit transfers#389
svalat wants to merge 33 commits into
idefix-code:developfrom
svalat:feature/no-gpu-direct-support

Conversation

@svalat

@svalat svalat commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

Handle when there is no-gpu-direct

The patch mostly wrap the MPI interface to make the transfers manually between the host & device when GPU direct is not available.

In order to avoid allocating an host buffer for every transfers, the IdefixArrayXD classes has been extended as IdefixCommArrayXD which contains the standard array on device, and optionally (if GPU direct is disabled and if running on GPU) a host buffer to make the communications.

In order to apply in the code, all the MPI communication calls (MPI_Send, MPI_Recv, MPI_Start....) need to be replaced by the idefix::MPI_* corresponding frunctions which expect an IdefixCommArray as parameter.

It means that we should not continue to use .data() to feed the MPI calls (the compiler will complain for the idefix::MPI_* functions).

When the wrapper is called it ensure to sync the communication buffer in & out during the communication process so it is transparent for the calling code.

One care

You should not use directly the IdefixCommArrayXDin a GPU loop (get a compiler warning in case due to host array in use). This is way the patch uses the .deviceView() to get it casted in a friendly way for the GPU.

Note: in case you still keep it that way, it should not be a problem appart the warning in the compilation output because the host array will not be used in the kernel because not accessible (private).

Note: this semantic is only used if we communicate with the buffer, all the rest of the code using standard IdefixArray is not concerned.

Case of persitent comms

For the persistent comms and async comms, an new idefix::MPI_Request_XD has been defined to carry the information on how to sync the communication buffer with its device counter part when required. Again if you use idefix::MPI_* just follow the compiler which will force you to type correctly using idefix::MPI_Request_XD instead of MPI_Request.

New CMake flags :

  • Idefix_MPI_GPU_DIRECT : Enable the fallback imlpementation at compile time (will apply only if at runtime we cannot GPU direct and if we run on GPU.

Variable marked as advanced (not seen by default in ccmake) :

  • Idefix_MPI_GPU_FORCE_COPY : For validation purpose, force the copy in any situation (even on CPU). It should come in conjunition with Idefix_MPI_GPU_DIRECT to have an effect.
  • Idefix_MPI_MODE: To select a compile time via cmake which communication mode to use. Can be Persistent, Blocking, NonBlocking.

Validation

In order to validate the implementation in any condition, we can :

  1. Disable GPU direct with the CMake variable : Idefix_MPI_GPU_DIRECT.
  2. Force the copy on communications in any case via : Idefix_MPI_GPU_FORCE_COPY.

This is done on the test suite by using the -mpiGpuForceCopy option.

PYTHONPATH=$PWD ./test.py -subdir ./test/MHD/ -k "mpi" -mpiGpuForceCopy -cuda

It can be played in conjuntion with the -mpiMode option which can be either : Persistent, NonBlocking, BLocking to validate the various paths.

Validation in github actions

The test suite has been extended to test automatically those combinations on the test : MHD/AxisFluxTube.

When having GPU direct

in this case, the IdefixCommArrayGpuDirect implementation is used, which is just an empty stub which just does nothing.

Questions

  1. When making a MPI_waitall with multiple comms, I start all the host-device transfers asynchronously and using Kokkos::fence(). I can also avoid it to get simpler code as we have max 2 comms at this location.
  2. I currently named the MPI_* API the same inside the idefix:: namespace, we can also name them all MPI_View_ to clearly separate the concept. To be discussed what is best.
  3. I patched Mpi::CheckConfig() so it uses the copies when not having GPU Direct, is it what we want ?

TODO

  • Fix the issue on Axis cases
  • Validate all configuration (mpi sync/async/persistent)
  • Validate on a non gpu-direct condition.
  • Some minor cleanup

svalat added 17 commits July 8, 2026 15:34
…e IdefixCommArrayXD<> to handle the no gpu-direct transfers.
@svalat
svalat marked this pull request as draft July 10, 2026 15:21
@glesur
glesur self-requested a review August 18, 2026 07:28
@svalat
svalat force-pushed the feature/no-gpu-direct-support branch from 1e989f8 to 5431e11 Compare August 18, 2026 08:28
@svalat
svalat force-pushed the feature/no-gpu-direct-support branch from 04913f8 to f24de7d Compare August 19, 2026 09:05
@svalat
svalat marked this pull request as ready for review August 20, 2026 08:12
@svalat
svalat force-pushed the feature/no-gpu-direct-support branch from 001c28a to e5b4522 Compare August 20, 2026 13:13
@svalat
svalat force-pushed the feature/no-gpu-direct-support branch from e5b4522 to 43d4040 Compare August 20, 2026 13:13
@svalat
svalat force-pushed the feature/no-gpu-direct-support branch from c3991ca to a29cbd1 Compare August 20, 2026 13:24

@glesur glesur left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a few relatively minor things.

What is unclear at this stage is whether we want to accept all possible input data for MPI function, and not just the ones involving IdefixCommArray?

Comment thread doc/source/reference/makefile.rst Outdated
Comment thread doc/source/faq.rst Outdated
Comment thread src/mpi/commArray.hpp

#include "idefix.hpp"

namespace idefix {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
namespace idefix {
namespace idfx {

IdefixArray2D<real> sbEyL;
IdefixArray2D<real> sbEyR;
IdefixArray2D<real> sbEyRL;
idefix::IdefixCommArray2D<real> sbEyL;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

name space should be idfx:: (like cout, and other global functionalities), or idfxMPI:: (to highlight this is our own implementation)

Comment thread src/mpi/commArray.hpp Outdated
Comment thread src/mpi/mpi.cpp
#endif
#if defined(KOKKKOS_ENABLE_CUDA) || defined(KOKKOS_ENABLE_HIP)
errmsg << "You can look on Idefix cmake option -DIdefix_MPI_GPU_DIRECT=OFF to not"
"use it (will be slower)." << std::endl;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"use it (will be slower)." << std::endl;

Comment thread src/output/vtk.cpp
MPI_SAFE_CALL(MPI_File_set_view(fvtk, this->offset, MPI_FLOAT, this->nodeView,
"native", MPI_INFO_NULL));
MPI_SAFE_CALL(MPI_File_write_all(fvtk, node_coord.data(), size_int,
MPI_SAFE_CALL(idefix::MPI_File_write_all(fvtk, node_coord, size_int,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cette réécriture n'est pas utile car node_coord est déjà sur l'hote

Comment thread src/timeIntegrator.cpp
const double allowedImbalance = 20.0;
std::vector<double> computeLogPerCore(idfx::psize);
MPI_Gather(&computeLastLog, 1, MPI_DOUBLE, computeLogPerCore.data(), 1, MPI_DOUBLE, 0,
idefix::MPI_Gather(&computeLastLog, 1, MPI_DOUBLE, computeLogPerCore, 1, MPI_DOUBLE, 0,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comprends pas: computeLogPerCore est un std::vector, pas un IdefixArray

Comment thread src/pydefix.cpp
MPI_Recv(np_tot.data(), 3, MPI_INT, rank, 011, MPI_COMM_WORLD, &status);
MPI_Recv(beg.data(), 3, MPI_INT, rank, 012, MPI_COMM_WORLD, &status);
MPI_Recv(gbeg.data(), 3, MPI_INT, rank, 013, MPI_COMM_WORLD, &status);
idefix::MPI_Recv(np_int, 3, MPI_INT, rank, 010, MPI_COMM_WORLD, &status);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tout ces tableaux sont des vecteurs sur l'hote, je ne comprends même pas comment ça peut marcher (mais cette routine n'est probablement pas testée)

Comment thread src/mpi/mpiView.hpp
* CPU / GPU transfers if the array in on GPU and WITH_MPI_GPU_DIRECT is disabled.
*/
template <class T, size_t U>
int MPI_Send(const std::array<T, U> & buf, int count, MPI_Datatype datatype,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pas sur de comprendre l'intéret de cette fonction. Tout encapsuler dans ton API, même pour des fonctions qui n'utilisent pas le GPU aware?

Co-authored-by: Geoffroy Lesur <geoffroy.lesur@univ-grenoble-alpes.fr>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants