Skip to content

Commit a1b4d1a

Browse files
committed
Remove lock/unlock test, add API for active target communication
1 parent 0cbecc2 commit a1b4d1a

File tree

5 files changed

+206
-36
lines changed

5 files changed

+206
-36
lines changed

c++/mpi/group.hpp

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// Copyright (c) 2024 Simons Foundation
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0.txt
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
//
15+
// Authors: Thomas Hahn, Alexander Hampel, Olivier Parcollet, Nils Wentzell
16+
17+
/**
18+
* @file
19+
* @brief Provides a C++ wrapper class for an @p MPI_Group object.
20+
*/
21+
22+
#pragma once
23+
24+
#include "./communicator.hpp"
25+
#include "./environment.hpp"
26+
27+
#include <mpi.h>
28+
29+
#include <cstdlib>
30+
#include <unistd.h>
31+
32+
namespace mpi {
33+
34+
/**
35+
* @ingroup mpi_essentials
36+
* @brief C++ wrapper around @p MPI_Group providing various convenience functions.
37+
*
38+
* @details It stores an @p MPI_Group object as its only member which by default is set to @p MPI_GROUP_NULL.
39+
*/
40+
class group {
41+
// Wrapped `MPI_Comm` object.
42+
MPI_Group _grp = MPI_GROUP_NULL;
43+
44+
public:
45+
/// Construct a communicator with @p MPI_GROUP_NULL.
46+
group() = default;
47+
48+
/// Deleted copy constructor.
49+
group(group const&) = delete;
50+
51+
/// Deleted copy assignment operator.
52+
group& operator=(group const&) = delete;
53+
54+
/// Move constructor leaves moved-from object with @p MPI_GROUP_NULL.
55+
group(group &&other) noexcept : _grp{std::exchange(other._grp, MPI_GROUP_NULL)} {}
56+
57+
/// Move assignment operator leaves moved-from object with @p MPI_GROUP_NULL.
58+
group& operator=(group &&rhs) noexcept {
59+
if (this != std::addressof(rhs)) {
60+
this->free();
61+
this->_grp = std::exchange(rhs._grp, MPI_GROUP_NULL);
62+
}
63+
return *this;
64+
}
65+
66+
/**
67+
* @brief Take ownership of an existing @p MPI_Group object.
68+
* @param grp The group to be handled.
69+
*/
70+
explicit group(MPI_Group grp) : _grp(grp) {}
71+
72+
/**
73+
* @brief Create a group from a communicator.
74+
* @param c The communicator from which to create a group.
75+
*/
76+
explicit group(communicator c) {
77+
if (has_env) {
78+
MPI_Comm_group(c.get(), &_grp);
79+
}
80+
}
81+
82+
/// Get the wrapped @p MPI_Group object.
83+
[[nodiscard]] MPI_Group get() const noexcept { return _grp; }
84+
85+
/// Check if the contained @p MPI_Group is @p MPI_GROUP_NULL.
86+
[[nodiscard]] bool is_null() const noexcept { return _grp == MPI_GROUP_NULL; }
87+
88+
/// Rank of the calling process in the given group.
89+
[[nodiscard]] int rank() const {
90+
int rank = 0;
91+
if (has_env) {
92+
MPI_Group_rank(_grp, &rank);
93+
}
94+
return rank;
95+
}
96+
97+
/// Size of a group.
98+
[[nodiscard]] int size() const {
99+
int size = 1;
100+
if (has_env) {
101+
MPI_Group_size(_grp, &size);
102+
}
103+
return size;
104+
}
105+
106+
/// Produces a group by reordering an existing group and taking only listed members.
107+
group incl(std::vector<int> const &ranks) const {
108+
MPI_Group newgroup = MPI_GROUP_NULL;
109+
if (has_env) {
110+
MPI_Group_incl(_grp, ranks.size(), ranks.data(), &newgroup);
111+
}
112+
return group(newgroup);
113+
}
114+
115+
/// Free the group.
116+
void free() {
117+
if (has_env) {
118+
if (_grp != MPI_GROUP_NULL) {
119+
MPI_Group_free(&_grp);
120+
}
121+
}
122+
}
123+
};
124+
125+
} // namespace mpi

c++/mpi/window.hpp

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#pragma once
2323

2424
#include "./communicator.hpp"
25+
#include "./group.hpp"
2526
#include "./macros.hpp"
2627

2728
#include <mpi.h>
@@ -86,7 +87,6 @@ namespace mpi {
8687
* @param size The number of elements of type @p BaseType in the buffer. (default @p 0)
8788
* @param info Additional MPI information. (default @p MPI_INFO_NULL)
8889
*/
89-
9090
explicit window(communicator &c, BaseType *base, MPI_Aint size = 0, MPI_Info info = MPI_INFO_NULL) noexcept(false) {
9191
ASSERT(size >= 0)
9292
ASSERT(!(base == nullptr && size > 0))
@@ -241,6 +241,48 @@ namespace mpi {
241241
}
242242
}
243243

244+
/**
245+
* @brief Starts an RMA access epoch.
246+
*
247+
* @param grp The group of target processes.
248+
* @param assert An assertion flag providing optimization hints to MPI.
249+
*/
250+
void start(group const &grp, int assert = 0) const noexcept {
251+
if (has_env) {
252+
MPI_Win_start(grp.get(), assert, win);
253+
}
254+
}
255+
256+
/**
257+
* @brief Completes an RMA access epoch on win started by a call to @p start.
258+
*/
259+
void complete() const noexcept {
260+
if (has_env) {
261+
MPI_Win_complete(win);
262+
}
263+
}
264+
265+
/**
266+
* @brief Starts an RMA exposure epoch for the local window.
267+
*
268+
* @param grp The group of origin processes.
269+
* @param assert An assertion flag providing optimization hints to MPI.
270+
*/
271+
void post(group const &grp, int assert = 0) const noexcept {
272+
if (has_env) {
273+
MPI_Win_post(grp.get(), assert, win);
274+
}
275+
}
276+
277+
/**
278+
* @brief Completes an RMA exposure epoch started by a call to @p post.
279+
*/
280+
void wait() const noexcept {
281+
if (has_env) {
282+
MPI_Win_wait(win);
283+
}
284+
}
285+
244286
/**
245287
* @brief Reads data from a remote memory window.
246288
*

doc/DoxygenLayout.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
<tab type="usergroup" url="@ref mpi_essentials" title="MPI essentials">
2828
<tab type="user" url="@ref mpi::communicator" title="communicator"/>
2929
<tab type="user" url="@ref mpi::environment" title="environment"/>
30+
<tab type="user" url="@ref mpi::group" title="group"/>
3031
</tab>
3132
<tab type="usergroup" url="@ref mpi_types_ops" title="MPI datatypes and operations">
3233
<tab type="usergroup" url="@ref mpi::mpi_type" title="mpi_type">

doc/documentation.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ If you are looking for a specific function, class, etc., try using the search ba
2424
Besides storing the `MPI_Comm` object, it also provides some convient functions for getting the size of the
2525
communicator, the rank of the current process or for splitting an existing communicator.
2626

27+
* The mpi::group class is a simple wrapper around an `MPI_Group` object.
28+
Besides storing the `MPI_Group` object, it also provides some convient functions for getting the size of the
29+
group, the rank of the current process or for splitting the group based on include rules.
30+
2731
It further contains the convenient function mpi::is_initialized and the static boolean mpi::has_env.
2832

2933
## MPI datatypes and operations

test/c++/mpi_window.cpp

Lines changed: 33 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -58,61 +58,59 @@ TEST(MPI_Window, WindowAllocate) {
5858
EXPECT_EQ(rcv, rank);
5959
}
6060

61-
TEST(MPI_Window, LockUnlock) {
61+
TEST(MPI_Window, PassiveTargetCommunication) {
6262
mpi::communicator world;
6363
int rank = world.rank();
64-
int target_rank = 1;
65-
int origin_count = 1;
66-
67-
if (target_rank >= world.size()) return;
6864

69-
mpi::window<int> win{world, 1};
65+
auto win_comm = world.split(rank == 0 || rank == 1 ? 0 : MPI_UNDEFINED);
7066

71-
*(win.base()) = 0;
67+
if (rank == 0 || rank == 1) {
68+
mpi::window<int> win{win_comm, 1};
69+
*(win.base()) = -1;
7270

73-
win.fence();
71+
win.fence();
72+
if (rank == 0) {
73+
int val = 42;
74+
win.put(&val, 1, 1);
75+
}
76+
win.fence();
7477

75-
if (rank == 0) {
76-
int origin_addr = 42;
77-
win.lock(MPI_LOCK_EXCLUSIVE, target_rank);
78-
win.put(&origin_addr, origin_count, target_rank);
79-
win.unlock(target_rank);
80-
}
81-
82-
win.fence();
83-
84-
if (rank == target_rank) {
85-
EXPECT_EQ(*(win.base()), 42);
78+
if (rank == 1) {
79+
EXPECT_EQ(*(win.base()), 42);
80+
}
8681
}
8782
}
8883

89-
TEST(MPI_Window, RoyalFlush) {
84+
TEST(MPI_Window, ActiveTargetCommunication) {
9085
mpi::communicator world;
9186
int rank = world.rank();
92-
int size = world.size();
9387

94-
int* buf;
9588
mpi::window<int> win{world, 1};
89+
*(win.base()) = -1;
9690

97-
buf = win.base();
98-
99-
int next = (rank + 1) % size;
100-
int val = rank;
91+
int origin_rank = 0;
92+
int target_rank = 1;
10193

102-
win.lock(next);
103-
win.put(&val, 1, next);
104-
win.flush(next);
105-
win.unlock(next);
94+
// Only the origin and target ranks will participate in the communication.
95+
mpi::group world_group(world);
96+
auto origin_group = world_group.incl({origin_rank});
97+
auto target_group = world_group.incl({target_rank});
10698

107-
win.fence();
99+
if (rank == target_rank) {
100+
win.post(origin_group);
101+
win.wait(); // blocks until origin_rank calls complete()
102+
EXPECT_EQ(*(win.base()), 42);
103+
}
108104

109-
if (rank == (size - 1) && mpi::has_env) {
110-
EXPECT_EQ(*buf, size - 2);
105+
if (rank == origin_rank) {
106+
win.start(target_group); // blocks until target_rank calls post()
107+
int origin_addr[] = { 42 };
108+
int origin_count = 1;
109+
win.put(origin_addr, origin_count, target_rank);
110+
win.complete();
111111
}
112112
}
113113

114-
115-
116114
TEST(MPI_Window, GetAttrSize) {
117115
mpi::communicator world;
118116
int buffer;

0 commit comments

Comments
 (0)