Skip to content

Commit 6bec730

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

File tree

3 files changed

+205
-36
lines changed

3 files changed

+205
-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
*

test/c++/mpi_window.cpp

Lines changed: 37 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -58,61 +58,63 @@ 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;
6664

67-
if (target_rank >= world.size()) return;
65+
auto win_comm = world.split(rank == 0 || rank == 1 ? 0 : MPI_UNDEFINED);
6866

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

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-
}
71+
win.fence();
72+
if (rank == 0) {
73+
int val = 42;
74+
win.put(&val, 1, 1);
75+
}
76+
win.fence();
8177

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();
87+
int target_rank = 1;
88+
int origin_count = 1;
9389

94-
int* buf;
95-
mpi::window<int> win{world, 1};
90+
if (target_rank >= world.size()) return;
9691

97-
buf = win.base();
92+
mpi::window<int> win{world, 1};
93+
*(win.base()) = -1;
9894

99-
int next = (rank + 1) % size;
100-
int val = rank;
95+
int origin_rank = 0;
96+
int target_rank = 1;
10197

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

107-
win.fence();
103+
if (rank == target_rank) {
104+
win.post(origin_group);
105+
win.wait(); // blocks until origin_rank calls complete()
106+
EXPECT_EQ(*(win.base()), 42);
107+
}
108108

109-
if (rank == (size - 1) && mpi::has_env) {
110-
EXPECT_EQ(*buf, size - 2);
109+
if (rank == origin_rank) {
110+
win.start(target_group); // blocks until target_rank calls post()
111+
int origin_addr[] = { 42 };
112+
int origin_count = 1;
113+
win.put(origin_addr, origin_count, target_rank);
114+
win.complete();
111115
}
112116
}
113117

114-
115-
116118
TEST(MPI_Window, GetAttrSize) {
117119
mpi::communicator world;
118120
int buffer;

0 commit comments

Comments
 (0)