deal.II version GIT relicensing-2289-g1e5549a87a 2024-12-21 21:30:00+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
mpi_consensus_algorithms.h
Go to the documentation of this file.
1// ------------------------------------------------------------------------
2//
3// SPDX-License-Identifier: LGPL-2.1-or-later
4// Copyright (C) 2020 - 2023 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Part of the source code is dual licensed under Apache-2.0 WITH
9// LLVM-exception OR LGPL-2.1-or-later. Detailed license information
10// governing the source code and code contributions can be found in
11// LICENSE.md and CONTRIBUTING.md at the top level directory of deal.II.
12//
13// ------------------------------------------------------------------------
14
15#ifndef dealii_mpi_consensus_algorithm_h
16#define dealii_mpi_consensus_algorithm_h
17
18#include <deal.II/base/config.h>
19
20#include <deal.II/base/mpi.h>
21#include <deal.II/base/mpi.templates.h>
23
25
26
27namespace Utilities
28{
29 namespace MPI
30 {
129 namespace ConsensusAlgorithms
130 {
160 template <typename RequestType, typename AnswerType>
161 class DEAL_II_DEPRECATED_EARLY Process
162 {
163 public:
168 virtual ~Process() = default;
169
176 virtual std::vector<unsigned int>
178
188 virtual void
189 create_request(const unsigned int other_rank, RequestType &send_buffer);
190
203 virtual void
204 answer_request(const unsigned int other_rank,
205 const RequestType &buffer_recv,
206 AnswerType &request_buffer);
207
215 virtual void
216 read_answer(const unsigned int other_rank,
217 const AnswerType &recv_buffer);
218 };
219
220
221
235 template <typename RequestType, typename AnswerType>
237 {
238 public:
242 Interface() = default;
243
248 virtual ~Interface() = default;
249
263 DEAL_II_DEPRECATED_EARLY
264 std::vector<unsigned int>
266
290 virtual std::vector<unsigned int>
292 const std::vector<unsigned int> &targets,
293 const std::function<RequestType(const unsigned int)> &create_request,
294 const std::function<AnswerType(const unsigned int,
295 const RequestType &)> &answer_request,
296 const std::function<void(const unsigned int, const AnswerType &)>
297 &process_answer,
298 const MPI_Comm comm) = 0;
299 };
300
301
315 template <typename RequestType, typename AnswerType>
316 class NBX : public Interface<RequestType, AnswerType>
317 {
318 public:
322 NBX() = default;
323
327 virtual ~NBX() = default;
328
329 // Import the declarations from the base class.
330 using Interface<RequestType, AnswerType>::run;
331
335 virtual std::vector<unsigned int>
337 const std::vector<unsigned int> &targets,
338 const std::function<RequestType(const unsigned int)> &create_request,
339 const std::function<AnswerType(const unsigned int,
340 const RequestType &)> &answer_request,
341 const std::function<void(const unsigned int, const AnswerType &)>
342 &process_answer,
343 const MPI_Comm comm) override;
344
345 private:
346#ifdef DEAL_II_WITH_MPI
350 std::vector<std::vector<char>> send_buffers;
351
355 std::vector<MPI_Request> send_requests;
356
364 std::vector<std::unique_ptr<std::vector<char>>> request_buffers;
365
369 std::vector<std::unique_ptr<MPI_Request>> request_requests;
370
375
376 // request for barrier
377 MPI_Request barrier_request;
378#endif
379
383 std::set<unsigned int> requesting_processes;
384
390 bool
392 const std::function<void(const unsigned int, const AnswerType &)>
393 &process_answer,
394 const MPI_Comm comm);
395
400 void
402
408 bool
410
416 void
418 const std::function<AnswerType(const unsigned int,
419 const RequestType &)> &answer_request,
420 const MPI_Comm comm);
421
426 void
428 const std::vector<unsigned int> &targets,
429 const std::function<RequestType(const unsigned int)> &create_request,
430 const MPI_Comm comm);
431
436 void
438 };
439
440
485 template <typename RequestType, typename AnswerType>
486 std::vector<unsigned int>
487 nbx(const std::vector<unsigned int> &targets,
488 const std::function<RequestType(const unsigned int)> &create_request,
489 const std::function<AnswerType(const unsigned int,
490 const RequestType &)> &answer_request,
491 const std::function<void(const unsigned int, const AnswerType &)>
492 &process_answer,
493 const MPI_Comm comm);
494
532 template <typename RequestType>
533 std::vector<unsigned int>
534 nbx(const std::vector<unsigned int> &targets,
535 const std::function<RequestType(const unsigned int)> &create_request,
536 const std::function<void(const unsigned int, const RequestType &)>
537 &process_request,
538 const MPI_Comm comm);
539
565 template <typename RequestType, typename AnswerType>
566 class PEX : public Interface<RequestType, AnswerType>
567 {
568 public:
572 PEX() = default;
573
577 virtual ~PEX() = default;
578
579 // Import the declarations from the base class.
580 using Interface<RequestType, AnswerType>::run;
581
585 virtual std::vector<unsigned int>
587 const std::vector<unsigned int> &targets,
588 const std::function<RequestType(const unsigned int)> &create_request,
589 const std::function<AnswerType(const unsigned int,
590 const RequestType &)> &answer_request,
591 const std::function<void(const unsigned int, const AnswerType &)>
592 &process_answer,
593 const MPI_Comm comm) override;
594
595 private:
596#ifdef DEAL_II_WITH_MPI
600 std::vector<std::vector<char>> send_buffers;
601
605 std::vector<std::vector<char>> recv_buffers;
606
610 std::vector<MPI_Request> send_request_requests;
611
615 std::vector<std::vector<char>> requests_buffers;
616
620 std::vector<MPI_Request> send_answer_requests;
621#endif
625 std::set<unsigned int> requesting_processes;
626
631 unsigned int
633 const std::vector<unsigned int> &targets,
634 const std::function<RequestType(const unsigned int)> &create_request,
635 const MPI_Comm comm);
636
641 void
643 const unsigned int index,
644 const std::function<AnswerType(const unsigned int,
645 const RequestType &)> &answer_request,
646 const MPI_Comm comm);
647
652 void
654 const unsigned int n_targets,
655 const std::function<void(const unsigned int, const AnswerType &)>
656 &process_answer,
657 const MPI_Comm comm);
658
663 void
665 };
666
667
668
725 template <typename RequestType, typename AnswerType>
726 std::vector<unsigned int>
727 pex(const std::vector<unsigned int> &targets,
728 const std::function<RequestType(const unsigned int)> &create_request,
729 const std::function<AnswerType(const unsigned int,
730 const RequestType &)> &answer_request,
731 const std::function<void(const unsigned int, const AnswerType &)>
732 &process_answer,
733 const MPI_Comm comm);
734
772 template <typename RequestType>
773 std::vector<unsigned int>
774 pex(const std::vector<unsigned int> &targets,
775 const std::function<RequestType(const unsigned int)> &create_request,
776 const std::function<void(const unsigned int, const RequestType &)>
777 &process_request,
778 const MPI_Comm comm);
779
780
785 template <typename RequestType, typename AnswerType>
786 class Serial : public Interface<RequestType, AnswerType>
787 {
788 public:
792 Serial() = default;
793
794 // Import the declarations from the base class.
795 using Interface<RequestType, AnswerType>::run;
796
800 virtual std::vector<unsigned int>
802 const std::vector<unsigned int> &targets,
803 const std::function<RequestType(const unsigned int)> &create_request,
804 const std::function<AnswerType(const unsigned int,
805 const RequestType &)> &answer_request,
806 const std::function<void(const unsigned int, const AnswerType &)>
807 &process_answer,
808 const MPI_Comm comm) override;
809 };
810
811
812
845 template <typename RequestType, typename AnswerType>
846 std::vector<unsigned int>
848 const std::vector<unsigned int> &targets,
849 const std::function<RequestType(const unsigned int)> &create_request,
850 const std::function<AnswerType(const unsigned int, const RequestType &)>
851 &answer_request,
852 const std::function<void(const unsigned int, const AnswerType &)>
853 &process_answer,
854 const MPI_Comm comm);
855
885 template <typename RequestType>
886 std::vector<unsigned int>
888 const std::vector<unsigned int> &targets,
889 const std::function<RequestType(const unsigned int)> &create_request,
890 const std::function<void(const unsigned int, const RequestType &)>
891 &process_request,
892 const MPI_Comm comm);
893
894
895
908 template <typename RequestType, typename AnswerType>
909 class Selector : public Interface<RequestType, AnswerType>
910 {
911 public:
915 Selector() = default;
916
920 virtual ~Selector() = default;
921
922 // Import the declarations from the base class.
923 using Interface<RequestType, AnswerType>::run;
924
930 virtual std::vector<unsigned int>
932 const std::vector<unsigned int> &targets,
933 const std::function<RequestType(const unsigned int)> &create_request,
934 const std::function<AnswerType(const unsigned int,
935 const RequestType &)> &answer_request,
936 const std::function<void(const unsigned int, const AnswerType &)>
937 &process_answer,
938 const MPI_Comm comm) override;
939
940 private:
941 // Pointer to the actual ConsensusAlgorithms::Interface implementation.
942 std::shared_ptr<Interface<RequestType, AnswerType>> consensus_algo;
943 };
944
945
946
991 template <typename RequestType, typename AnswerType>
992 std::vector<unsigned int>
994 const std::vector<unsigned int> &targets,
995 const std::function<RequestType(const unsigned int)> &create_request,
996 const std::function<AnswerType(const unsigned int, const RequestType &)>
997 &answer_request,
998 const std::function<void(const unsigned int, const AnswerType &)>
999 &process_answer,
1000 const MPI_Comm comm);
1001
1039 template <typename RequestType>
1040 std::vector<unsigned int>
1042 const std::vector<unsigned int> &targets,
1043 const std::function<RequestType(const unsigned int)> &create_request,
1044 const std::function<void(const unsigned int, const RequestType &)>
1045 &process_request,
1046 const MPI_Comm comm);
1047
1048
1049
1050#ifndef DOXYGEN
1051 // Implementation of the functions in this namespace.
1052
1053 template <typename RequestType, typename AnswerType>
1054 std::vector<unsigned int>
1055 nbx(const std::vector<unsigned int> &targets,
1056 const std::function<RequestType(const unsigned int)> &create_request,
1057 const std::function<AnswerType(const unsigned int,
1058 const RequestType &)> &answer_request,
1059 const std::function<void(const unsigned int, const AnswerType &)>
1060 &process_answer,
1061 const MPI_Comm comm)
1062 {
1064 targets, create_request, answer_request, process_answer, comm);
1065 }
1066
1067
1068
1069 template <typename RequestType>
1070 std::vector<unsigned int>
1071 nbx(const std::vector<unsigned int> &targets,
1072 const std::function<RequestType(const unsigned int)> &create_request,
1073 const std::function<void(const unsigned int, const RequestType &)>
1074 &process_request,
1075 const MPI_Comm comm)
1076 {
1077 // TODO: For the moment, simply implement this special case by
1078 // forwarding to the other function with rewritten function
1079 // objects and using an empty type as answer type. This way,
1080 // we have the interface in place and can provide a more
1081 // efficient implementation later on.
1082 using EmptyType = std::tuple<>;
1083
1084 return nbx<RequestType, EmptyType>(
1085 targets,
1086 create_request,
1087 // answer_request:
1088 [&process_request](const unsigned int source_rank,
1089 const RequestType &request) -> EmptyType {
1090 process_request(source_rank, request);
1091 // Return something. What it is is arbitrary here, except that
1092 // we want it to be as small an object as possible. Using
1093 // std::tuple<> is interpreted as an empty object that is packed
1094 // down to a zero-length char array.
1095 return {};
1096 },
1097 // process_answer:
1098 [](const unsigned int /*target_rank */,
1099 const EmptyType & /*answer*/) {},
1100 comm);
1101 }
1102
1103
1104
1105 template <typename RequestType, typename AnswerType>
1106 std::vector<unsigned int>
1107 pex(const std::vector<unsigned int> &targets,
1108 const std::function<RequestType(const unsigned int)> &create_request,
1109 const std::function<AnswerType(const unsigned int,
1110 const RequestType &)> &answer_request,
1111 const std::function<void(const unsigned int, const AnswerType &)>
1112 &process_answer,
1113 const MPI_Comm comm)
1114 {
1115 return PEX<RequestType, AnswerType>().run(
1116 targets, create_request, answer_request, process_answer, comm);
1117 }
1118
1119
1120
1121 template <typename RequestType>
1122 std::vector<unsigned int>
1123 pex(const std::vector<unsigned int> &targets,
1124 const std::function<RequestType(const unsigned int)> &create_request,
1125 const std::function<void(const unsigned int, const RequestType &)>
1126 &process_request,
1127 const MPI_Comm comm)
1128 {
1129 // TODO: For the moment, simply implement this special case by
1130 // forwarding to the other function with rewritten function
1131 // objects and using an empty type as answer type. This way,
1132 // we have the interface in place and can provide a more
1133 // efficient implementation later on.
1134 using EmptyType = std::tuple<>;
1135
1136 return pex<RequestType, EmptyType>(
1137 targets,
1138 create_request,
1139 // answer_request:
1140 [&process_request](const unsigned int source_rank,
1141 const RequestType &request) -> EmptyType {
1142 process_request(source_rank, request);
1143 // Return something. What it is is arbitrary here, except that
1144 // we want it to be as small an object as possible. Using
1145 // std::tuple<> is interpreted as an empty object that is packed
1146 // down to a zero-length char array.
1147 return {};
1148 },
1149 // process_answer:
1150 [](const unsigned int /*target_rank */,
1151 const EmptyType & /*answer*/) {},
1152 comm);
1153 }
1154
1155
1156
1157 template <typename RequestType, typename AnswerType>
1158 std::vector<unsigned int>
1159 serial(
1160 const std::vector<unsigned int> &targets,
1161 const std::function<RequestType(const unsigned int)> &create_request,
1162 const std::function<AnswerType(const unsigned int, const RequestType &)>
1163 &answer_request,
1164 const std::function<void(const unsigned int, const AnswerType &)>
1165 &process_answer,
1166 const MPI_Comm comm)
1167 {
1168 return Serial<RequestType, AnswerType>().run(
1169 targets, create_request, answer_request, process_answer, comm);
1170 }
1171
1172
1173
1174 template <typename RequestType>
1175 std::vector<unsigned int>
1176 serial(
1177 const std::vector<unsigned int> &targets,
1178 const std::function<RequestType(const unsigned int)> &create_request,
1179 const std::function<void(const unsigned int, const RequestType &)>
1180 &process_request,
1181 const MPI_Comm comm)
1182 {
1183 // TODO: For the moment, simply implement this special case by
1184 // forwarding to the other function with rewritten function
1185 // objects and using an empty type as answer type. This way,
1186 // we have the interface in place and can provide a more
1187 // efficient implementation later on.
1188 using EmptyType = std::tuple<>;
1189
1190 return serial<RequestType, EmptyType>(
1191 targets,
1192 create_request,
1193 // answer_request:
1194 [&process_request](const unsigned int source_rank,
1195 const RequestType &request) -> EmptyType {
1196 process_request(source_rank, request);
1197 // Return something. What it is is arbitrary here, except that
1198 // we want it to be as small an object as possible. Using
1199 // std::tuple<> is interpreted as an empty object that is packed
1200 // down to a zero-length char array.
1201 return {};
1202 },
1203 // process_answer:
1204 [](const unsigned int /*target_rank */,
1205 const EmptyType & /*answer*/) {},
1206 comm);
1207 }
1208
1209
1210
1211 template <typename RequestType, typename AnswerType>
1212 std::vector<unsigned int>
1213 selector(
1214 const std::vector<unsigned int> &targets,
1215 const std::function<RequestType(const unsigned int)> &create_request,
1216 const std::function<AnswerType(const unsigned int, const RequestType &)>
1217 &answer_request,
1218 const std::function<void(const unsigned int, const AnswerType &)>
1219 &process_answer,
1220 const MPI_Comm comm)
1221 {
1222 return Selector<RequestType, AnswerType>().run(
1223 targets, create_request, answer_request, process_answer, comm);
1224 }
1225
1226
1227
1228 template <typename RequestType>
1229 std::vector<unsigned int>
1230 selector(
1231 const std::vector<unsigned int> &targets,
1232 const std::function<RequestType(const unsigned int)> &create_request,
1233 const std::function<void(const unsigned int, const RequestType &)>
1234 &process_request,
1235 const MPI_Comm comm)
1236 {
1237 // TODO: For the moment, simply implement this special case by
1238 // forwarding to the other function with rewritten function
1239 // objects and using an empty type as answer type. This way,
1240 // we have the interface in place and can provide a more
1241 // efficient implementation later on.
1242 using EmptyType = std::tuple<>;
1243
1244 return selector<RequestType, EmptyType>(
1245 targets,
1246 create_request,
1247 // answer_request:
1248 [&process_request](const unsigned int source_rank,
1249 const RequestType &request) -> EmptyType {
1250 process_request(source_rank, request);
1251 // Return something. What it is is arbitrary here, except that
1252 // we want it to be as small an object as possible. Using
1253 // std::tuple<> is interpreted as an empty object that is packed
1254 // down to a zero-length char array.
1255 return {};
1256 },
1257 // process_answer:
1258 [](const unsigned int /*target_rank */,
1259 const EmptyType & /*answer*/) {},
1260 comm);
1261 }
1262
1263#endif
1264
1265
1266 } // namespace ConsensusAlgorithms
1267 } // end of namespace MPI
1268} // end of namespace Utilities
1269
1270
1271
1272#ifndef DOXYGEN
1273
1274// ----------------- Implementation of template functions
1275
1276namespace Utilities
1277{
1278 namespace MPI
1279 {
1280 namespace ConsensusAlgorithms
1281 {
1282 namespace
1283 {
1288 inline bool
1289 has_unique_elements(const std::vector<unsigned int> &targets)
1290 {
1291 std::vector<unsigned int> my_destinations = targets;
1292 std::sort(my_destinations.begin(), my_destinations.end());
1293 return (std::adjacent_find(my_destinations.begin(),
1294 my_destinations.end()) ==
1295 my_destinations.end());
1296 }
1297
1298
1299
1303 inline void
1304 handle_exception(std::exception_ptr &&exception, const MPI_Comm comm)
1305 {
1306# ifdef DEAL_II_WITH_MPI
1307 // an exception within a ConsensusAlgorithm likely causes an
1308 // MPI deadlock. Abort with a reasonable error message instead.
1309 try
1310 {
1311 std::rethrow_exception(exception);
1312 }
1313 catch (ExceptionBase &exc)
1314 {
1315 // report name of the deal.II exception:
1316 std::cerr
1317 << std::endl
1318 << std::endl
1319 << "----------------------------------------------------"
1320 << std::endl;
1321 std::cerr
1322 << "Exception '" << exc.get_exc_name() << "'"
1323 << " on rank " << Utilities::MPI::this_mpi_process(comm)
1324 << " on processing: " << std::endl
1325 << exc.what() << std::endl
1326 << "Aborting!" << std::endl
1327 << "----------------------------------------------------"
1328 << std::endl;
1329
1330 // Then bring down the whole MPI world
1331 MPI_Abort(comm, 255);
1332 }
1333 catch (std::exception &exc)
1334 {
1335 std::cerr
1336 << std::endl
1337 << std::endl
1338 << "----------------------------------------------------"
1339 << std::endl;
1340 std::cerr
1341 << "Exception within ConsensusAlgorithm"
1342 << " on rank " << Utilities::MPI::this_mpi_process(comm)
1343 << " on processing: " << std::endl
1344 << exc.what() << std::endl
1345 << "Aborting!" << std::endl
1346 << "----------------------------------------------------"
1347 << std::endl;
1348
1349 // Then bring down the whole MPI world
1350 MPI_Abort(comm, 255);
1351 }
1352 catch (...)
1353 {
1354 std::cerr
1355 << std::endl
1356 << std::endl
1357 << "----------------------------------------------------"
1358 << std::endl;
1359 std::cerr
1360 << "Unknown exception within ConsensusAlgorithm!" << std::endl
1361 << "Aborting!" << std::endl
1362 << "----------------------------------------------------"
1363 << std::endl;
1364
1365 // Then bring down the whole MPI world
1366 MPI_Abort(comm, 255);
1367 }
1368# else
1369 (void)comm;
1370
1371 // No need to be concerned about deadlocks without MPI.
1372 // Defer to exception handling further up the callstack.
1373 std::rethrow_exception(exception);
1374# endif
1375 }
1376 } // namespace
1377
1378
1379
1380 template <typename RequestType, typename AnswerType>
1381 void
1383 const RequestType &,
1384 AnswerType &)
1385 {
1386 // nothing to do
1387 }
1388
1389
1390
1391 template <typename RequestType, typename AnswerType>
1392 void
1394 RequestType &)
1395 {
1396 // nothing to do
1397 }
1398
1399
1400
1401 template <typename RequestType, typename AnswerType>
1402 void
1404 const AnswerType &)
1405 {
1406 // nothing to do
1407 }
1408
1409
1410
1411 template <typename RequestType, typename AnswerType>
1412 std::vector<unsigned int>
1414 Process<RequestType, AnswerType> &process,
1415 const MPI_Comm comm)
1416 {
1417 // Unpack the 'process' object and call the function that takes
1418 // function objects for all operations.
1419 return run(
1420 process.compute_targets(),
1421 /* create_request: */
1422 [&process](const unsigned int target) {
1423 RequestType request;
1424 process.create_request(target, request);
1425 return request;
1426 },
1427 /* answer_request: */
1428 [&process](const unsigned int source, const RequestType &request) {
1429 AnswerType answer;
1430 process.answer_request(source, request, answer);
1431 return answer;
1432 },
1433 /* process_answer: */
1434 [&process](const unsigned int target, const AnswerType &answer) {
1435 process.read_answer(target, answer);
1436 },
1437 comm);
1438 }
1439
1440
1441
1442 template <typename RequestType, typename AnswerType>
1443 std::vector<unsigned int>
1445 const std::vector<unsigned int> &targets,
1446 const std::function<RequestType(const unsigned int)> &create_request,
1447 const std::function<AnswerType(const unsigned int, const RequestType &)>
1448 &answer_request,
1449 const std::function<void(const unsigned int, const AnswerType &)>
1450 &process_answer,
1451 const MPI_Comm comm)
1452 {
1453 Assert(has_unique_elements(targets),
1454 ExcMessage("The consensus algorithms expect that each process "
1455 "only sends a single message to another process, "
1456 "but the targets provided include duplicates."));
1457
1458 static CollectiveMutex mutex;
1459 CollectiveMutex::ScopedLock lock(mutex, comm);
1460
1461 try
1462 {
1463 // 1) Send data to identified targets and start receiving
1464 // the answers from these very same processes.
1465 start_communication(targets, create_request, comm);
1466
1467 // 2) Until all posted receive operations are known to have
1468 // completed, answer requests and keep checking whether all
1469 // requests of this process have been answered.
1470 //
1471 // The requests that we catch in the answer_requests()
1472 // function originate elsewhere, that is, they are not in
1473 // response to our own messages
1474 //
1475 // Note also that we may not catch all incoming requests in
1476 // the following two lines: our own requests may have been
1477 // satisfied before we've dealt with all incoming requests.
1478 // That's ok: We will get around to dealing with all
1479 // remaining message later. We just want to move on to the
1480 // next step as early as possible.
1481 while (all_locally_originated_receives_are_completed(process_answer,
1482 comm) == false)
1483 maybe_answer_one_request(answer_request, comm);
1484
1485 // 3) Signal to all other processes that all requests of this
1486 // process have been answered
1487 signal_finish(comm);
1488
1489 // 4) Nevertheless, this process has to keep on answering
1490 // (potential) incoming requests until all processes have
1491 // received the answer to all requests
1492 while (all_remotely_originated_receives_are_completed() == false)
1493 maybe_answer_one_request(answer_request, comm);
1494
1495 // 5) process the answer to all requests
1496 clean_up_and_end_communication(comm);
1497 }
1498 catch (...)
1499 {
1500 handle_exception(std::current_exception(), comm);
1501 }
1502
1503 return std::vector<unsigned int>(requesting_processes.begin(),
1504 requesting_processes.end());
1505 }
1506
1507
1508
1509 template <typename RequestType, typename AnswerType>
1510 void
1512 const std::vector<unsigned int> &targets,
1513 const std::function<RequestType(const unsigned int)> &create_request,
1514 const MPI_Comm comm)
1515 {
1516# ifdef DEAL_II_WITH_MPI
1517 // 1)
1518 const auto n_targets = targets.size();
1519
1520 const int tag_request = Utilities::MPI::internal::Tags::
1522
1523 // 2) allocate memory
1524 send_requests.resize(n_targets);
1525 send_buffers.resize(n_targets);
1526
1527 {
1528 // 4) send and receive
1529 for (unsigned int index = 0; index < n_targets; ++index)
1530 {
1531 const unsigned int rank = targets[index];
1533
1534 auto &send_buffer = send_buffers[index];
1535 send_buffer =
1536 (create_request ? Utilities::pack(create_request(rank), false) :
1537 std::vector<char>());
1538
1539 // Post a request to send data
1540 auto ierr = MPI_Isend(send_buffer.data(),
1541 send_buffer.size(),
1542 MPI_CHAR,
1543 rank,
1544 tag_request,
1545 comm,
1546 &send_requests[index]);
1547 AssertThrowMPI(ierr);
1548 }
1549
1550 // Also record that we expect an answer from each target we sent
1551 // a request to:
1552 n_outstanding_answers = n_targets;
1553 }
1554# else
1555 (void)targets;
1556 (void)create_request;
1557 (void)comm;
1558# endif
1559 }
1560
1561
1562
1563 template <typename RequestType, typename AnswerType>
1564 bool
1567 const std::function<void(const unsigned int, const AnswerType &)>
1568 &process_answer,
1569 const MPI_Comm comm)
1570 {
1571# ifdef DEAL_II_WITH_MPI
1572 // We know that all requests have come in when we have pending
1573 // messages from all targets with the right tag (some of which we may
1574 // have already taken care of below, after discovering their existence).
1575 // We can check for pending messages with MPI_IProbe, which returns
1576 // immediately with a return code that indicates whether
1577 // it has found a message from any process with a given
1578 // tag.
1579 if (n_outstanding_answers == 0)
1580 return true;
1581 else
1582 {
1583 const int tag_deliver = Utilities::MPI::internal::Tags::
1585
1586 int request_is_pending;
1587 MPI_Status status;
1588 const auto ierr = MPI_Iprobe(
1589 MPI_ANY_SOURCE, tag_deliver, comm, &request_is_pending, &status);
1590 AssertThrowMPI(ierr);
1591
1592 // If there is no pending message with this tag,
1593 // then we are clearly not done receiving everything
1594 // yet -- so return false.
1595 if (request_is_pending == 0)
1596 return false;
1597 else
1598 {
1599 // OK, so we have gotten a reply to our request from
1600 // one rank. Let us process it.
1601 const auto target = status.MPI_SOURCE;
1602
1603 // Then query the size of the message, allocate enough memory,
1604 // receive the data, and process it.
1605 int message_size;
1606 {
1607 const int ierr =
1608 MPI_Get_count(&status, MPI_CHAR, &message_size);
1609 AssertThrowMPI(ierr);
1610 }
1611 std::vector<char> recv_buffer(message_size);
1612
1613 {
1614 const int tag_deliver = Utilities::MPI::internal::Tags::
1616
1617 const int ierr = MPI_Recv(recv_buffer.data(),
1618 recv_buffer.size(),
1619 MPI_CHAR,
1620 target,
1621 tag_deliver,
1622 comm,
1623 MPI_STATUS_IGNORE);
1624 AssertThrowMPI(ierr);
1625 }
1626
1627 if (process_answer)
1628 process_answer(target,
1629 Utilities::unpack<AnswerType>(recv_buffer,
1630 false));
1631
1632 // Finally, remove this rank from the list of outstanding
1633 // targets:
1634 --n_outstanding_answers;
1635
1636 // We could do another go-around from the top of this
1637 // else-branch to see whether there are actually other messages
1638 // that are currently pending. But that would mean spending
1639 // substantial time in receiving answers while we should also be
1640 // sending answers to requests we have received from other
1641 // places. So let it be enough for now. If there are outstanding
1642 // answers, we will get back to this function before long and
1643 // can take care of them then.
1644 return (n_outstanding_answers == 0);
1645 }
1646 }
1647
1648# else
1649 (void)process_answer;
1650 (void)comm;
1651
1652 return true;
1653# endif
1654 }
1655
1656
1657
1658 template <typename RequestType, typename AnswerType>
1659 void
1661 const std::function<AnswerType(const unsigned int, const RequestType &)>
1662 &answer_request,
1663 const MPI_Comm comm)
1664 {
1665# ifdef DEAL_II_WITH_MPI
1666
1667 const int tag_request = Utilities::MPI::internal::Tags::
1669 const int tag_deliver = Utilities::MPI::internal::Tags::
1671
1672 // Check if there is a request pending. By selecting the
1673 // tag_request tag, these are other processes asking for
1674 // our own replies, not these other processes' replies
1675 // to our own requests.
1676 //
1677 // There may be multiple such pending messages. We
1678 // only answer one.
1679 MPI_Status status;
1680 int request_is_pending;
1681 const auto ierr = MPI_Iprobe(
1682 MPI_ANY_SOURCE, tag_request, comm, &request_is_pending, &status);
1683 AssertThrowMPI(ierr);
1684
1685 if (request_is_pending != 0)
1686 {
1687 // Get the rank of the requesting process and add it to the
1688 // list of requesting processes (which may contain duplicates).
1689 const auto other_rank = status.MPI_SOURCE;
1690
1691 Assert(requesting_processes.find(other_rank) ==
1692 requesting_processes.end(),
1693 ExcMessage("Process is requesting a second time!"));
1694 requesting_processes.insert(other_rank);
1695
1696 // get size of incoming message
1697 int number_amount;
1698 auto ierr = MPI_Get_count(&status, MPI_CHAR, &number_amount);
1699 AssertThrowMPI(ierr);
1700
1701 // allocate memory for incoming message
1702 std::vector<char> buffer_recv(number_amount);
1703 ierr = MPI_Recv(buffer_recv.data(),
1704 number_amount,
1705 MPI_CHAR,
1706 other_rank,
1707 tag_request,
1708 comm,
1709 MPI_STATUS_IGNORE);
1710 AssertThrowMPI(ierr);
1711
1712 // Allocate memory for an answer message to the current request,
1713 // and ask the 'process' object to produce an answer:
1714 request_buffers.emplace_back(std::make_unique<std::vector<char>>());
1715 auto &request_buffer = *request_buffers.back();
1716 if (answer_request)
1717 request_buffer =
1718 Utilities::pack(answer_request(other_rank,
1719 Utilities::unpack<RequestType>(
1720 buffer_recv, false)),
1721 false);
1722
1723 // Then initiate sending the answer back to the requester.
1724 request_requests.emplace_back(std::make_unique<MPI_Request>());
1725 ierr = MPI_Isend(request_buffer.data(),
1726 request_buffer.size(),
1727 MPI_CHAR,
1728 other_rank,
1729 tag_deliver,
1730 comm,
1731 request_requests.back().get());
1732 AssertThrowMPI(ierr);
1733 }
1734# else
1735 (void)answer_request;
1736 (void)comm;
1737# endif
1738 }
1739
1740
1741
1742 template <typename RequestType, typename AnswerType>
1743 void
1745 {
1746# ifdef DEAL_II_WITH_MPI
1747 const auto ierr = MPI_Ibarrier(comm, &barrier_request);
1748 AssertThrowMPI(ierr);
1749# else
1750 (void)comm;
1751# endif
1752 }
1753
1754
1755
1756 template <typename RequestType, typename AnswerType>
1757 bool
1758 NBX<RequestType,
1759 AnswerType>::all_remotely_originated_receives_are_completed()
1760 {
1761# ifdef DEAL_II_WITH_MPI
1762 int all_ranks_reached_barrier;
1763 const auto ierr = MPI_Test(&barrier_request,
1764 &all_ranks_reached_barrier,
1765 MPI_STATUS_IGNORE);
1766 AssertThrowMPI(ierr);
1767 return all_ranks_reached_barrier != 0;
1768# else
1769 return true;
1770# endif
1771 }
1772
1773
1774
1775 template <typename RequestType, typename AnswerType>
1776 void
1778 const MPI_Comm comm)
1779 {
1780 (void)comm;
1781# ifdef DEAL_II_WITH_MPI
1782 // clean up
1783 {
1784 if (send_requests.size() > 0)
1785 {
1786 const int ierr = MPI_Waitall(send_requests.size(),
1787 send_requests.data(),
1788 MPI_STATUSES_IGNORE);
1789 AssertThrowMPI(ierr);
1790 }
1791
1792 int ierr = MPI_Wait(&barrier_request, MPI_STATUS_IGNORE);
1793 AssertThrowMPI(ierr);
1794
1795 for (auto &i : request_requests)
1796 {
1797 ierr = MPI_Wait(i.get(), MPI_STATUS_IGNORE);
1798 AssertThrowMPI(ierr);
1799 }
1800
1801# ifdef DEBUG
1802 // note: IBarrier seems to make problem during testing, this
1803 // additional Barrier seems to help
1804 ierr = MPI_Barrier(comm);
1805 AssertThrowMPI(ierr);
1806# endif
1807 }
1808# endif
1809 }
1810
1811
1812
1813 template <typename RequestType, typename AnswerType>
1814 std::vector<unsigned int>
1816 const std::vector<unsigned int> &targets,
1817 const std::function<RequestType(const unsigned int)> &create_request,
1818 const std::function<AnswerType(const unsigned int, const RequestType &)>
1819 &answer_request,
1820 const std::function<void(const unsigned int, const AnswerType &)>
1821 &process_answer,
1822 const MPI_Comm comm)
1823 {
1824 Assert(has_unique_elements(targets),
1825 ExcMessage("The consensus algorithms expect that each process "
1826 "only sends a single message to another process, "
1827 "but the targets provided include duplicates."));
1828
1829 static CollectiveMutex mutex;
1830 CollectiveMutex::ScopedLock lock(mutex, comm);
1831
1832 try
1833 {
1834 // 1) Send requests and start receiving the answers.
1835 // In particular, determine how many requests we should expect
1836 // on the current process.
1837 const unsigned int n_requests =
1838 start_communication(targets, create_request, comm);
1839
1840 // 2) Answer requests:
1841 for (unsigned int request = 0; request < n_requests; ++request)
1842 answer_one_request(request, answer_request, comm);
1843
1844 // 3) Process answers:
1845 process_incoming_answers(targets.size(), process_answer, comm);
1846
1847 // 4) Make sure all sends have successfully terminated:
1848 clean_up_and_end_communication();
1849 }
1850 catch (...)
1851 {
1852 handle_exception(std::current_exception(), comm);
1853 }
1854
1855 return std::vector<unsigned int>(requesting_processes.begin(),
1856 requesting_processes.end());
1857 }
1858
1859
1860
1861 template <typename RequestType, typename AnswerType>
1862 unsigned int
1864 const std::vector<unsigned int> &targets,
1865 const std::function<RequestType(const unsigned int)> &create_request,
1866 const MPI_Comm comm)
1867 {
1868# ifdef DEAL_II_WITH_MPI
1869 const int tag_request = Utilities::MPI::internal::Tags::
1871
1872 // 1) determine with which processes this process wants to communicate
1873 // with
1874 const unsigned int n_targets = targets.size();
1875
1876 // 2) determine who wants to communicate with this process
1877 const unsigned int n_sources =
1879
1880 // 2) allocate memory
1881 recv_buffers.resize(n_targets);
1882 send_buffers.resize(n_targets);
1883 send_request_requests.resize(n_targets);
1884
1885 send_answer_requests.resize(n_sources);
1886 requests_buffers.resize(n_sources);
1887
1888 // 4) send and receive
1889 for (unsigned int i = 0; i < n_targets; ++i)
1890 {
1891 const unsigned int rank = targets[i];
1893
1894 // pack data which should be sent
1895 auto &send_buffer = send_buffers[i];
1896 if (create_request)
1897 send_buffer = Utilities::pack(create_request(rank), false);
1898
1899 // start to send data
1900 auto ierr = MPI_Isend(send_buffer.data(),
1901 send_buffer.size(),
1902 MPI_CHAR,
1903 rank,
1904 tag_request,
1905 comm,
1906 &send_request_requests[i]);
1907 AssertThrowMPI(ierr);
1908 }
1909
1910 return n_sources;
1911# else
1912 (void)targets;
1913 (void)create_request;
1914 (void)comm;
1915 return 0;
1916# endif
1917 }
1918
1919
1920
1921 template <typename RequestType, typename AnswerType>
1922 void
1924 const unsigned int index,
1925 const std::function<AnswerType(const unsigned int, const RequestType &)>
1926 &answer_request,
1927 const MPI_Comm comm)
1928 {
1929# ifdef DEAL_II_WITH_MPI
1930 const int tag_request = Utilities::MPI::internal::Tags::
1932 const int tag_deliver = Utilities::MPI::internal::Tags::
1934
1935 // Wait until we have a message ready for retrieval, though we don't
1936 // care which process it is from.
1937 MPI_Status status;
1938 int ierr = MPI_Probe(MPI_ANY_SOURCE, tag_request, comm, &status);
1939 AssertThrowMPI(ierr);
1940
1941 // Get rank of incoming message and verify that it makes sense
1942 const unsigned int other_rank = status.MPI_SOURCE;
1943
1944 Assert(requesting_processes.find(other_rank) ==
1945 requesting_processes.end(),
1946 ExcMessage(
1947 "A process is sending a request after a request from "
1948 "the same process has previously already been "
1949 "received. This algorithm does not expect this to happen."));
1950 requesting_processes.insert(other_rank);
1951
1952 // Actually get the incoming message:
1953 int number_amount;
1954 ierr = MPI_Get_count(&status, MPI_CHAR, &number_amount);
1955 AssertThrowMPI(ierr);
1956
1957 std::vector<char> buffer_recv(number_amount);
1958 ierr = MPI_Recv(buffer_recv.data(),
1959 number_amount,
1960 MPI_CHAR,
1961 other_rank,
1962 tag_request,
1963 comm,
1964 &status);
1965 AssertThrowMPI(ierr);
1966
1967 // Process request by asking the user-provided function for
1968 // the answer and post a send for it.
1969 auto &request_buffer = requests_buffers[index];
1970 request_buffer =
1971 (answer_request ?
1972 Utilities::pack(answer_request(other_rank,
1973 Utilities::unpack<RequestType>(
1974 buffer_recv, false)),
1975 false) :
1976 std::vector<char>());
1977
1978 ierr = MPI_Isend(request_buffer.data(),
1979 request_buffer.size(),
1980 MPI_CHAR,
1981 other_rank,
1982 tag_deliver,
1983 comm,
1984 &send_answer_requests[index]);
1985 AssertThrowMPI(ierr);
1986# else
1987 (void)answer_request;
1988 (void)comm;
1989 (void)index;
1990# endif
1991 }
1992
1993
1994
1995 template <typename RequestType, typename AnswerType>
1996 void
1998 const unsigned int n_targets,
1999 const std::function<void(const unsigned int, const AnswerType &)>
2000 &process_answer,
2001 const MPI_Comm comm)
2002 {
2003# ifdef DEAL_II_WITH_MPI
2004 const int tag_deliver = Utilities::MPI::internal::Tags::
2006
2007 // We know how many targets we have sent requests to. These
2008 // targets will all eventually send us their responses, but
2009 // we need not process them in order -- rather, just see what
2010 // comes in and then look at message originators' ranks and
2011 // message sizes
2012 for (unsigned int i = 0; i < n_targets; ++i)
2013 {
2014 MPI_Status status;
2015 {
2016 const int ierr =
2017 MPI_Probe(MPI_ANY_SOURCE, tag_deliver, comm, &status);
2018 AssertThrowMPI(ierr);
2019 }
2020
2021 const auto other_rank = status.MPI_SOURCE;
2022 int message_size;
2023 {
2024 const int ierr = MPI_Get_count(&status, MPI_CHAR, &message_size);
2025 AssertThrowMPI(ierr);
2026 }
2027 std::vector<char> recv_buffer(message_size);
2028
2029 // Now actually receive the answer. Because the MPI_Probe
2030 // above blocks until we have a message, we know that the
2031 // following MPI_Recv call will immediately succeed.
2032 {
2033 const int ierr = MPI_Recv(recv_buffer.data(),
2034 recv_buffer.size(),
2035 MPI_CHAR,
2036 other_rank,
2037 tag_deliver,
2038 comm,
2039 MPI_STATUS_IGNORE);
2040 AssertThrowMPI(ierr);
2041 }
2042
2043 if (process_answer)
2044 process_answer(other_rank,
2045 Utilities::unpack<AnswerType>(recv_buffer, false));
2046 }
2047# else
2048 (void)n_targets;
2049 (void)process_answer;
2050 (void)comm;
2051# endif
2052 }
2053
2054
2055
2056 template <typename RequestType, typename AnswerType>
2057 void
2059 {
2060# ifdef DEAL_II_WITH_MPI
2061 // Finalize all MPI_Request objects for both the
2062 // send-request and receive-answer operations.
2063 if (send_request_requests.size() > 0)
2064 {
2065 const int ierr = MPI_Waitall(send_request_requests.size(),
2066 send_request_requests.data(),
2067 MPI_STATUSES_IGNORE);
2068 AssertThrowMPI(ierr);
2069 }
2070
2071 // Then also check the send-answer requests.
2072 if (send_answer_requests.size() > 0)
2073 {
2074 const int ierr = MPI_Waitall(send_answer_requests.size(),
2075 send_answer_requests.data(),
2076 MPI_STATUSES_IGNORE);
2077 AssertThrowMPI(ierr);
2078 }
2079# endif
2080 }
2081
2082
2083
2084 template <typename RequestType, typename AnswerType>
2085 std::vector<unsigned int>
2087 const std::vector<unsigned int> &targets,
2088 const std::function<RequestType(const unsigned int)> &create_request,
2089 const std::function<AnswerType(const unsigned int, const RequestType &)>
2090 &answer_request,
2091 const std::function<void(const unsigned int, const AnswerType &)>
2092 &process_answer,
2093 const MPI_Comm comm)
2094 {
2095 (void)comm;
2097 ExcMessage("You shouldn't use the 'Serial' class on "
2098 "communicators that have more than one process "
2099 "associated with it."));
2100
2101 // The only valid target for a serial program is itself.
2102 if (targets.size() != 0)
2103 {
2104 Assert(targets.size() == 1,
2105 ExcMessage(
2106 "On a single process, the only valid target "
2107 "is process zero (the process itself), which can only be "
2108 "listed once."));
2109 AssertDimension(targets[0], 0);
2110
2111 // Since the caller indicates that there is a target, and since we
2112 // know that it is the current process, let the process send
2113 // something to itself.
2114 const RequestType request =
2115 (create_request ? create_request(0) : RequestType());
2116 const AnswerType answer =
2117 (answer_request ? answer_request(0, request) : AnswerType());
2118
2119 if (process_answer)
2120 process_answer(0, answer);
2121 }
2122
2123 return targets; // nothing to do
2124 }
2125
2126
2127
2128 template <typename RequestType, typename AnswerType>
2129 std::vector<unsigned int>
2131 const std::vector<unsigned int> &targets,
2132 const std::function<RequestType(const unsigned int)> &create_request,
2133 const std::function<AnswerType(const unsigned int, const RequestType &)>
2134 &answer_request,
2135 const std::function<void(const unsigned int, const AnswerType &)>
2136 &process_answer,
2137 const MPI_Comm comm)
2138 {
2139 // Depending on the number of processes we switch between
2140 // implementations. We reduce the threshold for debug mode to be
2141 // able to test also the non-blocking implementation. This feature
2142 // is tested by:
2143 // tests/multigrid/transfer_matrix_free_06.with_mpi=true.with_p4est=true.with_trilinos=true.mpirun=10.output
2144
2145 const unsigned int n_procs = (Utilities::MPI::job_supports_mpi() ?
2147 1);
2148# ifdef DEAL_II_WITH_MPI
2149# ifdef DEBUG
2150 if (n_procs > 10)
2151# else
2152 if (n_procs > 99)
2153# endif
2154 consensus_algo.reset(new NBX<RequestType, AnswerType>());
2155 else
2156# endif
2157 if (n_procs > 1)
2158 consensus_algo.reset(new PEX<RequestType, AnswerType>());
2159 else
2160 consensus_algo.reset(new Serial<RequestType, AnswerType>());
2161
2162 return consensus_algo->run(
2163 targets, create_request, answer_request, process_answer, comm);
2164 }
2165
2166
2167 } // namespace ConsensusAlgorithms
2168 } // end of namespace MPI
2169} // end of namespace Utilities
2170
2171#endif // DOXYGEN
2172
2173
2175
2176#endif
const char * get_exc_name() const
virtual const char * what() const noexcept override
std::vector< unsigned int > run(Process< RequestType, AnswerType > &process, const MPI_Comm comm)
virtual std::vector< unsigned int > run(const std::vector< unsigned int > &targets, const std::function< RequestType(const unsigned int)> &create_request, const std::function< AnswerType(const unsigned int, const RequestType &)> &answer_request, const std::function< void(const unsigned int, const AnswerType &)> &process_answer, const MPI_Comm comm)=0
void start_communication(const std::vector< unsigned int > &targets, const std::function< RequestType(const unsigned int)> &create_request, const MPI_Comm comm)
void maybe_answer_one_request(const std::function< AnswerType(const unsigned int, const RequestType &)> &answer_request, const MPI_Comm comm)
std::vector< std::unique_ptr< std::vector< char > > > request_buffers
virtual std::vector< unsigned int > run(const std::vector< unsigned int > &targets, const std::function< RequestType(const unsigned int)> &create_request, const std::function< AnswerType(const unsigned int, const RequestType &)> &answer_request, const std::function< void(const unsigned int, const AnswerType &)> &process_answer, const MPI_Comm comm) override
void clean_up_and_end_communication(const MPI_Comm comm)
bool all_locally_originated_receives_are_completed(const std::function< void(const unsigned int, const AnswerType &)> &process_answer, const MPI_Comm comm)
std::vector< std::unique_ptr< MPI_Request > > request_requests
std::vector< std::vector< char > > send_buffers
void signal_finish(const MPI_Comm comm)
unsigned int start_communication(const std::vector< unsigned int > &targets, const std::function< RequestType(const unsigned int)> &create_request, const MPI_Comm comm)
std::vector< std::vector< char > > requests_buffers
virtual std::vector< unsigned int > run(const std::vector< unsigned int > &targets, const std::function< RequestType(const unsigned int)> &create_request, const std::function< AnswerType(const unsigned int, const RequestType &)> &answer_request, const std::function< void(const unsigned int, const AnswerType &)> &process_answer, const MPI_Comm comm) override
std::vector< std::vector< char > > send_buffers
void answer_one_request(const unsigned int index, const std::function< AnswerType(const unsigned int, const RequestType &)> &answer_request, const MPI_Comm comm)
std::vector< std::vector< char > > recv_buffers
void process_incoming_answers(const unsigned int n_targets, const std::function< void(const unsigned int, const AnswerType &)> &process_answer, const MPI_Comm comm)
virtual std::vector< unsigned int > compute_targets()=0
virtual void answer_request(const unsigned int other_rank, const RequestType &buffer_recv, AnswerType &request_buffer)
virtual void read_answer(const unsigned int other_rank, const AnswerType &recv_buffer)
virtual void create_request(const unsigned int other_rank, RequestType &send_buffer)
virtual std::vector< unsigned int > run(const std::vector< unsigned int > &targets, const std::function< RequestType(const unsigned int)> &create_request, const std::function< AnswerType(const unsigned int, const RequestType &)> &answer_request, const std::function< void(const unsigned int, const AnswerType &)> &process_answer, const MPI_Comm comm) override
std::shared_ptr< Interface< RequestType, AnswerType > > consensus_algo
virtual std::vector< unsigned int > run(const std::vector< unsigned int > &targets, const std::function< RequestType(const unsigned int)> &create_request, const std::function< AnswerType(const unsigned int, const RequestType &)> &answer_request, const std::function< void(const unsigned int, const AnswerType &)> &process_answer, const MPI_Comm comm) override
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:498
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:499
#define Assert(cond, exc)
#define AssertDimension(dim1, dim2)
#define AssertThrowMPI(error_code)
#define AssertIndexRange(index, range)
static ::ExceptionBase & ExcMessage(std::string arg1)
const MPI_Comm comm
Definition mpi.cc:913
const unsigned int n_procs
Definition mpi.cc:924
std::vector< unsigned int > selector(const std::vector< unsigned int > &targets, const std::function< RequestType(const unsigned int)> &create_request, const std::function< AnswerType(const unsigned int, const RequestType &)> &answer_request, const std::function< void(const unsigned int, const AnswerType &)> &process_answer, const MPI_Comm comm)
std::vector< unsigned int > serial(const std::vector< unsigned int > &targets, const std::function< RequestType(const unsigned int)> &create_request, const std::function< AnswerType(const unsigned int, const RequestType &)> &answer_request, const std::function< void(const unsigned int, const AnswerType &)> &process_answer, const MPI_Comm comm)
std::vector< unsigned int > nbx(const std::vector< unsigned int > &targets, const std::function< RequestType(const unsigned int)> &create_request, const std::function< AnswerType(const unsigned int, const RequestType &)> &answer_request, const std::function< void(const unsigned int, const AnswerType &)> &process_answer, const MPI_Comm comm)
std::vector< unsigned int > pex(const std::vector< unsigned int > &targets, const std::function< RequestType(const unsigned int)> &create_request, const std::function< AnswerType(const unsigned int, const RequestType &)> &answer_request, const std::function< void(const unsigned int, const AnswerType &)> &process_answer, const MPI_Comm comm)
@ consensus_algorithm_nbx_process_deliver
ConsensusAlgorithms::NBX::process.
Definition mpi_tags.h:89
@ consensus_algorithm_pex_process_deliver
ConsensusAlgorithms::PEX::process.
Definition mpi_tags.h:94
@ consensus_algorithm_nbx_answer_request
ConsensusAlgorithms::NBX::process.
Definition mpi_tags.h:87
@ consensus_algorithm_pex_answer_request
ConsensusAlgorithms::PEX::process.
Definition mpi_tags.h:92
unsigned int n_mpi_processes(const MPI_Comm mpi_communicator)
Definition mpi.cc:92
unsigned int this_mpi_process(const MPI_Comm mpi_communicator)
Definition mpi.cc:107
bool job_supports_mpi()
Definition mpi.cc:681
unsigned int compute_n_point_to_point_communications(const MPI_Comm mpi_comm, const std::vector< unsigned int > &destinations)
Definition mpi.cc:362
size_t pack(const T &object, std::vector< char > &dest_buffer, const bool allow_compression=true)
Definition utilities.h:1381
void run(const Iterator &begin, const std_cxx20::type_identity_t< Iterator > &end, Worker worker, Copier copier, const ScratchData &sample_scratch_data, const CopyData &sample_copy_data, const unsigned int queue_length, const unsigned int chunk_size)
STL namespace.