Arcane  4.1.12.0
Developer documentation
Loading...
Searching...
No Matches
MpiNeighborVariableSynchronizeDispatcher.cc
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2026 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
4// See the top-level COPYRIGHT file for details.
5// SPDX-License-Identifier: Apache-2.0
6//-----------------------------------------------------------------------------
7/*---------------------------------------------------------------------------*/
8/* MpiNeighborVariableSynchronizeDispatcher.cc (C) 2000-2025 */
9/* */
10/* Variable synchronizations via MPI_Neighbor_alltoallv. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/utils/FatalErrorException.h"
15#include "arcane/utils/NotSupportedException.h"
16#include "arcane/utils/CheckedConvert.h"
17#include "arcane/utils/MemoryView.h"
18
19#include "arcane/parallel/mpi/MpiParallelMng.h"
20#include "arcane/parallel/mpi/MpiTimeInterval.h"
21#include "arcane/parallel/mpi/IVariableSynchronizerMpiCommunicator.h"
22#include "arcane/parallel/IStat.h"
23
24#include "arcane/impl/IDataSynchronizeBuffer.h"
25#include "arcane/impl/IDataSynchronizeImplementation.h"
26#include "arcane/impl/DataSynchronizeInfo.h"
27
28#include "arccore/message_passing_mpi/internal/MpiAdapter.h"
29
30/*---------------------------------------------------------------------------*/
31/*---------------------------------------------------------------------------*/
32/*
33 * This implementation uses the MPI_Neighbor_alltoallv function for
34 * synchronizations. This function is available in version 3.1
35 * of MPI.
36 */
37/*---------------------------------------------------------------------------*/
38/*---------------------------------------------------------------------------*/
39
40namespace Arcane
41{
42
43/*---------------------------------------------------------------------------*/
44/*---------------------------------------------------------------------------*/
45
46/*
47 * \brief Implementation of variable synchronization via
48 * MPI_Neighbor_alltoallv().
49 */
50class MpiNeighborVariableSynchronizerDispatcher
52{
53 public:
54
55 class Factory;
56 explicit MpiNeighborVariableSynchronizerDispatcher(Factory* f);
57
58 public:
59
60 void compute() override;
61 void beginSynchronize(IDataSynchronizeBuffer* buf) override;
62 void endSynchronize(IDataSynchronizeBuffer* buf) override;
63
64 private:
65
66 MpiParallelMng* m_mpi_parallel_mng = nullptr;
67 UniqueArray<int> m_mpi_send_counts;
68 UniqueArray<int> m_mpi_receive_counts;
69 UniqueArray<int> m_mpi_send_displacements;
70 UniqueArray<int> m_mpi_receive_displacements;
71 Ref<IVariableSynchronizerMpiCommunicator> m_synchronizer_communicator;
72};
73
74/*---------------------------------------------------------------------------*/
75/*---------------------------------------------------------------------------*/
76
79{
80 public:
81
82 Factory(MpiParallelMng* mpi_pm, Ref<IVariableSynchronizerMpiCommunicator> synchronizer_communicator)
83 : m_mpi_parallel_mng(mpi_pm)
84 , m_synchronizer_communicator(synchronizer_communicator)
85 {}
86
87 Ref<IDataSynchronizeImplementation> createInstance() override
88 {
89 auto* x = new MpiNeighborVariableSynchronizerDispatcher(this);
91 }
92
93 public:
94
95 MpiParallelMng* m_mpi_parallel_mng = nullptr;
96 Ref<IVariableSynchronizerMpiCommunicator> m_synchronizer_communicator;
97};
98
99/*---------------------------------------------------------------------------*/
100/*---------------------------------------------------------------------------*/
101
103arcaneCreateMpiNeighborVariableSynchronizerFactory(MpiParallelMng* mpi_pm,
105{
106 auto* x = new MpiNeighborVariableSynchronizerDispatcher::Factory(mpi_pm, sync_communicator);
108}
109
110/*---------------------------------------------------------------------------*/
111/*---------------------------------------------------------------------------*/
112
113MpiNeighborVariableSynchronizerDispatcher::
114MpiNeighborVariableSynchronizerDispatcher(Factory* f)
115: m_mpi_parallel_mng(f->m_mpi_parallel_mng)
116, m_synchronizer_communicator(f->m_synchronizer_communicator)
117{
118}
119
120/*---------------------------------------------------------------------------*/
121/*---------------------------------------------------------------------------*/
122
123void MpiNeighborVariableSynchronizerDispatcher::
124beginSynchronize(IDataSynchronizeBuffer* buf)
125{
126 // Does nothing at the MPI level in this part because this implementation
127 // does not yet support asynchronous operations.
128 // We simply copy the variable values into the send buffer
129 // to allow the variable values to be modified between
130 // beginSynchronize() and endSynchronize().
131
132 double send_copy_time = 0.0;
133 {
134 MpiTimeInterval tit(&send_copy_time);
135
136 // Copies the send buffers
137 buf->copyAllSend();
138 }
139 Int64 total_share_size = buf->totalSendSize();
140 m_mpi_parallel_mng->stat()->add("SyncSendCopy", send_copy_time, total_share_size);
141}
142
143/*---------------------------------------------------------------------------*/
144/*---------------------------------------------------------------------------*/
145
146void MpiNeighborVariableSynchronizerDispatcher::
147endSynchronize(IDataSynchronizeBuffer* buf)
148{
149 const Int32 nb_message = buf->nbRank();
150
151 auto* sync_communicator = m_synchronizer_communicator.get();
152 ARCANE_CHECK_POINTER(sync_communicator);
153
154 MPI_Comm communicator = sync_communicator->communicator();
155 if (communicator == MPI_COMM_NULL)
156 ARCANE_FATAL("Invalid null communicator");
157
158 MpiParallelMng* pm = m_mpi_parallel_mng;
159 const MPI_Datatype mpi_dt = MP::Mpi::MpiBuiltIn::datatype(Byte());
160
161 double copy_time = 0.0;
162 double wait_time = 0.0;
163
164 if (!buf->hasGlobalBuffer())
165 ARCANE_THROW(NotSupportedException, "Can not use MPI_Neighbor_alltoallv when hasGlobalBufer() is false");
166
167 for (Integer i = 0; i < nb_message; ++i) {
168 Int32 nb_send = CheckedConvert::toInt32(buf->sendBuffer(i).bytes().size());
169 Int32 nb_receive = CheckedConvert::toInt32(buf->receiveBuffer(i).bytes().size());
170 Int32 send_displacement = CheckedConvert::toInt32(buf->sendDisplacement(i));
171 Int32 receive_displacement = CheckedConvert::toInt32(buf->receiveDisplacement(i));
172
173 m_mpi_send_counts[i] = nb_send;
174 m_mpi_receive_counts[i] = nb_receive;
175 m_mpi_send_displacements[i] = send_displacement;
176 m_mpi_receive_displacements[i] = receive_displacement;
177 }
178
179 {
180 MpiTimeInterval tit(&wait_time);
181 auto send_buf = buf->globalSendBuffer();
182 auto receive_buf = buf->globalReceiveBuffer();
183 MPI_Neighbor_alltoallv(send_buf.data(), m_mpi_send_counts.data(), m_mpi_send_displacements.data(), mpi_dt,
184 receive_buf.data(), m_mpi_receive_counts.data(), m_mpi_receive_displacements.data(), mpi_dt,
185 communicator);
186 }
187
188 // Copies the received values
189 {
190 MpiTimeInterval tit(&copy_time);
191 buf->copyAllReceive();
192 }
193
194 Int64 total_ghost_size = buf->totalReceiveSize();
195 Int64 total_share_size = buf->totalSendSize();
196 Int64 total_size = total_ghost_size + total_share_size;
197 pm->stat()->add("SyncCopy", copy_time, total_ghost_size);
198 pm->stat()->add("SyncWait", wait_time, total_size);
199}
200
201/*---------------------------------------------------------------------------*/
202/*---------------------------------------------------------------------------*/
203
204void MpiNeighborVariableSynchronizerDispatcher::
205compute()
206{
207 DataSynchronizeInfo* sync_info = _syncInfo();
208 ARCANE_CHECK_POINTER(sync_info);
209
210 auto* sync_communicator = m_synchronizer_communicator.get();
211 ARCANE_CHECK_POINTER(sync_communicator);
212
213 const Int32 nb_message = sync_info->size();
214
215 // Some versions of OpenMPI (before 4.1) crash if there are no
216 // messages and one of the following arrays is empty. To bypass
217 // this problem, we allocate an array of size 1.
218 Int32 size = nb_message;
219 if (size == 0)
220 size = 1;
221
222 m_mpi_send_counts.resize(size);
223 m_mpi_receive_counts.resize(size);
224 m_mpi_send_displacements.resize(size);
225 m_mpi_receive_displacements.resize(size);
226
227 m_mpi_send_counts.fill(0);
228 m_mpi_receive_counts.fill(0);
229 m_mpi_send_displacements.fill(0);
230 m_mpi_receive_displacements.fill(0);
231}
232
233/*---------------------------------------------------------------------------*/
234/*---------------------------------------------------------------------------*/
235
236/*---------------------------------------------------------------------------*/
237/*---------------------------------------------------------------------------*/
238
239} // End namespace Arcane
240
241/*---------------------------------------------------------------------------*/
242/*---------------------------------------------------------------------------*/
#define ARCANE_CHECK_POINTER(ptr)
Macro returning the pointer ptr if it is not null or throwing an exception if it is null.
#define ARCANE_THROW(exception_class,...)
Macro for throwing an exception with formatting.
#define ARCANE_FATAL(...)
Macro throwing a FatalErrorException.
Generic buffer for data synchronization.
Interface for a generic dispatcher factory.
Parallelism manager using MPI.
Reference to an instance.
1D data vector with value semantics (STL style).
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
std::int64_t Int64
Signed integer type of 64 bits.
Int32 Integer
Type representing an integer.
unsigned char Byte
Type of a byte.
Definition BaseTypes.h:43
auto makeRef(InstanceType *t) -> Ref< InstanceType >
Creates a reference on a pointer.
std::int32_t Int32
Signed integer type of 32 bits.