Skip to content

Commit 9344820

Browse files
svalatglesur
andauthored
MPI gpu-direct: make it optional and have fallback solution with device-host explicit transfers (#389)
* gpu-direct: handle communications when MPI GPU DIRECT is not there/available. --------- Co-authored-by: Geoffroy Lesur <geoffroy.lesur@univ-grenoble-alpes.fr>
1 parent 73255b0 commit 9344820

24 files changed

Lines changed: 991 additions & 171 deletions

CMakeLists.txt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ option(Idefix_DEBUG "Enable Idefix debug features (makes the code very slow)" OF
1616
option(Idefix_RUNTIME_CHECKS "Enable runtime sanity checks" OFF)
1717
option(Idefix_WERROR "Treat compiler warnings as errors" OFF)
1818
option(Idefix_PYTHON "Enable python bindings (requires pybind11)" OFF)
19+
option(Idefix_MPI_GPU_DIRECT "Enable usage of GPU direct to avoid CPU-GPU copies for MPI messages." ON)
20+
option(Idefix_MPI_GPU_FORCE_COPY "When GPU_DIRECT is disabled, force using transfers for validation purpose." OFF)
21+
mark_as_advanced(Idefix_MPI_GPU_FORCE_TRANSFERS)
1922
option(Idefix_SUPPRESS_FMA "Disable FMA (fused multiply-add) contraction/codegen. Useful for code validation across architectures." OFF)
2023
set(Idefix_PROBLEM_DIR "${CMAKE_BINARY_DIR}" CACHE STRING "Problem directory to build for.")
2124
set(Idefix_CXX_FLAGS "" CACHE STRING "Additional compiler/linker flag")
@@ -36,6 +39,10 @@ set_property(CACHE Idefix_PRECISION PROPERTY STRINGS Double Single)
3639
set(Idefix_LOOP_PATTERN "Default" CACHE STRING "Loop pattern for idefix_for")
3740
set_property(CACHE Idefix_LOOP_PATTERN PROPERTY STRINGS Default SIMD Range MDRange TeamPolicy TeamPolicyInnerVector)
3841

42+
set(Idefix_MPI_MODE "Persistent" CACHE STRING "MPI communication mode to use.")
43+
set_property(CACHE Idefix_MPI_MODE PROPERTY STRINGS Persistent Blocking NonBlocking)
44+
mark_as_advanced(Idefix_MPI_MODE)
45+
3946
# load git revision tools
4047
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/")
4148
include(GetGitRevisionDescription)
@@ -109,6 +116,10 @@ if(Idefix_MPI)
109116
add_subdirectory(src/mpi)
110117
endif()
111118

119+
if (Idefix_MPI_GPU_DIRECT)
120+
add_compile_definitions("WITH_MPI_GPU_DIRECT")
121+
endif()
122+
112123
if(Idefix_HDF5)
113124
add_compile_definitions("WITH_HDF5")
114125
if(Idefix_MPI)
@@ -245,6 +256,20 @@ if(${Idefix_PRECISION} STREQUAL "Single")
245256
add_compile_definitions("SINGLE_PRECISION")
246257
endif()
247258

259+
# MPI mode
260+
if(${Idefix_MPI_MODE} STREQUAL "Persistent")
261+
add_compile_definitions("COMMUNICATION_MODE_PERSISTENT")
262+
elseif(${Idefix_MPI_MODE} STREQUAL "NonBlocking")
263+
add_compile_definitions("COMMUNICATION_MODE_NON_BLOCKING")
264+
else(NOT ${Idefix_MPI_MODE} STREQUAL "Blocking")
265+
message(ERROR "Unknown MPI communication mode : Idefix_MPI_MODE=${Idefix_MPI_MODE}")
266+
endif()
267+
268+
# GPU direct disabled and force transfers
269+
if (${Idefix_MPI_GPU_FORCE_COPY})
270+
add_compile_definitions("WITH_GPU_FORCE_COPY")
271+
endif()
272+
248273
target_include_directories(idefix PUBLIC
249274
"${Idefix_PROBLEM_DIR_ABS}"
250275
)
@@ -297,6 +322,7 @@ endif()
297322
message(STATUS " MPI: ${Idefix_MPI}")
298323
message(STATUS " HDF5: ${Idefix_HDF5}")
299324
message(STATUS " Python: ${Idefix_PYTHON}")
325+
message(STATUS " GPU direct: ${Idefix_MPI_GPU_DIRECT}")
300326
message(STATUS " Reconstruction: ${Idefix_RECONSTRUCTION}")
301327
message(STATUS " Precision: ${Idefix_PRECISION}")
302328
message(STATUS " Version: ${Idefix_VERSION}")

doc/source/faq.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ I have a complex setup, and have written some functions in separate .cpp files.
2727
I want to run on the GPUs of xxx machine, how do I proceed?
2828
Check the examples in :ref:`setupExamples`
2929

30+
I don't have a CUDA or HIP aware MPI with GPU DIRECT support, how I can run *Idefix* ?
31+
You can look at the compile option `-DIdefix_MPI_GPU_DIRECT=OFF` in :ref:`makefile` to disable GPU DIRECT usage. Note that this will decrease performances.
32+
3033
Compilation
3134
-----------
3235

doc/source/reference/makefile.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
.. _makefile:
2+
13
Code configuration with Cmake
24
=============================
35

@@ -33,6 +35,10 @@ Several options can be enabled from the command line (or are accessible with ``c
3335
``-D Idefix_MPI=ON``
3436
Enable MPI parallelisation. Requires an MPI library. When used in conjonction with CUDA (Nvidia GPUs), a CUDA-aware MPI library is required by *Idefix*.
3537

38+
``-D Idefix_MPI_GPU_DIRECT=OFF``
39+
Disable the usage of MPI GPU direct for inter-process communication. In this case it uses a copy between GPU and GPU before and after making communications.
40+
It is usefull if your MPI is not GPU-aware. Note that this option decreases performances.
41+
3642
``-D Idefix_DEFS=foo.hpp``
3743
Specify a particular filename to be used in place of the default problem file ``definitions.hpp``
3844

pytools/idfx_test.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,20 @@ def __init__(self, current_test_file, name=""):
9292

9393
parser.add_argument("-mpi", help="Enable MPI", action="store_true")
9494

95+
parser.add_argument(
96+
"-mpiMode",
97+
default="Persistent",
98+
choices=["Default", "Persistent", "NonBlocking", "Blocking"],
99+
help="MPI communication mode to use (Default)",
100+
type=str,
101+
)
102+
103+
parser.add_argument(
104+
"-mpiGpuForceCopy",
105+
action="store_true",
106+
help="Disable MPI GPU direct and force memory transfers for validation purpose.",
107+
)
108+
95109
parser.add_argument(
96110
"-all",
97111
help="Do all test suite (otherwise, just do the test with the current configuration)",
@@ -266,6 +280,13 @@ def _genCmakeCommand(self, definitionFile=""):
266280
else:
267281
comm.append("-DIdefix_MPI=OFF")
268282

283+
if self.mpiMode != "Default":
284+
comm.append(f"-DIdefix_MPI_MODE={self.mpiMode}")
285+
286+
if self.mpiGpuForceCopy:
287+
comm.append("-DIdefix_MPI_GPU_DIRECT=OFF")
288+
comm.append("-DIdefix_MPI_GPU_FORCE_COPY=ON")
289+
269290
if self.reconstruction == 2:
270291
comm.append("-DIdefix_RECONSTRUCTION=Linear")
271292
elif self.reconstruction == 3:

src/fluid/boundary/axis.cpp

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ void Axis::ShowConfig() {
2525
void Axis::SymmetrizeEx1Side(int jref, IdefixArray3D<real> Ex1) {
2626
#if DIMENSIONS == 3
2727

28-
IdefixArray1D<real> Ex1Avg = this->Ex1Avg;
28+
auto Ex1Avg = this->Ex1Avg.deviceView();
2929

3030
idefix_for("Ex1_ini",0,data->np_tot[IDIR],
3131
KOKKOS_LAMBDA(int i) {
@@ -40,7 +40,7 @@ void Axis::SymmetrizeEx1Side(int jref, IdefixArray3D<real> Ex1) {
4040
#ifdef WITH_MPI
4141
Kokkos::fence();
4242
// sum along all of the processes on the same r
43-
MPI_Allreduce(MPI_IN_PLACE, Ex1Avg.data(), data->np_tot[IDIR], realMPI,
43+
idfx::MPI_Allreduce(MPI_IN_PLACE, this->Ex1Avg, data->np_tot[IDIR], realMPI,
4444
MPI_SUM, data->mygrid->AxisComm);
4545
#endif
4646
}
@@ -88,7 +88,8 @@ void Axis::RegularizeCurrentSide(int side) {
8888
jc = data->end[JDIR]-1;
8989
sign = -1;
9090
}
91-
IdefixArray1D<real> BAvg = this->Ex1Avg;
91+
auto BAvg = this->Ex1Avg.deviceView();
92+
auto BAvgComm = this->Ex1Avg;
9293
IdefixArray1D<real> x1 = data->x[IDIR];
9394
IdefixArray1D<real> dx3 = data->dx[KDIR];
9495
IdefixArray1D<real> dx2 = data->dx[JDIR];
@@ -107,7 +108,7 @@ void Axis::RegularizeCurrentSide(int side) {
107108
#ifdef WITH_MPI
108109
Kokkos::fence();
109110
// sum along all of the processes on the same r
110-
MPI_Allreduce(MPI_IN_PLACE, BAvg.data(), data->np_tot[IDIR], realMPI,
111+
MPI_Allreduce(MPI_IN_PLACE, BAvgComm, data->np_tot[IDIR], realMPI,
111112
MPI_SUM, data->mygrid->AxisComm);
112113
#endif
113114
}
@@ -169,7 +170,7 @@ void Axis::FixBx2sAxis(int side) {
169170
// Compute the values of Bx and By that are consistent with BX2 along the axis
170171
#if DIMENSIONS == 3
171172
IdefixArray4D<real> Vs = this->Vs;
172-
IdefixArray2D<real> BAvg = this->BAvg;
173+
auto BAvg = this->BAvg.deviceView();
173174
IdefixArray1D<real> phi = data->x[KDIR];
174175

175176
int jin = 0;
@@ -208,7 +209,7 @@ void Axis::FixBx2sAxis(int side) {
208209
Kokkos::fence();
209210
#ifdef WITH_MPI
210211
// sum along all of the processes on the same r
211-
MPI_Allreduce(MPI_IN_PLACE, BAvg.data(), 2*data->np_tot[IDIR], realMPI,
212+
idfx::MPI_Allreduce(MPI_IN_PLACE, this->BAvg, 2*data->np_tot[IDIR], realMPI,
212213
MPI_SUM, data->mygrid->AxisComm);
213214
#endif
214215
}
@@ -392,7 +393,6 @@ void Axis::ExchangeMPI(int side) {
392393
idfx::pushRegion("Axis::ExchangeMPI");
393394
#ifdef WITH_MPI
394395
// Load the buffers with data
395-
[[maybe_unused]] int ibeg,iend,jbeg,jend,kbeg,kend;
396396
int offset;
397397
int ny;
398398
Buffer bufferSend = this->bufferSend;
@@ -409,7 +409,7 @@ void Axis::ExchangeMPI(int side) {
409409
MPI_Status recvStatus;
410410

411411
double tStart = MPI_Wtime();
412-
MPI_SAFE_CALL(MPI_Start(&recvRequest));
412+
MPI_SAFE_CALL(idfx::MPI_Start(&recvRequest));
413413
idfx::mpiCallsTimer += MPI_Wtime() - tStart;
414414

415415
// Coordinates of the ghost region which needs to be transfered
@@ -471,8 +471,8 @@ void Axis::ExchangeMPI(int side) {
471471
Kokkos::fence();
472472

473473
tStart = MPI_Wtime();
474-
MPI_SAFE_CALL(MPI_Start(&sendRequest));
475-
MPI_Wait(&recvRequest,&recvStatus);
474+
MPI_SAFE_CALL(idfx::MPI_Start(&sendRequest));
475+
idfx::MPI_Wait(&recvRequest,&recvStatus);
476476
idfx::mpiCallsTimer += MPI_Wtime() - tStart;
477477

478478
// Unpack
@@ -526,8 +526,7 @@ void Axis::ExchangeMPI(int side) {
526526
} // MHD
527527
}
528528

529-
MPI_Wait(&sendRequest, &sendStatus);
530-
529+
idfx::MPI_Wait(&sendRequest, &sendStatus);
531530
idfx::mpiCallsTimer += MPI_Wtime() - tStart;
532531

533532

@@ -594,11 +593,11 @@ void Axis::InitMPI() {
594593
MPI_SAFE_CALL(MPI_Cart_shift(data->mygrid->AxisComm,0,data->mygrid->nproc[KDIR]/2,
595594
&procRecv,&procSend ));
596595

597-
MPI_SAFE_CALL(MPI_Send_init(bufferSend.data(), bufferSend.Size(), realMPI, procSend,
598-
650, data->mygrid->AxisComm, &sendRequest));
596+
MPI_SAFE_CALL(idfx::MPI_Send_init(bufferSend.commView(), bufferSend.Size(),
597+
realMPI, procSend, 650, data->mygrid->AxisComm, &sendRequest));
599598

600-
MPI_SAFE_CALL(MPI_Recv_init(bufferRecv.data(), bufferRecv.Size(), realMPI, procRecv,
601-
650, data->mygrid->AxisComm, &recvRequest));
599+
MPI_SAFE_CALL(idfx::MPI_Recv_init(bufferRecv.commView(), bufferRecv.Size(),
600+
realMPI, procRecv, 650, data->mygrid->AxisComm, &recvRequest));
602601

603602
#endif
604603
idfx::popRegion();

src/fluid/boundary/axis.hpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ class Axis {
5757

5858
enum {faceTop, faceBot};
5959
#ifdef WITH_MPI
60-
MPI_Request sendRequest;
61-
MPI_Request recvRequest;
60+
idfx::MPI_Request_1D<real> sendRequest;
61+
idfx::MPI_Request_1D<real> recvRequest;
6262

6363
Buffer bufferSend;
6464
Buffer bufferRecv;
@@ -71,8 +71,8 @@ class Axis {
7171
#endif
7272
void InitMPI();
7373

74-
IdefixArray1D<real> Ex1Avg;
75-
IdefixArray2D<real> BAvg;
74+
idfx::IdefixCommArray1D<real> Ex1Avg;
75+
idfx::IdefixCommArray2D<real> BAvg;
7676
bool haveCurrent;
7777
IdefixArray2D<real> JAvg;
7878
IdefixArray1D<int> symmetryVc;
@@ -155,8 +155,8 @@ Axis::Axis(Boundary<Phys> *boundary) {
155155
}
156156
Kokkos::deep_copy(symmetryVs, symmetryVsHost);
157157

158-
this->Ex1Avg = IdefixArray1D<real>("Axis:Ex1Avg",data->np_tot[IDIR]);
159-
this->BAvg = IdefixArray2D<real>("Axis:BxAvg",data->np_tot[IDIR],2);
158+
this->Ex1Avg = idfx::IdefixCommArray1D<real>("Axis:Ex1Avg",data->np_tot[IDIR]);
159+
this->BAvg = idfx::IdefixCommArray2D<real>("Axis:BxAvg",data->np_tot[IDIR],2);
160160
if(haveCurrent) {
161161
this->JAvg = IdefixArray2D<real>("Axis:JAvg",data->np_tot[IDIR],3);
162162
}

src/fluid/constrainedTransport/EMFexchange.hpp

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ void ConstrainedTransport<Phys>::ExchangeX1(IdefixArray3D<real> ey, IdefixArray3
4141
MPI_Status recvStatus[2];
4242

4343
double tStart = MPI_Wtime();
44-
MPI_SAFE_CALL(MPI_Startall(2, recvRequestX1));
44+
MPI_SAFE_CALL(idfx::MPI_Startall(2, recvRequestX1));
4545
idfx::mpiCallsTimer += MPI_Wtime() - tStart;
4646

4747
BoundaryType lbound = data->lbound[IDIR];
@@ -82,11 +82,10 @@ void ConstrainedTransport<Phys>::ExchangeX1(IdefixArray3D<real> ey, IdefixArray3
8282
Kokkos::fence();
8383

8484
tStart = MPI_Wtime();
85-
MPI_SAFE_CALL(MPI_Startall(2, sendRequestX1));
85+
MPI_SAFE_CALL(idfx::MPI_Startall(2, sendRequestX1));
8686
// Wait for buffers to be received
87-
88-
MPI_Waitall(2,recvRequestX1,recvStatus);
89-
MPI_Waitall(2, sendRequestX1, sendStatus);
87+
idfx::MPI_Waitall(2,recvRequestX1,recvStatus);
88+
idfx::MPI_Waitall(2, sendRequestX1, sendStatus);
9089
idfx::mpiCallsTimer += MPI_Wtime() - tStart;
9190

9291
// Unpack
@@ -130,7 +129,7 @@ void ConstrainedTransport<Phys>::ExchangeX2(IdefixArray3D<real> ex, IdefixArray3
130129
double tStart = MPI_Wtime();
131130
MPI_Status sendStatus[2];
132131
MPI_Status recvStatus[2];
133-
MPI_SAFE_CALL(MPI_Startall(2, recvRequestX2));
132+
MPI_SAFE_CALL(idfx::MPI_Startall(2, recvRequestX2));
134133
idfx::mpiCallsTimer += MPI_Wtime() - tStart;
135134

136135
BoundaryType lbound = data->lbound[JDIR];
@@ -169,10 +168,10 @@ void ConstrainedTransport<Phys>::ExchangeX2(IdefixArray3D<real> ex, IdefixArray3
169168
Kokkos::fence();
170169

171170
tStart = MPI_Wtime();
172-
MPI_SAFE_CALL(MPI_Startall(2, sendRequestX2));
171+
MPI_SAFE_CALL(idfx::MPI_Startall(2, sendRequestX2));
173172
// Wait for buffers to be received
174-
MPI_Waitall(2, recvRequestX2, recvStatus);
175-
MPI_Waitall(2, sendRequestX2, sendStatus);
173+
idfx::MPI_Waitall(2, recvRequestX2, recvStatus);
174+
idfx::MPI_Waitall(2, sendRequestX2, sendStatus);
176175
idfx::mpiCallsTimer += MPI_Wtime() - tStart;
177176

178177
// Unpack
@@ -216,7 +215,7 @@ void ConstrainedTransport<Phys>::ExchangeX3(IdefixArray3D<real> ex, IdefixArray3
216215
double tStart = MPI_Wtime();
217216
MPI_Status sendStatus[2];
218217
MPI_Status recvStatus[2];
219-
MPI_SAFE_CALL(MPI_Startall(2, recvRequestX3));
218+
MPI_SAFE_CALL(idfx::MPI_Startall(2, recvRequestX3));
220219
idfx::mpiCallsTimer += MPI_Wtime() - tStart;
221220

222221
BoundaryType lbound = data->lbound[KDIR];
@@ -256,10 +255,10 @@ void ConstrainedTransport<Phys>::ExchangeX3(IdefixArray3D<real> ex, IdefixArray3
256255
Kokkos::fence();
257256

258257
tStart = MPI_Wtime();
259-
MPI_SAFE_CALL(MPI_Startall(2, sendRequestX3));
258+
MPI_SAFE_CALL(idfx::MPI_Startall(2, sendRequestX3));
260259
// Wait for buffers to be received
261-
MPI_Waitall(2, recvRequestX3, recvStatus);
262-
MPI_Waitall(2, sendRequestX3, sendStatus);
260+
idfx::MPI_Waitall(2, recvRequestX3, recvStatus);
261+
idfx::MPI_Waitall(2, sendRequestX3, sendStatus);
263262
idfx::mpiCallsTimer += MPI_Wtime() - tStart;
264263

265264
// Unpack

0 commit comments

Comments
 (0)