XGCa
mpi_space_wrapper.hpp
Go to the documentation of this file.
1 #ifndef MPI_SPACE_WRAPPER_HPP
2 #define MPI_SPACE_WRAPPER_HPP
3 
4 #include "space_settings.hpp"
5 #include "my_mirror_view.hpp"
6 
7 enum class MPISpaceOptions { // If supported, send/recv between device memory...
8  AlwaysPreferGPUMPI, // ... always (even if both views are on host)
9  MixedPreferGPUMPI, // ... if at least one of the views is on device
10  MixedPreferHostMPI, // ... if both views are on device
11  AlwaysPreferHostMPI, // ... never (even if both views are on device)
12 };
13 
14 enum class MPISpaceWrapperOptions { // If input view is not in the same space as the selected MPI space...
15  NotInPlace, // ... allocate a send buffer
16  InPlace, // ... copy the input view to the recv buffer and use MPI_IN_PLACE
17 };
18 
19 
20 // Check whether some space is HostSpace
21 template<class Space>
22 struct is_host_space : std::false_type {};
23 
24 template<>
25 struct is_host_space<Kokkos::HostSpace> : std::true_type {};
26 
27 // Copy data to the selected space and execute the supplied MPI call
28 template<class SendView, class RecvView, class Device, class F>
29 void MPI_call_space_wrapper(const SendView& send, const RecvView& recv,
30  F&& user_mpi_call, MPISpaceWrapperOptions option_in_place, Device nd){
31 
32  // Check whether to try in-place optimization (i.e. reuse recv buffer as send buffer)
33  bool use_in_place = (option_in_place==MPISpaceWrapperOptions::InPlace &&
34  !std::is_same_v<typename SendView::memory_space, typename Device::memory_space>);
35 
36  // Allocate buffer for one or both views in MPI space as needed
37  // If send buffer is not in the right space, we can try to do MPI_IN_PLACE on recv_mpi
38  auto recv_mpi = my_mirror_view(recv, Device());
39  auto send_mpi = use_in_place ? recv_mpi : my_mirror_view(send, Device());
40 
41  // Copy send if needed
42  mirror_copy(send_mpi, send);
43 
44  // MPI call
45  user_mpi_call(use_in_place ? MPI_IN_PLACE : send_mpi.data(), recv_mpi.data());
46 
47  // Copy recv back if needed
48  mirror_copy(recv, recv_mpi);
49 
50 }
51 
52 // (MPI_IN_PLACE) Copy data to the selected space and execute the supplied MPI call
53 template<class SendView, class Device, class F>
54 void MPI_call_space_wrapper(const SendView& send,
55  F&& user_mpi_call, Device nd){
56 
57  // Allocate buffer in MPI space as needed
58  auto send_mpi = my_mirror_view(send, Device());
59 
60  // Copy send if needed
61  mirror_copy(send_mpi, send);
62 
63  // MPI call
64  user_mpi_call(send_mpi.data());
65 
66  // Copy recv back if needed
67  mirror_copy(send, send_mpi);
68 
69 }
70 
71 // Choose which space to do the MPI operation in
72 template<class SendView, class RecvView>
73 bool MPI_choose_space(const SendView& send, const RecvView& recv, MPISpaceOptions option){
74  using SendSpace = typename SendView::memory_space;
75  using RecvSpace = typename RecvView::memory_space;
76  using MPISpace = typename MPIDeviceType::memory_space;
77 
78  // Decide whether MPI should see device (MPISpace) or host pointers
79  constexpr bool gpu_aware_mpi_available = !is_host_space<MPISpace>::value;
80  constexpr bool send_view_is_on_device = !is_host_space<SendSpace>::value;
81  constexpr bool recv_view_is_on_device = !is_host_space<RecvSpace>::value;
82  bool use_device_for_mpi = false; // Default to host MPI
84  use_device_for_mpi = gpu_aware_mpi_available;
85  }else if(option==MPISpaceOptions::MixedPreferGPUMPI){
86  use_device_for_mpi = gpu_aware_mpi_available && (send_view_is_on_device || recv_view_is_on_device);
87  }else if(option==MPISpaceOptions::MixedPreferHostMPI){
88  use_device_for_mpi = gpu_aware_mpi_available && (send_view_is_on_device && recv_view_is_on_device);
89  }
90  return use_device_for_mpi;
91 }
92 
93 // (MPI_IN_PLACE) Choose which space to do the MPI operation in
94 template<class SendView>
95 bool MPI_choose_space(const SendView& send, MPISpaceOptions option){
96  using SendSpace = typename SendView::memory_space;
97  using MPISpace = typename MPIDeviceType::memory_space;
98 
99  // Decide whether MPI should see device (MPISpace) or host pointers
100  constexpr bool gpu_aware_mpi_available = !is_host_space<MPISpace>::value;
101  bool use_device_for_mpi = false; // Default to host MPI
103  use_device_for_mpi = gpu_aware_mpi_available;
104  }else if(option==MPISpaceOptions::MixedPreferGPUMPI){ // These options should be disallowed for in-place
105  use_device_for_mpi = gpu_aware_mpi_available;
106  }else if(option==MPISpaceOptions::MixedPreferHostMPI){ // These options should be disallowed for in-place
107  use_device_for_mpi = false;
108  }
109  return use_device_for_mpi;
110 }
111 
112 /* Automatically copy views to/from device as needed to use MPI in the specified manner
113  Wrap your intended MPI call, simply replacing f_send.data() and f_recv.data() with send_ptr and recv_ptr respectively.
114  Default space option is MPISpaceOptions::MixedPreferGPUMPI
115  in_place_option is an optimization to be used with care since MPI_IN_PLACE may not improve performance and will not always
116  work with the same syntax (e.g. MPI_Reduce requires a different input for the root rank)
117 
118  Usage example:
119  MPI_space_wrapper(f_send, f_recv, [&](double* send_ptr, double* recv_ptr){
120  MPI_Allreduce(send_ptr, recv_ptr, f_send.size(), MPI_DOUBLE, MPI_SUM, pol_decomp.mpi.intpl_comm);
121  }, MPISpaceOptions::AlwaysPreferHostMPI);
122  */
123 template<class SendView, class RecvView, class F>
124 void MPI_space_wrapper(const SendView& send_MD, const RecvView& recv_MD, F&& user_mpi_call,
127 
128  using value_type = typename SendView::non_const_value_type;
129  using SendSpace = typename SendView::memory_space;
130  using RecvSpace = typename RecvView::memory_space;
131 
132  static_assert(std::is_same_v<typename SendView::non_const_value_type,
133  typename RecvView::non_const_value_type>,
134  "Send/recv value types must match.");
135 
136  // Get 1D unmanaged send/recv views; remove unmanaged tag for simpler template deductions (okay since views are const)
137  const View<value_type*,CLayout, typename SendView::device_type, Kokkos::MemoryTraits<Kokkos::Unmanaged>> send_unm(send_MD.data(), send_MD.size());
138  const View<value_type*,CLayout, typename RecvView::device_type, Kokkos::MemoryTraits<Kokkos::Unmanaged>> recv_unm(recv_MD.data(), recv_MD.size());
139  const View<value_type*,CLayout, typename SendView::device_type> send = send_unm;
140  const View<value_type*,CLayout, typename RecvView::device_type> recv = recv_unm;
141 
142  // Choose whether to do operation on host or device
143  bool use_device_for_mpi = MPI_choose_space(send, recv, option);
144 
145  // Execute MPI operation
146  if(use_device_for_mpi){
147  MPI_call_space_wrapper(send, recv, user_mpi_call, in_place_option, DeviceType());
148  }else{
149  MPI_call_space_wrapper(send, recv, user_mpi_call, in_place_option, HostType());
150  }
151 }
152 
153 /* For MPI_IN_PLACE usage: Automatically copy view to/from device as needed to use MPI in the specified manner
154  Wrap your intended MPI call, simply replacing f.data() with mpi_ptr
155  Default space option is MPISpaceOptions::AlwaysPreferGPUMPI
156 
157  Usage example:
158  MPI_space_wrapper(f, [&](double* mpi_ptr){
159  MPI_Allreduce(MPI_IN_PLACE, mpi_ptr, f.size(), MPI_DOUBLE, MPI_SUM, pol_decomp.mpi.intpl_comm);
160  }, MPISpaceOptions::AlwaysPreferHostMPI);
161  */
162 template<class SendView, class F>
163 void MPI_space_wrapper(const SendView& send_MD, F&& user_mpi_call,
165 
166  using value_type = typename SendView::non_const_value_type;
167  using SendSpace = typename SendView::memory_space;
168 
169  // Get 1D unmanaged send views; remove unmanaged tag for simpler template deductions (okay since views are const)
170  const View<value_type*,CLayout, typename SendView::device_type, Kokkos::MemoryTraits<Kokkos::Unmanaged>> send_unm(send_MD.data(), send_MD.size());
171  const View<value_type*,CLayout, typename SendView::device_type> send = send_unm;
172 
173  // Choose whether to do operation on host or device
174  bool use_device_for_mpi = MPI_choose_space(send, option);
175 
176  // Execute MPI operation
177  if(use_device_for_mpi){
178  MPI_call_space_wrapper(send, user_mpi_call, DeviceType());
179  }else{
180  MPI_call_space_wrapper(send, user_mpi_call, HostType());
181  }
182 }
183 
184 
185 #endif
MPISpaceOptions
Definition: mpi_space_wrapper.hpp:7
void MPI_space_wrapper(const SendView &send_MD, const RecvView &recv_MD, F &&user_mpi_call, MPISpaceOptions option=MPISpaceOptions::MixedPreferGPUMPI, MPISpaceWrapperOptions in_place_option=MPISpaceWrapperOptions::NotInPlace)
Definition: mpi_space_wrapper.hpp:124
MPISpaceWrapperOptions
Definition: mpi_space_wrapper.hpp:14
bool MPI_choose_space(const SendView &send, const RecvView &recv, MPISpaceOptions option)
Definition: mpi_space_wrapper.hpp:73
void MPI_call_space_wrapper(const SendView &send, const RecvView &recv, F &&user_mpi_call, MPISpaceWrapperOptions option_in_place, Device nd)
Definition: mpi_space_wrapper.hpp:29
void mirror_copy(T1 &view_dest, const T2 &view_src)
Definition: my_mirror_view.hpp:122
View< T *, CLayout, Device > my_mirror_view(const View< T *, CLayout, Device > &view, Device nd)
Definition: my_mirror_view.hpp:14
Definition: col_grid.cpp:127
Kokkos::Device< HostExSpace, HostMemSpace > HostType
Definition: space_settings.hpp:57
Kokkos::Device< ExSpace, MemSpace > DeviceType
Definition: space_settings.hpp:48
Definition: mpi_space_wrapper.hpp:22